Search code examples
perlseleniumselenium-webdrivercoercion

How can I escape an element not found in selenium from perl?


I'm writing a code where I access a website whith some elements defined by some ID. Depending on some parameters this elements have a different id.

For example I have a blue web and a red web. All have the same elements, but the id in one is "title_red", and in other is "title_blue".

I can "solve" this, by having a code like this:

use Selenium::Waiter qw/wait_until/;

my $title="";
wait_until{$itle=($driver->find_element("//span[contains(\@id,'id_blue'))]")->get_text()}timeout => 1;
if ($titleeq ""){ #It'S not blue
    wait_until{$title=($driver->find_element("//span[contains(\@id,'id_red')]")>get_text()}timeout => 1;    
}

If I don't use the "wait_until" the program would just crash after not getting the element. Using it, it kind of does the trick, but it still generates a print exception which is pretty annoying when I'm looking into a lot of elements which are not in the page and makes it really hard to see the rest of the prints in the console. This is the error:

coercion for "id" failed: When passing in an object to the WebElement id attribute, it must have at least one of the ELEMENT or element-6066-11e4-a52e-4f735466cecf keys. at (eval 70) line 70.

Is there any way to check if the element exists or just hide this prints? I've tried some "no warnings" examples but I couldn't get anything to work, and I think this is not a warning, it's an error. If I try it inside a try/catch it would execute the catch.

I've tried this as suggested in the comments and I still get the error:

print "Here I check if it's fixed:\n";
try{wait_until{$variable=($driver->find_element("//img[\@class='".$other_variable."']"))}timeout=>1}catch{};

EDIT: I just wrote a code to reproduce the problem instead of just writing some lines from my code. The right ID or class may change depending on the region, i'm redirected to google.de:

use Selenium::Chrome;
use Selenium::Remote::Driver;
use Selenium::Waiter qw/wait_until/;
use Try::Tiny;
use strict;
use warnings;
use utf8;

my $driver = Selenium::Chrome->new('binary' => "C:\\chromedriver.exe"); 

my $login="http://google.com";
$driver->get($login); 

print "Let's try the wrong id:\n";
my $print_variable ="";
my $id_not_found = "something";
wait_until{$print_variable=($driver->find_element("//div[\@class='".$id_not_found."']"))->get_text()};  #this produces an error
print "Variable now is: $print_variable\n";

$print_variable="";
print "Let's try the right id:\n";
my $id_found = "_gIg";
wait_until{$print_variable=($driver->find_element("//div[\@class='".$id_found."']"))->get_text()};  #this not
print "Variable now is: $print_variable\n";

$print_variable="";
print "Let's try the wrong id with try, catch:\n";
try{wait_until{$print_variable=($driver->find_element("//div[\@class='".$id_not_found."']"))->get_text()}}catch{};  #this produces an error
print "Variable now is: $print_variable\n";

$print_variable="";
print "Let's try the wrong id with try, catch and catch statement:\n";
try{wait_until{$print_variable=($driver->find_element("//div[\@class='".$id_not_found."']"))->get_text()}}catch{warn "ERROR: $_";}; #this produces an error
print "Variable now is: $print_variable\n";

sleep(10);
$driver->shutdown_binary();

And this is the output:

perl example_stack.pl
Let's try the wrong id:
coercion for "id" failed: When passing in an object to the WebElement id attribute, it must have at least one of the ELEMENT or element-6066-11e4-a52e-4f735466cecf keys. at (eval 66) line 70.
Variable now is:
Let's try the right id:
Variable now is: Hinweise zum Datenschutz bei Google
Let's try the wrong id with try, catch:
coercion for "id" failed: When passing in an object to the WebElement id attribute, it must have at least one of the ELEMENT or element-6066-11e4-a52e-4f735466cecf keys. at (eval 66) line 70.
Variable now is:
Let's try the wrong id with try, catch and catch statement:
coercion for "id" failed: When passing in an object to the WebElement id attribute, it must have at least one of the ELEMENT or element-6066-11e4-a52e-4f735466cecf keys. at (eval 66) line 70.
Variable now is:

Solution

  • I've found a "solution", which isn't clean and short, but does the job, and it's to use find_elements instead of find_element. This returns an empty array and then you can check if it's defined.

    I'd love to see another solution as this adds a lot of "dirty" code if I have to check a lot of elements.

    Example:

    my $element_text="";
    my $myid = "asdasd";
    wait_until{$element_text=($driver->find_elements("//span[contains(\@id,'".$myid."')]"))[0]}timeout => 1;                    
    if (!defined($element_text)){$element_text=""}else{$element_text=$element_text->get_text()};