Robot Framework: passing argument to a keyword that returns an XPath - selenium

In my RF-Selenium project I have a language selection with a couple different options to choose from, which I locate through xpath. I usually separate my high-level keywords, locators/global variables and tests in 3 different files, so I need to get the xpath in one file and keywords in other.
The xpath that I tested and works when hardcoded looks like this: //select[#id="language"]/option[#value="?hl=es"] (and then change the 'es' to any other language identifier to locate other options). So, following the suggestions here I built a "GET LOCATOR" keyword to take a language identifier as a parameter and return the correct xpath:
GET LOCATOR
[Arguments] ${language}
${option locator} Replace String ${LANG} placeholder ${language}
[Return] ${option locator}
I have two different keywords that would use the return value from the GET LOCATOR keyword: in one of them I verify the currently selected language is disabled in the selection list:
${current} Get Element Attribute html#lang
Element Should Be Disabled GET LOCATOR ${current}
and then I actually select a different language and check the page has switched to it:
Select From List By Value ${LANGUAGE SWITCH} es
Wait Until Page Contains Element GET LOCATOR 'es'
All of these is in the Resources file, while the ${LANGUAGE SWITCH} and ${LANG} variables are in a different file (and the Replace String keyword is in the String RF standard library).
The ${LANGUAGE SWITCH} variable holds a css selector that successfully locates the language dropdown. And I already did some tests without the GET LOCATOR keyword and they passed, like:
${current} Get Element Attribute html#lang
Element Should Be Selected xpath=//select[#id="language"]/option[#value="?hl=${current}"]
So I suspect there's a problem with my placeholder xpath, stored in the ${LANG} variable: xpath = //select[#id="language-switch"]/option[#value="?hl=placeholder"]
And this is the DOM part with the language selection dropdown:
<select id="language">
<option value="?hl=ar">Arabic</option>
<option value="?hl=zh-TW">Chinese (Traditional)</option>
<option value="?hl=nl">Dutch - Nederlands‎</option>
<option value="?hl=en" selected="" disabled="">English</option>
<option value="?hl=el">Greek</option>
<option value="?hl=es">Spanish‎</option>
</select>
To make things worse, the test using this keyword fails with no error message, as I only get:
| FAIL |
en
So... what am I doing wrong here?

You cannot call a second keyword from Element should be disabled and Wait Until Page Contains Element. What is happening is that Element should be disabled thinks the locator is the string GET LOCATOR (which obviously doesn't exist), and the custom error message is es.
You will need to break that down into two steps:
${locator}= GET LOCATOR ${current}
Element should be disabled ${locator}

Related

How does dot(.) in xpath to take multiple form in identifying an element and matching a text

I have the below dom structure:
<h3 class="popover-title">
<div class="popup-title">
<div class="title-txt">Associated Elements &nbsp(5)</div>
</div>
</h3>
I am trying to write an xpath which will identify the title "Associated Elements" under h3 tag.
When my xpath is
//div[contains(#class, popover)]//h3[contains(.,'Associated Elements')]
the element is identified.
However when my xpath is
//div[contains(#class, popover)]//h3[contains(text(),'Associated Elements')]
the element is not identified.
As per my understanding the dot(.) is a replacement for text(), but then why does it not identify the element when I use the text() function.
However, for another dom structure:
<h3 class="popover-title">
<a class="btn-popover" href="#">x</a>
"Associated Elements"
</h3>
The xpath :
//div[contains(#class, popover)]//h3[contains(text(),'Associated Elements')]
&
//div[contains(#class, popover)]//h3[contains(.,'Associated Elements')]
works fine.
Can someone please explain the behaviour of dot(.) under both these scenarios?
Is there a better way to write an xpath that holds good for both the exmaples? Please suggest.
As selenium is tagged so this answer would be based on xpath-1.0 and the associated XML Path Language (XPath) Version 1.0 specifications.
contains(string, string)
The function boolean contains(string, string) returns true if the first argument string contains the second argument string, and otherwise returns false. As an example:
//h3[contains(.,'Associated Elements')]
Text Nodes
Character data is grouped into text nodes. As much character data as possible is grouped into each text node. The string-value of a text node is the character data. A text node always has at least one character of data. In the below example, text() selects all text node children of the context node:
//h3[text()='Associated Elements']
In your usecase, within the HTML the text Associated Elements &nbsp(5) have which is alternatively referred to as a fixed space or hard space, NBSP (non-breaking space) used in programming to create a space in a line that cannot be broken by word wrap. Within HTML, allows you to create multiple spaces that are visible on a web page and not only in the source code.
Analyzing your code trials
Your first code trial with:
//h3[contains(.,'Associated Elements')]
locates the element as it successfully identifies with partial text Associated Elements
Your second code trial with:
//h3[contains(text(),'Associated Elements')]
fails as the element contains some more characters e.g. in addition to the text Associated Elements.
Reference
You can find a couple of relevant discussions in:
How to locate the button element using Selenium through Python
What does contains(., 'some text') refers to within xpath used in Selenium
While fetching all links,Ignore logout link from the loop and continue navigation in selenium java
The text() in contains(text(),'Associated Elements') is a selector that matches all of the text nodes that are children of the context node - it returns a node-set. That node-set is converted to string and passed to the contains() function.
text() isn't a function but a node test. It is used to select all text-node children of the context node. So, if the context node is an element named x, then text() selects all text-node children of x.
When you use contains(., 'Associated Elements') only an individual text node is passed to the function and it is able to uniquely match the text.
Note: copied and edited from this and this post.

How to get the css style of text-overflow in robot framework

How to get the css style of text-overflow in robot framework. For validating the ellipsis text.
<td _ngcontent-c5="" class="fontStyle" data-placement="top" title="123456789123456789qwertyuiasdfghjklzxcvbnmasdfghjkqwertyuiasdfghjkzxcvbnmertyui"> 123456789123456789qwertyuiasdfghjklzxcvbnmasdfghjkqwertyuiasdfghjkzxcvbnmertyui </td>
Update: in the current SeleniumLibrary (3.2+) there is a dedicated keyword: Get Element Attribute
Getting a value of a CSS property is not supported in the SeleniumLibrary for Robot Framework. However, there is a method called value_of_css_property in the Selenium Python module that does exactly that.
In order to call a method on an element the standard keyword Call Method can be used on any Robot variable or object. In the below example I created a custom keyword and some examples using the Google main page. They should be easily modified for your purpose.
*** Settings ***
Library SeleniumLibrary
Suite Teardown Close All Browsers
*** Test Cases ***
TC
Open Browser http://www.google.com Chrome
# a CSS property from the element.
${element_prop}= Get CSS Property Value id=SIvCob line-height
Should Be Equal As Strings ${element_prop} 28px
# a CSS property inherited from the <body> tag.
${body_prop}= Get CSS Property Value id=SIvCob font-family
Should Be Equal As Strings ${body_prop} arial, sans-serif
*** Keywords ***
Get CSS Property Value
[Documentation]
... Get the CSS property value of an Element.
...
... This keyword retrieves the CSS property value of an element. The element
... is retrieved using the locator.
...
... Arguments:
... - locator (string) any Selenium Library supported locator xpath/css/id etc.
... - property_name (string) the name of the css property for which the value is returned.
...
... Returns (string) returns the string value of the given css attribute or fails.
...
[Arguments] ${locator} ${attribute name}
${css}= Get WebElement ${locator}
${prop_val}= Call Method ${css} value_of_css_property ${attribute name}
[Return] ${prop_val}
in robot framework to get text you can use,
this will store text in variable data
${data} Get Text xpath=*//td[#class='fontStyle' and #data-placement='top']
this will give you data variable as 123456789123456789qwertyuiasdfghjklzxcvbnmasdfghjkqwertyuiasdfghjkzxcvbnmertyui
for validation you can use
for partial match use :
Element Should Contain locator text_should_check_with
for exact match use :
Element Text Should Be locator text_should_check_with

Selenium WebDriver - Find Element

I've gone through the Selenium Documentation for locating elements, but I can't seem to figure out how to find the element in my code.
Here is my code from my .cshtml:
<a onclick="alter('#key', '#value')" href="#edit" id="#key-display">#value</a>
I am trying to locate and click the #value at the end.
Here is what it looks like when I inspect the value on Chrome:
<a onclick="alter('February 9, 2018', '1.00000')" href="#edit" id="February 9, 2018-display">1.00000 gallons</a>
I am able to locate the element by link text like this:
chromeDriver.FindElementByLinkText("1.00000 gallons").Click();
However, the link text will change constantly and I want to be able to locate it after it changes.
I have tried locating by several ways:
chromeDriver.FindElementByLinkText("#value").Click();
chromeDriver.FindElementByXPath("//a[#id='#key-display']").Click();
chromeDriver.FindElementById("#key-display").Click()
You will have to locate the element by the HTML in the page after it's rendered so the cshtml variable name can't be used. Having said that, you should be able to find a locator that will work. I would start with a CSS selector like
a[href='#edit']
That should work unless you have multiple edit links on the page. If that doesn't work, I would try
a[href='#edit'][id$='-display']
To find the element and invoke click() on the element you can use either of the following Locator Strategies :
xpath (where ID contains -display and href is #edit)
"//a[contains(#id,'-display') and #href='#edit']"
You can be more granular adding the onclick attribute as :
"//a[contains(#id,'-display') and #href='#edit' and starts-with(#onclick,'alter')]"
cssSelector (where ID ends with -display and href is #edit)
"//a[id$='-display'][href='#edit']"
You can be more granular adding the onclick attribute as :
"//a[id$='-display'][href='#edit'][onclick^='alter']"

Element Should Contain Text - for disabled field

I am working on automation tests using Appium and Robotframework. The keyword Element Should Contain Text seems to return empty if the input field is disabled. How to verify a disabled input field has a given value?
<input type="text" id="myId" name="myName" disabled />
I get the following error:
Element 'myId' should have contained text 'myValue' but its text was ''.
Make sure your code is correct and you are passing the correct id/classname. If this does not work please post the HTML code you are using. Adding some of the sample example which you can try out :-
If your tag is something like this below -
<input disabled="true" id='data'>
Your code should be -
WebElement webElement = driver.findElements(By.id('my-id'));
webElement.getAttribute("disabled")
or
WebElement.getAttribute("id")
For this tag -
<input id="j_idt93:j_idt93" type="text" disabled="disabled" maxlength="2000" value="Pārtraukts">
To get the value attribute -
String value = driver.findElement(By.id("j_idt93:j_idt93")).getAttribute("value");
If this does not work you may have to use the javascript executor -
String value = (String)((JavascriptExecutor) driver).executeScript("Java script query in here to return value","");
Your query should be -
return document.getElementById("j_idt93:j_idt93").getAttribute("value");
Let me know if this does not work.
Its true selenium returns empty if I assert text in disabled fields by using Element or page contains text... However we can compare the text in field by using Get Value and then comparing the field value with the value you want to assert by should be equal.
In your case what you can do is,
${valueInField} Get Value myId
should be equal ${valueInField} ${myValue}
I solved this problem.
*** Keywords ***
Should Not Be Empty
[Arguments] ${locator}
${getValueOfTextField}= Get Element Attribute ${locator} value
Should Not Be Empty ${getValueOfTextField}
I didn't get the value out, but I'm sure it's not empty.

locating html elements with selenese in a test

while writing some acceptance tests for my webapp (playframework based),I got confused by the usage of some selenium commands.
In my html page,I have a submit button like this
<input type="submit" id="removecartitem" value="remove"/>
to locate this,I used
assertElementPresent(id='removecartitem')
however,this fails,
assertElementPresent id='removecartitem' false
The selenium documentation says
id=id: Select the element with the specified #id attribute.
but,if i simply put
assertElementPresent('removecartitem')
Then,the test is executed correctly.This is the source for confusion, since the default way is to select the element whose name attribute is 'removecartitem' ,and I haven't mentioned any name attribute in my html
Any idea why this happens?
It looks like you need to remove the single quotes according to the documentation you provided...e.g:
assertElementPresent(id=removecartitem)