How does isDisplayed() in protractor/webDriver work? - selenium-webdriver

All of a sudden in our testing for our organization, the use of isDisplayed() just stopped being consistent.
Now it could be something that our Devs changed. But my question is what is isDisplayed() doing under the hood? What does it look for exactly? How does it work?

isDisplayed looks for the if the element is displyed in the browser screen. The display property tests whether any part of the element drawn is inside viewport. This is determined by the algorithm. In selenium it implemented as following.
/**
* Test whether this element is currently displayed.
*
* #return {!Promise<boolean>} A promise that will be
* resolved with whether this element is currently visible on the page.
*/
isDisplayed() {
return this.execute_(
new command.Command(command.Name.IS_ELEMENT_DISPLAYED));
}
IS_ELEMENT_DISPLAYED is itself an ENUM defined in the webdriver.CommandName.
The command is passed (IS_ELEMENT_DISPLAYED) uses the JsonWireProtocol to get the displayed property using the following get method:
session/:sessionId/element/:id/displayed
GET /session/:sessionId/element/:id/displayed
Determine if an element is currently displayed.
URL Parameters:
:sessionId - ID of the session to route the command to.
:id - ID of the element to route the command to.
Returns:
{boolean} Whether the element is displayed.
Potential Errors:
NoSuchWindow - If the currently selected window has been closed.
StaleElementReference - If the element referenced by :id is no longer attached to the page's DOM.
From the W3C and JsonWireProtocol which is implemented by selenium. I have quoted it down. [https://w3c.github.io/webdriver/#element-displayedness]
"The approach recommended to implementors to ascertain an element's
visibility was originally developed by the Selenium project, and is
based on crude approximations about an element's nature and
relationship in the tree. An element is in general to be considered
visible if any part of it is drawn on the canvas within the boundaries
of the viewport.
The element displayed algorithm is a boolean state where true
signifies that the element is displayed and false signifies that the
element is not displayed. To compute the state on element, invoke the
Call(bot.dom.isShown, null, element). If doing so does not produce an
error, return the return value from this function call. Otherwise
return an error with error code unknown error."
Simple words as I learned:
If the element is not present in the DOM tree, call isDisplayed() will report NoSuchElementException, this is why protractor has isPresent() API.
If the element size is zero, selenium will treat it as not dispalyed even
set dispaly: block CSS on the element
element or its parent/ancestors' CSS value of display set to none (display: none), the element is not displayed.

Related

Returning a value of a span tag using selenium.webdriver.find_element_by_xpath()

I am trying to extract the current video time from a youtube video as it is playing.
I've inspected the youtube page to find the element where the current time value is stored. This is the element below.
span class="ytp-time-current">0:03</span>
I had to remove the strict inequality signs before and after span because it returns 0:03 and the entire tag can't be seen.
I've tried searching finding the value using wd.find_element_by_class_name("ytp-time-current") but returns none
I've also copied the xpath for the element:
//*[#id="movie_player"]/div[20]/div[2]/div[1]/div/span[1]
and tried finding the value using the _xpath( )
code I've tried
from selenium import webdriver
wd = webdriver.Chrome(executable_path = r'pathtodriver')
wd.get('https://www.youtube.com/watch?v=N__fRbG84fI')
search = wd.find_element_by_class_name('ytp-time-current')
value = wd.find_element_by_xpath('//*[#id="movie_player"]/div[22]/div[2]/div[1]/div/span[1]').get_attribute('Value')
I expected to get an integer value or at least a string. but both scenarios return none.
A <span> tag doesn't have a value attribute. You probably want to do:
value = wd.find_element_by_xpath('//*[#id="movie_player"]/div[22]/div[2]/div[1]/div/span[1]').text
(I'm assuming this is the Python bindings)

React Collapse transition doesn't animate when wrapped in a div with a dynamic key

This is really weird. It took me many hours to figure out how to fix this. But even with it fixed, I don't know why it breaks in one configuration but not the other.
The best way to explain this is with the StackBlitz live example: https://stackblitz.com/edit/react-collapse-transition-breaks-with-dynamic-key
The live example details the exact issue, but here's the synopsis:
It's a React app using Material UI.
I have a <List> that is populated with an array of <ListItem>s.
Those <ListItem>s are clickable. When you click them, it uses the <Collapse> transition to expose a sub-<List> of "subheaders". The subheader <List>s are also populated with an array of <ListItem>s.
Here's where it gets weird: I have a simple <div> that holds the "header" <ListItem>s and the <Collapse>-ible <List> of "subheaders".
Because these are part of an array, React complains if I don't add a "key" attribute to top-level element (the <div>).
If I add a dynamically-generated key value to that containing <div>, it somehow kills the transition animation on the <Collapse> element. The <Collapse> still opens-and-closes, but it doesn't animate over a set number of milliseconds. It just opens (immediately) or closes (immediately).
If I add a static key value to that same containing <div>, the animation works just fine.
Why does the dynamic-key approach break the animation??
TLDR: Do not dynamically-generate globally-unique keys for array elements in React.
OK, after repeated attempts at Googling, I think I finally understand what's going on.
When you add items to an array in React, a warning is thrown if you don't add a unique "key" to each element in the array. As long as you use something unique as the "key" value for each element, the warning goes away and, for the most part, React seems to manage the array elements just fine.
When I was starting React development (a few years ago), I thought, "I can solve this easily by using a random GUID-generating function to add unique keys to all my array elements. So I would frequently use code that looks like this:
let newArray = [];
someMasterArrayOfObjects.forEach(object => {
if (someConditionIsMet) {
// SEE HOW CLEVER I THOUGHT I WAS?? USING A RANDOMLY-GENERATED GUID QUIETS THE
// UNIQUE-KEY WARNINGS THAT ARE THROWN BY REACT
newArray.push(
<div key={createRandomGuid()}>
{object.title}
</div>
);
}
});
But I missed one key word from the ReactJS documentation (emphasis, mine):
Keys help React identify which items have changed, are added, or are removed. Keys should be given to the elements inside the array to give the elements a STABLE identity:
They use the word "stable" a few times in the documentation, but it didn't really sink into my thick skull. Like most devs, my first concern was removing the warning that is thrown when array elements are missing a unique key. And using a randomly-generated GUID solved that problem.
But React uses those keys to figure out what should be re-rendered during state changes. If you randomly-generate a new, globally-unique key every time you build the elements in the array, then React will assume that all of these elements need to be completely rebuilt from scratch every time you set state.
At the very least, this is inefficient. You may not notice any performance effects in small arrays/apps, but there's no reason to artificially force a re-rendering of every array element every time that state is set for any reason. You may not notice any visual problem in your app, but it's poor practice.
At the worst, it will actually break some of your functionality. In my case, it was breaking the <Collapse> transition animation because the rendering engine couldn't compare the incremented "height" value between one state change and the next - because on every attempt to change that height, I was assigning a brand new, globally-unique identifier to the "key" value and React was seeing it as an entirely new, entirely different component.
Once I finally figured out what to Google, I also found this great JSFiddle:
http://jsfiddle.net/frosas/S4Dju/
Notice that the center row of inputs in his example are titled "Unique random keys". When you manually change the value of one of the those unique random keys, then you click on "Add item", it blows away the results of your changes, because it re-renders the input elements as brand-new elements with no ties to their previous state.

isEnable() is not working in selenium webdriver for find the button is enabled or disabled?

i am trying the bellow code,but always return true value.
if(updatebuton_status=="true"){
Thread.sleep(timmer*3);
//click on the update button
//ieDriver.findElement(By.xpath("html/body/div[1]/div/div/div/div[3]/div/div[1]/div/div/table/tbody/tr/td/table/tbody/tr/td[3]/div/div/div[2]/div[3]/div/div/div/div[2]/div/div[2]/div[2]/div/div/div[3]/div[2]/div/div/div/div[3]/div/div/div/div/div/div[2]/div[1]/div/div/div/div/table[1]/tbody/tr/td/table/tbody/tr/td[3]/span/table/tbody/tr[2]/td[2]")).click();
ieDriver.findElement(By.xpath("/html/body/div[1]/div/div/div/div[3]/div/div[1]/div/div/table/tbody/tr/td/table/tbody/tr/td[3]/div/div/div[2]/div[3]/div/div/div/div[2]/div/div[2]/div[2]/div/div/div[3]/div[2]/div/div/div/div[3]/div/div/div/div/div/div[2]/div[1]/div/div/div/div/table[1]/tbody/tr/td/table/tbody/tr/td[1]/span/table/tbody/tr[2]/td[2]")).click();
}else{
Thread.sleep(timmer*3);
//click on the Add button
ieDriver.findElement(By.xpath("/html/body/div[1]/div/div/div/div[3]/div/div[1]/div/div/table/tbody/tr/td/table/tbody/tr/td[3]/div/div/div[2]/div[3]/div/div/div/div[2]/div/div[2]/div[2]/div/div/div[3]/div[2]/div/div/div/div[3]/div/div/div/div/div/div[2]/div[1]/div/div/div/div/table[1]/tbody/tr/td/table/tbody/tr/td[1]/span/table/tbody/tr[2]/td[2]")).click();
}
I think somewhere you are doing wrong as your xpath is too long recommended to use relative xpath. and if your element having id then use it directly
I don't know how you are checking that element is displayed
use following method to do same -
boolean check= driver.findElement(By.id("tVLQur-btn")).isEnabled();
if(check) // if `check` returns true then go in if condition
{
driver.findElement(By.id("tVLQur-btn")).click();
}
else
{
// DO SOME OTHER FUNCTIONALITY
}
Edited
If your button id is dynamic then use following xpath to find the element
//td[contains(.,'Update')]/../preceding-sibling::tr//button
like
boolean check= driver.findElement(By.xpath("//td[contains(.,'Update')]/../preceding-sibling::tr//button")).isEnabled();
It will find your update button and check weather it is enable or not
I believe at the very least if you break the line into two statements, one to located the element, and the next to check its enabled status, at least you'll know that the problem (most likely) lies in the element location, as it would return a nul to the web element and isEnabled would return a null pointer exception. There are always ways to use relatively short xpaths if no consistent ID is available. Check your browser for add-ins that include the word xpath, and you'll see there are a variety of xpath helpers out there. The inspect element option is the safest, at least in theory, but it's not always the best.

Expand specific rows in Angular-ui-grid

I'm using angular-ui-grid 3.0.5 with the treeview extension to display a tree. The data loads normally, everything works as expected, except that expandRow fails silently.
My use case is this: suppose we have a path like a > b > c and I need c shown to the user as preselected. I know the selection is correctly done because when I manually expand the parent rows, the child row is indeed selected.
Should I call expandAllRows, all rows would be expanded. However, calling expandRow with references on rows a and b taken from gridOptions.data leads to nothing happening: all rows will remain collapsed.
Is there any precaution to be taken that I have maybe overlooked, or is this a bug?
There's one mention in a closed issue that may be related to this but problem I'm having, but I'm not even sure it's related, given how dry the comment/solution was.
There's no example of using expandRow in the documentation but it's in both the API and the source code.
The gridRow objects mentioned in the documentation http://ui-grid.info/docs/#/api/ui.grid.treeBase.api:PublicApi are not the elements you put into the data array (though this seems to be not explained anywhere).
What the function expects is an object that the grid creates when building the tree, you can access them by looping through the grids treeBase.tree array. This will only be valid when the grid has built the tree, it seems, so it is not directly available when filling in the data, that's why registering a DataChangeCallback helps here https://github.com/angular-ui/ui-grid/issues/3051
// expand the top-level rows
// https://github.com/angular-ui/ui-grid/issues/3051
ctrl.gridApi.grid.registerDataChangeCallback(function() {
if (ctrl.gridApi.grid.treeBase.tree instanceof Array) {
angular.forEach(ctrl.gridApi.grid.treeBase.tree, function(node) {
if (node.row.treeLevel == 0) {
ctrl.gridApi.treeBase.expandRow(node.row);
}
});
}
});
self.onContentReady = function (e) {
e.component.expandRow(e.component.getKeyByRowIndex(0));
e.component.expandRow(e.component.getKeyByRowIndex(1));
};
selec which row you wanna expand

Webdriver visibility of element without locator

I want to check the text of my string(client) inside tag using the following code:
boolean feedBack = driver.findElement(By.cssSelector("body")).getText().contains(client);
If it returns true then, Is there any way to directly check the visibility of this string (without locator)?
Find an element that contains the needed text in its text node:
WebElement element = driver.findElement(By.xpath("//*[text()='" + client + "']"));
If needed, use normalize-space() or contains() instead:
WebElement element = driver.findElement(By.xpath("//*[contains(text(),'" + client + "')]"));
This will find the innermost element containing the text, not its any random ancestor.
Check whether it's visible via
element.isDisplayed()
Note that you have to be sure your text only appears once on the page for this to be okay. But the solution can be easily adapted for more elements, too.

Resources