How to scroll into view when dealing with an "internal" scroll bar - selenium

I apologize if this question is somewhat ambiguous. I have noticed that with Geb elements won't be scrolled into view if said element is off the page and needs to be scrolled to using an "internal" scroll bar
by "internal" scroll bar I am referring to scroll bars that are nested within a given webpage, detached from the global webpage's scroll bar.
When I attempt to grab an element that is off the page due to this internal scroll bar, geb returns a null object (geb couldn't find the element on the page)
I have done a few different hacks that manually scroll these internal scroll bars, but I was wondering if Geb provided any funcionalty to handling these nested scrollbars.
Here is a code snippet to show how I handle finding a given row:
class TabledModule extends Module {
static content = {
headers {$(By.xpath("//lane-group-header"))}
table {$(By.xpath("//div[#class=',y-class']"))}
}
Navigator getAllRows(){
return table.children()
}
Navigator getRow(String text){
return table.children().find{it.text().contains(text)}
}
Navigator getRow(int index){
return table.children()[index]
}
}
from my script:
getAllRows() //returns 50 which it should (only 20 are displayed)
def row = getRow(45) //returns a navigator as it should
row.click() //successfully clicks the correct row
def row2 = getRow("someString") //returns null when the row is off the page this is the problem and I'm wondering now if it is a bug, since getting the row by index seems to work fine.
For this module only about 20 of the 50 rows are shown to show the other rows you have to scroll through a nested scroll bar to get to them. the row I want to access is found lower on the list so it requires scrolling to access it.
what's interesting is that getAllRows().size() returns the correct number of rows: 50, but when I call getRow for a row that's off the page, it returns null. If the same row is found at the top of the list then it works. it only returns null if it needs to be scrolled to.

So I found out what my issue was. If I grab an element off screen with index instead of string. Geb is able to grab the navigator and is able to click on said navigator, but if the element is off the screen, then GEB is unable to get text on the element. to fix this I implemented this method.
Navigator getRow(String text){
JavascriptExecutor jse = (JavascriptExecutor)browser.driver
for(int x = 0; x<getAllRows().size();x++){
def row = getRow(x)
WebElement element = row.firstElement()
jse.executeScript("arguments[0].scrollIntoView(true);", element);
if(row.text().contains(text)){
return row
}
}
return null
}

Related

In the Navigator.click() method where exactly in the stack does the scrollIntoView() occur?

With Geb and WebElement, before a click is performed the webelement is first scrolled into view. I run into a problem here because there is a 'menu' bar at the top of screen that the webdriver doesn't see. this cause chrome to throw a webdriver exception stating that the element is not clickable at point x,y since the webDriver scrolls the element under the menu banner.
If I look at the implementation of the NonEmptyNavigator and subsequently remote webElement, I can't find where the scrollIntoView() occurs in the code. I want to put some code in between the scrollIntoView() and the actual click action so that I can offset the ScrollIntoView() slightly so the click can be performed. where exactly in stack for Navigator.click() does the scrolling into view happen?
Scrolling elements into view before clicking when calling WebElement.click() happens inside of the implementation of the method on the browser side so you won't find any reference to scrolling anything into view in Geb or RemoteWebElement. Here's a link to an example of the scrolling happening on the browser side from Marionette's (Firefox WebDriver implementation) code base: https://searchfox.org/mozilla-central/source/testing/marionette/interaction.js#148.
If you wish to manually scroll the element into view before clicking on it you might want to write a Geb module and overwrite click() in it:
class ViewPortOffsetModule extends Module {
Navigator click() {
//put your implementation of scrolling the element to view here, most probably using the js executor
super.click()
}
}
and then in your content definition:
static content = {
elementNeedingOffsetWhenScrollingIntoView { $("#my-element").module(ViewPortOffsetModule) }
}
Now, if you correctly implemented scrolling into view in ViewPortOffsetModule, calling elementNeedingOffsetWhenScrollingIntoView.click() will scroll the element into view with offset before clicking it.

How to validate the user has been scroll to top when he click on "back to top" button in selenium

I came across one of the scenario where I need to validate the user is scroll to top of the page when clicked on the "back to top" button on the bottom of the screen.
I tried with the following way but that didn't work.
I tried to validate the element present on the top of the page using
isDisplayed method
I have attached the image for clear description.
Solved it using the javascript concept used pageYOffset method.
Complete code
JavascriptExecutor executor = (JavascriptExecutor) driver;
Long value = (Long) executor.executeScript("return window.pageYOffset;");
pageYOffset method will return the vertical pixels, so as soon I logged in got the vertical pixels and then scrolled to the back to top button and after performing the action on back to top button, again got the vertical pixels and validated it.
isDisplayed() checks if the element is actually present in the viewport so it should work. May be put some wait in between clicking and checking isDisplayed for debugging puropose .
if (element.isDisplayed()) {
doSomething();
}
else {
doSomethingElse();
}

how to verify view is scrollable in mobile application (Automation)

Anyone knows automation script to verify a view (homePage/Browse) is scrollable or not. i can use ScrollTo(id) which is at the bottom of the page. But it is not a correct method to do, as test case passes if that element present in 1st page
Basically You cannot. You could try to cast the view to ScrollView class however any custom view can implement scroll.
Get the coordinates of any particular element like button etc unique element.
Swipe using driver.swipe() to 100 or more pixels.
And get the coordinates of that element again and check whether x or y coordinates changed or not.
This will let you know whether it is a single page application or more to scroll.
Basically there is no API to check the view is scrollable or not but if you still require this then you can do work around
#Test
public void testVerticalScroll()
{
//Try to Scroll till the 15th row
driver.scrollTo("List item:15");
//Assert that the 1st row is not visible.
Assert.assertFalse( driver.findElement(By.name("List item:01")).isDiaplyes())
//Assert that the 15th row is not visible.
Assert.assertTrue( driver.findElement(By.name("List item:15")).isDiaplyes())
}
You can consider the last visible element as "YourText" But this is
just a workaround that needs to be customized for each page.
Here we are using swipe until we find the element. In this case, the last visible element indicates the margin of the page.
Dimension dimensions = driver.manage().window().getSize();
Double screenHeightStart = dimensions.getHeight() * 0.5;
int scrollStart = screenHeightStart.intValue();
System.out.println("s="+scrollStart);
Double screenHeightEnd = dimensions.getHeight() * 0.2;
int scrollEnd = screenHeightEnd.intValue();
for (int i = 0; i < dimensions.getHeight(); i++) {
driver.swipe(0,scrollStart,0,scrollEnd,2000);
if (driver.findElement(By.name("YourText")).size()>0)
exit;
}
driver.findElement(By.name("YourText")).click();
There is a way to check it. You have to find a layer that you will target for example:
MobileElement scrollableLayer= driver.findElementById("elementID");
Then you will extract attribute value "scrollable" of that element like this:
String scrollableState = scrollableLayer.getAttribute("scrollable");
And then you can check if the String value is true or false.
if (scrollableState.equals("true")){System.out.println("it's scrolable"); }else{System.out.println("it's not scrolable");}
Or you can do whatever you want with it :)

Dojo:how to find if the widget has focus in dojo

how do I find out if my custom widget has focus in Dojo?
i have dojo editor i wnat to know if the editor has already focus or not?
you can use the module dijit/focus to find out the focus
FROM DOJO DOCS
Tracking active widgets
At any point in time there is a set of (for lack of a better word)
“active” or “focused” widgets, meaning the currently focused widget
and that widget’s ancestors. “Ancestor” can mean either DOM ancestor
(ex: TextBox –> Form), or a logical parent-child relationship (ex:
TooltipDialog –> DropDownButton).
For example, if focus is on a TextBox inside a TabContainer inside a
TooltipDialog triggered by a DropDownButton, the stack would be
TextBox –> ContentPane –> TabContainer –> TooltipDialog –>
DropDownButton.
The activeStack[] parameter indicates this set of widgets, and an app
can monitor changes to activeStack[] by:
require([ "dijit/focus" ], function(focusUtil){
focusUtil.watch("activeStack", function(name, oldValue, newValue){
console.log("Focused widget + ancestors: ", newValue.join(", "));
});
});
the question in title has a different answer than the one in the descriptions.
there are two ways achieving the question in the title, by using dojo's focusUtil ("dijit/focus"). both ways give you something that you could find the widget using it and the dijit's registry ("dijit/registry").
focusUtil.curNode: gives you the DOM Node that currently has the focus. the function below, you could get the widget reference.
function getWidgetByNode(node){
var result;
while (!result && node){
result = registry.byNode(node);
if (node.parentElement)
node = node.parentElement;
else
node = null;
}
return result;
}
var focusedWidget = getWidgetByNode(focusUtil.curNode)
focusUtil.activeStack: gives you an array of the widgets (parent to child) that has the focus. so the last item in the array is the direct widget which has the focus. index values are widget ids, so you should get the widget by the following code
var focusedWidgetId = focusUtil.activeStack[focusUtil.activeStack.length-1];
var focusedWidget = registry.byId(focusedWidgetId);
now if you want to know if the currently focused widget is some specific one, it depends on what you have in hands from that specific widget:
widget itself: like the return values of above samples. now you have to compare if these are the same thing. you can not compare two widget objects using the == operator. you could compare their ids like this:
myWidget.id == focusedWidget.id
widget's id: this way you just easily get the id of the current node from focusUtil and compare it with the id you have liek this:
myWidgetId == focusedWidgetId
references:
http://dojotoolkit.org/reference-guide/1.9/dijit/focus.html
http://dojotoolkit.org/reference-guide/1.9/dijit/registry.html
require([ "dijit/focus" ], function(focusUtil){
var activeElement = focusUtil.curNode; // returns null if there is no focused element
});
check blow url here you can see some examples
http://dojotoolkit.org/reference-guide/1.8/dijit/focus.html#dijit-focus
a) For dojo 1.6: call dijit.getFocus(). This will return an object containing the currently focused dom node, among other things (selected text, etc.). To get the corresponding widget, simply do:
var activeElement = dijit.getEnclosingWidget(dijit.getFocus().node);
This is the full reference for dijit.getFocus(), from the source code:
// summary:
// Called as getFocus(), this returns an Object showing the current focus
// and selected text.
//
// Called as getFocus(widget), where widget is a (widget representing) a button
// that was just pressed, it returns where focus was before that button
// was pressed. (Pressing the button may have either shifted focus to the button,
// or removed focus altogether.) In this case the selected text is not returned,
// since it can't be accurately determined.
//
// menu: dijit._Widget or {domNode: DomNode} structure
// The button that was just pressed. If focus has disappeared or moved
// to this button, returns the previous focus. In this case the bookmark
// information is already lost, and null is returned.
//
// openedForWindow:
// iframe in which menu was opened
//
// returns:
// A handle to restore focus/selection, to be passed to `dijit.focus`.
b) For dojo 1.7 and up, use dijit/focus:
require([ "dijit/focus" ], function(focusUtil) {
var activeElement = focusUtil.curNode; // returns null if there is no focused element
});

Is it possible to assert that the page has stopped scrolling in a waitFor

I have a page where I click a "change" link which displays another section. When this happens, the page scrolls vertically a bit so that the visible section is somewhat centered on the screen.
My next step in the test is to click something inside this newly shown container. But since the viewport scrolled, the coordinates that geb picks up for the clickable element are no longer accurate and geb tells me it can't click the link.
There's nothing I can assert in the waitFor in terms of visible content. But I'm wondering if there is a way for me to waitFor the content to stop scrolling?
waitFor { // page no longer scrolling }
If not, is there a way to just tell geb to wait a few seconds before moving on to the next event?
If you know what element you're scrolling to (the element that is at the top of browser viewport when you're done with scrolling) you can wait for the y property of a navigator representing that element to equal zero. Following is an example that you can paste into groovy console which goes to a page and then scrolls to an element by using it's id in the url (I know there is no waiting here nor the scrolling is animated but I just want to show how that property can be used to achieve want you want):
#Grapes([
#Grab('org.gebish:geb-core:0.9.0'),
#Grab('org.seleniumhq.selenium:selenium-firefox-driver:2.32.0')
])
import geb.Browser
Browser.drive {
//at the top of the page
go 'http://docs.codehaus.org/display/GROOVY/Creating+an+extension+module'
//an element we'll scroll to later
def elem = $('#Creatinganextensionmodule-Themoduledescriptor')
assert elem.y != 0
//scroll to the element
go 'http://docs.codehaus.org/display/GROOVY/Creating+an+extension+module#Creatinganextensionmodule-Themoduledescriptor'
assert elem.y == 0
}
So you should end up with something like:
waitFor { elementWeScrollTo.y == 0 }
or even:
waitFor { !elementWeScrollTo.y }
I don't know how to express it in geb, but I'd write a loop checking the document.body.pageYOffset (document.body.scrollTop for IE) repeatedly until it settles down.