How can we find only the WebElements present on the viewport with using selenium python find_elements method? - selenium

I want to automate the task of scrolling through a web page and clicking on the buttons. But, when trying to find web elements with driver.find_elements() getting a list of all web elements which are not present on the viewport so clicking on them results in error "MoveTargetOutOfBounds"
def like(self):
likeButtonXpath = '//div[#aria-label="Like"]'
try:
a = ActionChains(self.browser)
likeWebElements = self.browser.find_elements(
By.XPATH, likeButtonXpath)
for element in likeWebElements:
try:
a.move_to_element(element).perform()
sleep(3)
element.click()
except:
print("error while clicking button")
except Exception as e:
print(e)
def scrollLike(self):
last_height = self.browser.execute_script(
"return document.body.scrollHeight")
cnt = self.maxScroll
curHeight = last_height
while cnt >= 0:
self.like()
self.browser.execute_script("window.scrollBy(0,1000)", "")
sleep(30)
new_height = self.browser.execute_script(
"return document.body.scrollHeight")
if new_height == last_height:
break
last_height = new_height
cnt -= 1

Is not possible to tell driver.find_elements to limit its search on the elements in the viewport. A possible workaround would be to compute the coordinates of the element and then scroll to them, but this is usually an hard task and almost impossible to get precise coordinates.
With javascript and scrollIntoView we can easily bring the desired element within the viewport.
driver.execute_script('arguments[0].scrollIntoView();', element)
By adding block: "center" the scroll will be executed in such a way that element is at the center of the viewport. By adding behavior: "smooth" the scroll will be smooth.
driver.execute_script('arguments[0].scrollIntoView({block: "center", behavior: "smooth"});', element)

Related

How to Find coordinates Canvas use extension Coords Firefox? [duplicate]

I have an online calculator that i want to automate some operations for, like subtraction, division, etc. but the thing is that there are no elements since it is a canvas app calculator (link below). I'm wondering how can i click the buttons so im able to automate some operations?
The online calculator im trying to automate:
https://www.online-calculator.com/full-screen-calculator/
The canvas HTML code
<canvas id="canvas" width="335" height="434" style="position: absolute; display: block; background-color: rgb(255, 255, 255); width: 167.939px; height: 217px;"></canvas>
My Selenium-Java code
driver.get("https://www.online-calculator.com/full-screen-calculator/");
driver.manage().window().maximize();
WebElement li = driver.findElements(By.tagName("iframe")).get(0);
driver.switchTo().frame(li);
WebElement canv = driver.findElements(By.tagName("canvas")).get(0);
System.out.println(canv.getSize());
System.out.println(canv.getLocation());
try {
Actions builder = new Actions(driver);
builder.moveToElement(canv, x, y);
builder.click();
builder.perform();
} catch (Exception e)
{
// do nothing
}
So as you see in above code, I still have to find the x and y of the operations i want to run. For ex, how can i find the coordinates of the buttons 10, 4 and the subtraction operation '-', if I want to calculate '10 - 4' and then validate that it is equal to 6. Any help would be highly appreciated. Thanks :)
Note: The canvas width, height and location will change if the window size changes ((Im thinking later of locking the window size so my tests are not flaky on different screens)).
The <canvas> element is within an <iframe>. So to invoke click() on the elements within the <canvas> you have to:
Induce WebDriverWait for the desired frame to be available and switch to it.
Induce WebDriverWait for the desired element to be clickable.
You can use the following solution:
Code Block:
driver.get("https://www.online-calculator.com/full-screen-calculator/")
new WebDriverWait(driver, 10).until(ExpectedConditions.frameToBeAvailableAndSwitchToIt(By.id("fullframe")));
WebElement canvas = new WebDriverWait(driver, 10).until(ExpectedConditions.elementToBeClickable(By.id("canvas")));
HTML5 Canvas
The element is only a container for graphics and is a rectangular area on an HTML page. By default, a canvas has no border and no content. However, an id attribute (to be referred to in a script), a width and height attribute are specified to define the size of the canvas. To add a border, the style attribute is used. An example:
<canvas id="myCanvas" width="200" height="100" style="border:1px solid #000000;"></canvas>
The HTML canvas is a two-dimensional grid. The upper-left corner of the canvas has the coordinates (0,0).
In the article Automated Testing of HTML5 Canvas Applications with Selenium WebDriver #Aaron Mulder mentions, interacting with the elements within <canvas> is possible using event support of the Actions Class API:
moveToElement(WebElement target, int xOffset, int yOffset): Moves the mouse to an offset from the top-left corner of the element.
new Actions(driver).moveToElement(canvas, xWithinCanvas, yWithinCanvas).click().perform();
But is not 100% reliable as, every mouse down, mouse up, or mouse click happens at the center of the element. So the code above produces a mouse move event to the provided coordinates (x,y), then a mouse move event to the center of the <canvas>, then a mouse down, mouse up, and click at the center of the <canvas>. That should have been fine for a <button> but is not worth for a <canvas>, where you want to be able to click at a specific location.
The workaround, is to dispatch synthesized mouse events using JavaScript as follows:
// pageX and pageY are offsets which you need to know through mouse coordinates.
((JavascriptExecutor)driver).executeScript("var evt = $.Event('click', { pageX: " + x +
", pageY: " + (y + 55) + " } );" +
"$('#myCanvas').trigger(evt);");
However, to click on the elements within the <canvas> you can be at ease using firefox as the mouse move event works well in Firefox and you can avoid using the mouse coordinates as the event processing as follows:
new Actions(driver).moveToElement(
canvas, xWithinCanvas, yWithinCanvas).perform();
((JavascriptExecutor)driver).executeScript("$('#canvas').click();");
This usecase
To automate a substruction operation e.g. 3-1= using Selenium you can use the following solution:
Code Block:
driver.get("https://www.online-calculator.com/full-screen-calculator/");
new WebDriverWait(driver, 10).until(ExpectedConditions.frameToBeAvailableAndSwitchToIt(By.id("fullframe")));
WebElement canvas = new WebDriverWait(driver, 10).until(ExpectedConditions.elementToBeClickable(By.id("canvas")));
//clicking on 3
new Actions(driver).moveToElement(canvas, 0, 0).moveByOffset(0,(255/6)*3).click().build().perform();
//clicking on the substract sign (-)
new Actions(driver).moveToElement(canvas, 0, 0).moveByOffset((174/5)*2,(255/6)*3).click().build().perform();
//clicking on 1
new Actions(driver).moveToElement(canvas, 0, 0).moveByOffset(-(174/5)*4,(255/6)*3).click().build().perform();
//clicking on equals to sign (=)
new Actions(driver).moveToElement(canvas, 0, 0).moveByOffset((174/5)*4,(255/6)*4).click().build().perform();
Execution Video:
Reference
You can find a couple of relevant detailed discussion in:
How to perform Mouse Wheel scrolling over HTML5 Canvas in Selenium?
I think you are facing difficulty to find the coordinates of the element. To find them easily will have some plugins for each browser. For chrome, you can use Page Ruler and for Firefox, you can use MeasureIt. By using these tools you can get the coordinates of the particular element. After that, you can easily click on those elements. Watch the following video for how to use MeasureIt in firefox (follow from 11:45 minutes).
Finding the coordinates of particular Element using Firefox MeasureIt plugin
And your other concern is how to find the coordinates of the size of the screen changes? normally will have some standard screen sizes for each monitor or laptop.
So first, get the size of the screen using selenium and then you can use if the condition for each screen size. Means, if the size is something like 800*1200 then use these coordinates else use some other coordinates for each screen size.

Scroll down google maps webpage using selenium python

I am trying to collect data on a google map webpage, however i can't find a way to scroll down the page to get all the data.
I'm using python 3 and Selenium package,
thank you in advance,
Try this code it works for me
SCROLL_PAUSE_TIME = 5
last_height = driver.execute_script("return document.body.scrollHeight")
number = 0
while True:
number = number+1
Scroll down to bottom
ele = driver.find_element_by_xpath('//*[#id="QA0Szd"]/div/div/div[1]/div[2]/div/div[1]/div/div/div[2]')
driver.execute_script('arguments[0].scrollBy(0, 5000);', ele)
Wait to load page
time.sleep(SCROLL_PAUSE_TIME)
Calculate new scroll height and compare with last scroll height
ele = driver.find_element_by_xpath('//*[#id="QA0Szd"]/div/div/div[1]/div[2]/div/div[1]/div/div/div[2]')
new_height = driver.execute_script("return arguments[0].scrollHeight", ele)
if new_height == last_height:
break
last_height = new_height
You can try to locate the element and use browserelement.execute_script("arguments[0].scrollIntoView(true);",option)
For example:
driver.get("https://www.google.com/maps/place/Bengaluru,+Karnataka/#12.95396,77.4908522,11z/data=!4m5!3m4!1s0x3bae1670c9b44e6d:0xf8dfc3e8517e4fe0!8m2!3d12.9715987!4d77.5945627")
option = driver.find_element_by_xpath("//div[#class='hNLpDc-HiaYvf-DWDkFd-HiaYvf-haAclf']")
driver.execute_script("arguments[0].scrollIntoView(true);",option)
To scroll down with selenium:
driver.execute_script('window.scrollTo(0, document.body.scrollHeight);')
and smooth way
driver.execute_script('window.scrollTo({ top: document.body.scrollHeight, behavior: "smooth" });')

Check if it's possible to scroll down with RSelenium

I'm using RSelenium to automatically scroll down a social media website and save posts. Sometimes I get to the bottom of the webpage and no more posts can be loaded as no more data is available. I just want to be able to check if this is the case so I can stop trying to scroll.
How can I tell if it's possible to continue scrolling in RSelenium? The code below illustrates what I'm trying to do - I think I just need help with the "if" statement.
FYI there's a solution for doing this in Python here (essentially checking if page height changes between iterations), but I can't figure out how to implement it (or any another solution) in R.
# Open webpage
library(RSelenium)
rD = rsDriver(browser = "firefox")
remDr = rD[["client"]]
url = "https://stocktwits.com/symbol/NZDCHF"
remDr$navigate(url)
# Keep scrolling down page, loading new content each time.
ptm = proc.time()
repeat {
remDr$executeScript("window.scrollTo(0,document.body.scrollHeight);")
Sys.sleep(3) #delay by 3sec to give chance to load.
# Here's where i need help
if([INSERT CONDITION TO CHECK IF SCROLL DOWN IS POSSIBLE]) {
break
}
}
Stumbled across a way to do this in Python here and modified it to work in R. Below is a now-working update of the original code I posted above.
# Open webpage
library(RSelenium)
rD = rsDriver(browser = "firefox")
remDr = rD[["client"]]
url = "https://stocktwits.com/symbol/NZDCHF"
remDr$navigate(url)
# Keep scrolling down page, loading new content each time.
last_height = 0 #
repeat {
remDr$executeScript("window.scrollTo(0,document.body.scrollHeight);")
Sys.sleep(3) #delay by 3sec to give chance to load.
# Updated if statement which breaks if we can't scroll further
new_height = remDr$executeScript("return document.body.scrollHeight")
if(unlist(last_height) == unlist(new_height)) {
break
} else {
last_height = new_height
}
}

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.

SVG Selenium click do not work

I want to simulate a simple mouse click and drag within an svg element.
I managed to get the coordinates of my starting and ending point, both absolute (window coordinates) and relative to the encapsulating svg element.
Here is the code I am using to simulate the mouse:
Actions builder = new Actions(driver);
builder.moveToElement(area, xStart, yStart);
builder.clickAndHold();
builder.moveToElement(area, xStop, yStop);
builder.release();
Action setFilter = builder.build();
setFilter.perform();
Where area is a WebElement representing my svg and the coordinates are relative to that element. Note that:
area.getLocation(); // returns null
This made me wonder whether the webdriver is able to find that element at all. So I tried with absolute coordinates:
Actions builder = new Actions(driver);
builder.moveByOffset(chart.getLocation().x + xStart, chart.getLocation().y + yStart);
builder.clickAndHold();
builder.moveByOffset(xStop - xStart, yStop - yStart);
builder.release();
Action setFilter = builder.build();
setFilter.perform();
where chart is the div surrounding the svg element (note that the offset between the div position and the svg position is only 10 pixels and is not significant). That didn't work either and I also tried by relative position to the div but still no luck.
What am I doing wrong here?
I got a hack for it to work but it requires the webpage to be opened on the foreground. If you're doing anything else at the same time it may break the test and I cannot say if it would work if ran remotely.
Here's what it looks like:
Robot robert = new Robot();
robert.mouseMove(xStart, yStart);
// full click once to get focus on the window
robert.mousePress(MouseEvent.BUTTON1_MASK);
robert.mouseRelease(MouseEvent.BUTTON1_MASK);
// then set the filter
robert.mousePress(MouseEvent.BUTTON1_MASK);
robert.mouseMove(xStop, yStop);
robert.mouseRelease(MouseEvent.BUTTON1_MASK);