Perl Selenium::Remote::Driver test to check button element is formed? - perl

I need to test web site where button is formed at the bottom of the page after
user scroll the page for two times.
I have written a small script to test if the required element is formed.
the condition tested always return false even though required element is formed in the page.
use Selenium::Remote::Driver;
use Scalar::Util qw/blessed reftype/;
my $driver= Selenium::Remote::Driver->new;
$driver->get('http://www.foo.com');
while ( 1 ) {
$query = $driver->find_element_by_xpath(q{//button[#class='button']});
#to test the if the element is present
if ( blessed($query) && $query->isa('Selenium::Remote::Driver') ) {
$query->click;
last;
}
else {
#always goes into else loop
#to go to the end of the webpage
my $script = q{window.scrollTo(0,document.body.scrollHeight);};
my $elem = $driver->execute_script($script);
}
}
Is there any way to test if the button element has been formed in the script?

Use the below to find the element
my $elem = $driver->find_element_by_xpath($locator);
If this doesn't return 0 you've found your element, then you could run below:
To check whether the element is displayed
$elem->is_displayed();
To check whether the element is hidden
$elem->is_hidden();
Check out more methods at: https://metacpan.org/pod/Selenium::Remote::WebElement

All these function return 0 when element is not found.
Read the doc carefully.
find_element_by_xpath
These functions all take a single STRING argument: the locator
search target of the element you want. If the element is found, we
will receive a WebElement. Otherwise, we will return 0. Note that
invoking methods on 0 will of course kill your script.

Related

evaluate condition assigned to a variable

i am using module Excel::Writer::XLSX and i need to evaluate this condition to check if the sheet was created or not (i need to only execute certain actions if the sheet already exists)
my $sheet = $workbook->add_worksheet($filters{$filter}{description});
how can i do that please ?
knowing that if the sheet already exists, the add_worksheet method "dies" with an error like :
Worksheet name 'All Users', with case ignored, is already used. at ./xxxx.pl line 203.
naturlly, doing something like this doesn't work as i believe it evaluates the variable assignment :
if (my $sheet = $workbook->add_worksheet($filters{$filter}{description});) {
# good, sheet doesn't already exists and was created
# i can place my code here to insert header rows etc./ colors
} else {
# program fires a message like :
# Worksheet name 'XXXXXX', with case ignored, is already used. at XXX
# no need to place code for column headers etc., sheet already exists
}
You could use
my $name = $filters{$filter}{description};
my $sheet =
$workbook->get_worksheet_by_name( $name ) ||
$workbook->add_worksheet( $name );
nvm, i used a hash and i'm checking the hash
if (!$sheets{$filter}) {
$sheets{$filter} = $workbook->add_worksheet($filters{$filter}{description});
works nicely

How would I match the correct hash pair based on a specific string?

I have a simple page hit tracking script that allows for the output to display friendly names instead of urls by using a hash.
UPDATE: I used php to generate the hash below, but used the wrong dynamic page name of item.html. When changed to the correct name, the script returns the desired results. Sorry for wasting anyone's time.
my %LocalAddressTitlePairs = (
'https://www.mywebsite.com/index.html' => 'HOME',
'https://www.mywebsite.com/art_gallery.html' => 'GALLERY',
'https://www.mywebsite.com/cart/item.html?itemID=83&cat=26' => 'Island Life',
'https://www.mywebsite.com/cart/item.html?itemID=11&cat=22' => 'Castaways',
'https://www.mywebsite.com/cart/item.html?itemID=13&cat=29' => 'Pelicans',
and so on..
);
The code for returning the page hits:
sub url_format {
local $_ = $_[0] || '';
if ((m!$PREF{'My_Web_Address'}!i) and (m!^https://(.*)!i) ) {
if ($UseLocalAddressTitlePairs == 1) {
foreach my $Address (keys %LocalAddressTitlePairs) {
return "<a title=\"$Address\" href=\"$_\">$LocalAddressTitlePairs{$Address}</A>" if (m!$_$! eq m!$Address$!);
}
}
my $stub =$1;
return $stub;
}
}
Displaying the log hits will show
HOME with the correct link, GALLERY with the correct url link, but https://www.mywebsite.com/cart/item.html?itemID=83&cat=26
will display a random name instead of what it should be, Island Life for this page.. it has the correct link,-- a different name displays every time the page is loaded.
And, the output for all pages with query strings will display the exact same name. I know the links are correct by clicking thru site pages and checking the log script for my own page visits.
I tried -
while (my($mykey, $Value) = each %LocalAddressTitlePairs) {
return "<a title=\"$mykey\" href=\"$_\">$Value</a>" if(m!$_$! eq m!$mykey$!);
but again, the link is correct but the mykey/Value associated is random too. Way too new to perl to figure this out but I'm doing a lot of online research.
m!$Address$! does not work as expected, because the expression contains special characters such as ?
You need to add escape sequences \Q and \E
m!\Q$Address\E$!
it’s even better to add a check at the beginning of the line, otherwise
my $url = "https://www.mywebsite.com/?foo=bar"
my $bad_url = "https://bad.com?u=https://www.mywebsite.com/?foo=bar"
$bad_url =~ m!\Q$url\E$! ? 1 : 0 # 1, pass
$bad_url =~ m!^\Q$url\E$! ? 1 : 0 # 0, fail

Using completion function in Term::ReadLine::Gnu

I want to make a console and change the automatic completion function when I press tab but I want to differentiate between two cases:
If I press tab and the beginning of the command matches a list I supplied in an array, the auto complete will be according to this array.
If I press tab and the command isn't recognized from the list I supplied, I want the generic completion function to work, so t hat it will auto complete directories and file names in the current directory.
Is it possible?
Thanks a lot.
Edit: I'm trying to do it inside a perl script. I saw this example:
rl_attempted_completion_function
A reference to an alternative function to create matches.
The function is called with TEXT, LINE_BUFFER, START, and END. LINE_BUFFER is a current input buffer string. START and END are indices in LINE_BUFFER saying what the boundaries of TEXT are.
If this function exists and returns null list or undef, or if this variable is set to undef, then an internal function rl_complete() will call the value of $rl_completion_entry_function to generate matches, otherwise the array of strings returned will be used.
The default value of this variable is undef. You can use it as follows;
use Term::ReadLine;
...
my $term = new Term::ReadLine 'sample';
my $attribs = $term->Attribs;
...
sub sample_completion {
my ($text, $line, $start, $end) = #_;
# If first word then username completion, else filename completion
if (substr($line, 0, $start) =~ /^\s*$/) {
return $term->completion_matches($text,
$attribs->{'username_completion_function'});
} else {
return ();
}
}
...
$attribs->{attempted_completion_function} = \&sample_completion;
completion_matches(TEXT, ENTRY_FUNC)
What I want to do is that in case when tab is pressed it recognizes a substring from an array I provide, the auto completion will be from that array (if there are multiple matches it will give all of them like a regular unix console).
Otherwise, I want the auto completion to be file recognition.
The subroutine used internally by Term::ReadLine::Gnu to provide the default completion is filename_completion_function, which you can call directly from your custom subroutine:
use Term::ReadLine;
my $term = new Term::ReadLine 'MyTerm';
$term->Attribs->{'completion_entry_function'} = \&my_completion;
my $ans = $term->readline('How can I help you? ');
sub my_completion {
my ($text, $state) = #_;
if (my_test) {
return my_custom_stuff;
}
else {
return Term::ReadLine::Gnu->filename_completion_function($text, $state);
}
}

How do I add this value to an Array and it stays in the script

I want to add a command that adds numbers to the array.
This is what i have exactly:
my $ownerids = ('374867065');
Then later in the script i have this:
if($ownerids == $spl2[0]){
if (index($message, "!adduser") != -1) {
$msg = $spl[1];
$send = "<m t=\"User Added $msg\" u=\"$botid\" />\0";
$socket->send($send);
push (my $ownerids, "$msg");
}
}
I am on a chatbox and this is a chatbot, i want to make it when i say !adduser (thereid) it adds them to a list and they can use the bot commands, and also i want a Delete User, If you can help this will be MUCH appretiated.
If you want ownerids to be an array, then you must prefix it with a #
my #ownerids = ('374867065');
Then to add an element, you can push
push #ownerids, "$msg";
However, you're going to need to fix your other references to #ownerids so it's treated like an array. For example, your first if looks like it's intending to see if $spl2[0] is an owner. If that's the case, then you'll need to grep the array:
if(grep {$_ == $spl2[0]} #ownerids) {

WWW::Mechanize::Firefox looping though links

I am using a foreach to loop through links. Do I need a $mech->back(); to continue the loop or is that implicit.
Furthermore do I need a separate $mech2 object for nested for each loops?
The code I currently have gets stuck (it does not complete) and ends on the first page where td#tabcolor3 is not found.
foreach my $sector ($mech->selector('a.link2'))
{
$mech->follow_link($sector);
foreach my $place ($mech->selector('td#tabcolor3'))
{
if (($mech->selector('td#tabcolor3', all=>1)) >= 1)
{
$mech->follow_link($place);
print $_->{innerHTML}, '\n'
for $mech->selector('td.dataCell');
$mech->back();
}
else
{
$mech->back();
}
}
You cannot access information from a page when it is no longer on display. However, the way foreach works is to build the list first before it is iterated through, so the code you have written should be fine.
There is no need for the call to back as the links are absolute. If you had used click then there must be a link in the page to click on, but with follow_link all you are doing is going to a new URL.
There is also no need to check the number of links to follow, as a for loop over an empty list will simply not be executed.
To make things clearer I suggest that you assign the results of selector to an array before the loop.
Like this
my #sectors = $mech->selector('a.link2');
for my $sector (#sectors) {
$mech->follow_link($sector);
my #places = $mech->selector('td#tabcolor3');
for my $place (#places) {
$mech->follow_link($place);
print $_->{innerHTML}, '\n' for $mech->selector('td.dataCell');
}
}
Update
My apologies. It seems that follow_link is finicky and needs to follow a link on the current page.
I suggest that you extract the href attribute from each link and use get instead of follow_link.
my #selectors = map $_->{href}, $mech->selector('a.link2');
for my $selector (#selectors) {
$mech->get($selector);
my #places = map $_->{href}, $mech->selector('td#tabcolor3');
for my $place (#places) {
$mech->get($place);
print $_->{innerHTML}, '\n' for $mech->selector('td.dataCell');
}
}
Please let me know whether this works on the site you are connecting to.
I recommend to use separate $mech object for this:
foreach my $sector ($mech->selector('a.link2'))
{
my $mech = $mech->clone();
$mech->follow_link($sector);
foreach my $place ($mech->selector('td#tabcolor3'))
{
if (($mech->selector('td#tabcolor3', all=>1)) >= 1)
{
my $mech = $mech->clone();
$mech->follow_link($place);
print $_->{innerHTML}, '\n'
for $mech->selector('td.dataCell');
#$mech->back();
}
# else
# {
# $mech->back();
# }
}
I am using WWW:Mechanize::Firefox to loop over a bunch of URLs with loads of Javascript. The page does not render immediately so need test if a particular page element is visible (similar to suggestion in Mechanize::Firefox documentation except 2 xpaths in the test) before deciding next action.
The page eventually renders a xpath to 'no info' or some wanted stuff after about 2-3 seconds. If no info we go to next URL. I think there is some sort of race condition with both xpaths not existing at once causing the MozRepl::RemoteObject: TypeError: can't access dead object error intermittently (at the sleep 1 in the loop oddly enough).
My solution that seems to work/improve reliability is to enclose all the $mech->getand$mech->is_visible in an eval{}; like this:
eval{
$mech->get("$url");
$retries = 15; #test to see if element visible = page complete
while ($retries-- and ! $mech->is_visible( xpath => $xpath_btn ) and ! $mech->is_visible( xpath => $xpath_no_info )){
sleep 1;
};
last if($mech->is_visible( xpath => $xpath_no_info) ); #skip rest if no info page
};
Others might suggest improvements on this.