3

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)).

undetected Selenium
  • 183,867
  • 41
  • 278
  • 352
Gėorge
  • 43
  • 1
  • 10

2 Answers2

3

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:

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 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:

AutomatingCanvasSelenium


Reference

You can find a couple of relevant detailed discussion in:

undetected Selenium
  • 183,867
  • 41
  • 278
  • 352
  • 1
    Hey @DebanjanB, thanks for the help. Looking, analyzing and searching what you have posted, I have 2 questions. By using above code in Firefox, the xWithinCanvas would be the horizontal offset from the top-left corner of the canvas? and yWithinCanvas would be the vertical offset from the top-left corner? - moreover, Im assuming that i should use #canvas (i.e. `driver.executeScript("$('#canvas').click();");` ) instead of `driver.executeScript("$('#diagramCanvas').click();");`? if you can elaborate on what `$('#diagramCanvas').click();` does that would be helpful... – Gėorge Jan 27 '20 at 15:01
  • 1
    I updated my code using your above suggestion, using Firefox browser, and Im getting the following error `Exception in thread "main" org.openqa.selenium.interactions.MoveTargetOutOfBoundsException: (770, 769) is out of bounds of viewport width (1440) and height (639)` -I got from another post for you [MoveTargetOutOfBoundsException with Firefox](https://stackoverflow.com/questions/44777053/selenium-movetargetoutofboundsexception-with-firefox) that im hitting outside the [Viewport](https://www.w3schools.com/css/css_rwd_viewport.asp), and im still trying to find the correct coordinates to use... – Gėorge Jan 27 '20 at 15:06
  • A small update, changed `#diagramCanvas` as `#canvas` – undetected Selenium Jan 27 '20 at 15:09
  • 1
    I think as well to add `((JavascriptExecutor) driver)` instead of `driver` in `driver.executeScript("$('#canvas').click();");` - so it would be `((JavascriptExecutor) driver).executeScript("$('#canvas').click();");` - I changed the coordinates randomly and im not receiving the 'out of bounds' error anymore...but im getting the `Exception in thread "main" org.openqa.selenium.JavascriptException: ReferenceError: $ is not defined` now... – Gėorge Jan 27 '20 at 15:22
  • @Gėorge Checkout the updated answer and let me know if any questions. – undetected Selenium Jan 28 '20 at 16:28
  • 1
    just so i make sure, how did you find the coordinates you used on the `moveByOffset` methods in your above code? – Gėorge Jan 29 '20 at 11:17
  • @Gėorge You need to consider the `height` and `width` attributes and the effect of the applied **Css** `width: 167.939px;` and `height: 217px;` – undetected Selenium Jan 29 '20 at 11:22
  • 1
    hmm, but those are interactive with the screen size and browser dimensions... – Gėorge Jan 29 '20 at 11:48
1

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.