I know I can grab the value of a JavaScript variable using Selenium Java APIs as follows
JavascriptExecutor je = (JavascriptExecutor) driver;
Long JSvar = (Long)je.executeScript("return variable");
But is there a way in Selenium to get every value for that variable as it changes in JS code. For example if my JS code looks as follows
var variable=1;
/* some code */
variable=2;
/* some code */
variable=3;
Is there a way to grab these three values (1,2,3)? Something like putting a breakpoint on the JS code but in an automated way.
I was thinking that I could store these values in an array and then grab the array but is there another way in Selenium to handle this with the minimal changes to the JS code?
You have not specified how or when Selenium should retrieve these values, but here is some JavaScript code that at least makes these values available:
(function(window) {
var values = [];
var someVariable;
Object.defineProperty(window, 'someVariable', {
get: function() {
return someVariable;
},
set: function(value) {
someVariable = value;
values.push(value);
}
});
Object.defineProperty(window, 'someVariableValues', {
get: function() {
return values;
}
});
})(this);
Whether JavaScript code executes window.someVariable = X or simply someVariable = X, the setter will push that value onto the array. Selenium (or JavaScript for that matter) can access window.someVariableValues to return the historical and current values of that variable.
Retrieving this using Selenium could be:
JavascriptExecutor je = (JavascriptExecutor) driver;
// Get current value of variable
Long JSvar = (Long)je.executeScript("return someVariable");
// Get all values assigned to variable (this part I'm not 100% sure about)
Long[] historicalValues = (Long[])je.executeScript("return someVariableValues");
I am trying to automate - verification of top nav links of https://www.zillow.com.
For that I am matching actual urls with expected urls. But in page class method this line retruns null. I don't get it why?
String urlp = locator.all_topnav_links.get(i).getAttribute("href");
Method in page class:
public void verify_topnav_links() {
System.out.println("Started");
for (int i = 0; i < locator.all_topnav_links.size(); i++) {
System.out.println(locator.all_topnav_links.size());
if (locator.all_topnav_links.get(i).isDisplayed() == true) {
String link=locator.all_topnav_links.get(i).getText();
System.out.println(link);
String urlp = locator.all_topnav_links.get(i).getAttribute("href");
System.out.println("Links are : " + urlp);
// now click link one by one
locator.all_topnav_links.get(i).click();
driver.navigate().back();
// 2 verify before url with current opened url
//Assert.assertEquals(urlp, driver.getCurrentUrl());
}
Locator in locator class:
#FindAll({#FindBy(xpath = "//ul[#data-zg-section='main']/li")})
public List<WebElement> all_topnav_links;
Test case in test class:
#Test
public void verify_TopNav_links()
{
hpage.verify_topnav_links();
}
Also I am not sure whether this correct approach or not. Can anybody tell me the best approach to verify this scenario.
Note: This zillow.com will starts to display captcha if we open multiple times using selenium. To ignore it manually remove catpcha parameters from the url when test case is getting executed.
It appears you need to use
//ul[#data-zg-section='main']/li/a
here
#FindAll({#FindBy(xpath = "//ul[#data-zg-section='main']/li")})
public List<WebElement> all_topnav_links;
This xpath
//ul[#data-zg-section='main']/li
Seems to contain multiple elements. But you're trying to access the href attribute directly. The li here doesn't have a href attribute.
So I am using a dynamic object and I can receive an API that lists all of the object's properties such as ID in one page, but that ID is the ID of a frame layout that is not interactable and only contains child such as textbox or error message.
what I'm trying to achieve is to know the properties of that parent object so that I can try to interact with the child, for example, the textbox EditText inside. I am able to interact with the child by using the XPath but haven't found a way to determine the parent by the determined ID.
I am trying to make automatic testing that only uses the API as a reference point to check what is the object to test so that in the future I don't have to change the test script in case of any changes in the configuration.
I can check the API for existing object properties like this:
public static checkIfExistForm1(String code){
def response = WS.sendRequest(findTestObject("Object Repository/WebService"))
def json = new JsonSlurper().parseText(response.getResponseBodyContent())
for(int i = 0;i < json.page.form.fieldset.size(); i++){
if(code.contentEquals(json.page.form.fieldset[i].control[0].id)){
return true;
break
}
}
return false;
}
and then I can make some kind of method to get the child of that object and try to interact with it
for example with this method I can select an object by XPath
public static getObjectID(String code){
//considering i know that this is the child, or creating another validation
//the child(EditText) also has ID "content-desc" that i can use
String dynamicIdPath = '//*[#content-desc="%s"]'
String xpath = String.format(dynamicIdPath,code)
TestObject to = new TestObject()
to.addProperty("xpath", ConditionType.EQUALS, xpath)
return to
}
any thoughts? thanks!
I'm creating a list of IWebElements to access each of elements from same type, but the test works slowly when I want to access a certain element. I came with the idea to create a dictionary of elements and access each element by it's name (text that is stored in this element). I found some topics here but was unable to make them work for me. This is the way I'm accessing elements.
public IWebElement OneElement
{
get
{
return this.Driver.FindElement(By.Id("oneElement"));
}
}
public List<IWebElement> ListOfNames
{
get
{
return this.Driver.FindElements(By.Id("name")).ToList();
}
}
You can try the following code to get element by it's text and it is fast compared the list of elements as list needs to be searched till element found. it will take time if searched element is at the last position in the list. Below code will be direct fetch of particular element. Try it and let us know.
public IWebElement GetElementByText(String text)
{
get
{
return this.Driver.FindElement(By.xpath(String.Format("//*[#id='name'][text()='{0}']",text)));
}
}
Is their any way to find the object locator type, by passing the object locator alone.
for e.g. i need to click on a login button, where its id=login, classname=loginbutton or xpath=//input[#name='login']. I need to build method where i will be just passing the objectlocator (either id or name) as the input and its type(either id or name) should be decided in the method like if it contains // then type should be of xpath etc.
I need to pass the objectLocator() which returns type to the findElement()
WebElement element = driver.findElement(objectLocator());
I do not think it is available off the shelf, you would have to implement your own logic.
The only thing is, let's say you want to search by linktext. As per your usecase, you would, in your object repo specify, "this is my linktext".
Now how do you know it is an id or a name or a linktext?
For xpath you can check if it starts with /, then its an xpath. If its only id or name then you can use ByIdorName, but i think it would become tricky with css and linktext.
The one thing I can think is you can establish some sort of conventions like if it is linktext precede your lcoator definition with linktext=blah blah and then you split and consume it.
I find it very useful to store all my locators as By objects and either use the By directly or pass the By into methods as I need them. For example:
By passwordField= By.id("login");
By userNameField = By.name("username");
By submitButton = By.xpath("\\myxpath\div[2]");
public void clickLogin() {
driver.findElement(submitButton).click();
}
I also use static Bys from other classes as well:
public void clickLogin() {
driver.findElement(LoginPage.SUBMIT_BUTTON).click();
}
The modern way to do this is using PageFactory and PageObjects
The following is a quick and dirty which will adapt selenium locators strings to WebDriver locators.
public enum LocatorType {
CLASSNAME, CSS, ID, LINK, NAME, TAGNAME, XPATH ;
}
public WebElement objectLocator(LocatorType type, String ref) {
switch(type) {
case ID:
return this.webDriver.findElement(By.id(ref));
case CLASSNAME:
return this.webDriver.findElement(By.className(ref));
case XPATH:
return this.webDriver.findElement(By.xpath(ref));
case CSS:
return this.webDriver.findElement(By.cssSelector(ref));
case LINK:
return this.webDriver.findElement(By.linkText(ref));
case NAME:
return this.webDriver.findElement(By.name(ref));
case TAGNAME:
return this.webDriver.findElement(By.tagName(ref));
}
return null;
}
public WebElement objectLocator(String identifier) {
String typeString = identifier.substring(0, identifier.indexOf('='));
String ref = identifier.substring(identifier.indexOf('=')+1, identifier.length());
if (typeString.toLowerCase().contains("classname")) {
return objectLocator(LocatorType.CLASSNAME, ref);
} else if (typeString.toLowerCase().contains("css")) {
return objectLocator(LocatorType.CSS, ref);
} else if (typeString.toLowerCase().contains("id")) {
return objectLocator(LocatorType.ID, ref);
} else if (typeString.toLowerCase().contains("link")) {
return objectLocator(LocatorType.LINK, ref);
} else if (typeString.toLowerCase().contains("name")) {
return objectLocator(LocatorType.NAME, ref);
} else if (typeString.toLowerCase().contains("tagname")) {
return objectLocator(LocatorType.TAGNAME, ref);
} else if (typeString.toLowerCase().contains("xpath")) {
return objectLocator(LocatorType.XPATH, ref);
} else {
return null;
}
}
It looks like you are looking for this solution because you have an object repository maintained somewhere outside of your code in some kind of properties file or xml.
Using gui maps has lot of disadvantages like,
- maintain an external file with a list of locators
- parse locator files to read keys (you can abstract this but still an overhead)
- when writing PageObjects you need to switch back and forth from Page to gui map
- possibility of multiple duplicate locators in gui maps
- object repo grows over time and becomes impossible to maintain
- debugging is far more difficult
What you are looking for is adding one more layer of complexity which is not required in my opinion. Automating browsers is a challenge in itself and writing maintainable test automation code is utmost important.
Use PageFactory in your page objects.
- Natural place for your locators are Page Objects themselves.
- Locators easily accessible in page objects for review or correction
- No need for explicit driver.findElement, with #FindBy you get that for free
- modern Java and awesome annotations make page objects look beautiful & readable
I have used gui maps before and struggled a lot. Switching to page factory made me realize that using object repository was such a bad idea!
This should do for locating element. I have given example till 3 level deep.
public WebElement findElement(String locator){
WebElement w = null;
try{
return (driver.findElement(By.id(locator)));
}catch(Exception e1){
try{
return ( driver.findElement(By.name(locator)));
}catch(Exception e2){
try{
return (driver.findElement(By.xpath(locator)));
}catch(Exception e3){
System.out.println("Cound not find a locator");
e3.printStackTrace();
}
}
}
return(w);
}
public void type(String locator, String value){
try{
WebElement w= findElement(locator);
w.sendKeys(""+value);
Thread.sleep(sleepTime);
}catch(Exception e){
e.printStackTrace();
}
}
-Vinay