0

Problem 1: I am using https://candymapper.com/ demo page to practice automation tests and I encountered the following problem. When I am using Javascript Executor .scrollIntoView, afterwards my element becomes stale and test breaks.

  1. As using Thread.sleep is not a good practice, is there any other way I can resolve the issue using wdwait?
  2. Also, is there another way to scroll to the element without using Javascript Executor?

Here is my code:

@Test
    public void candyMapperTest() throws InterruptedException {
        driver.get("https://candymapper.com/");
        wdwait.until(ExpectedConditions.presenceOfElementLocated(By.id("popup-widget37723-close-icon")));
        driver.findElement(By.id("popup-widget37723-close-icon")).click();
        wdwait.until(ExpectedConditions.elementToBeClickable(driver.findElement(By.cssSelector("input[data-aid=\"CONTACT_FORM_EMAIL\"]"))));
        jse.executeScript("arguments[0].scrollIntoView(true)", driver.findElement(By.cssSelector("input[data-aid=\"CONTACT_FORM_EMAIL\"]")));
        //Thread.sleep(3000);
        //wdwait.until(ExpectedConditions.refreshed(ExpectedConditions.elementToBeClickable(By.cssSelector("input[data-aid=\"CONTACT_FORM_EMAIL\"]"))));
        //wdwait.until(ExpectedConditions.elementToBeClickable(By.cssSelector("input[data-aid=\"CONTACT_FORM_EMAIL\"]")));
        wdwait.until(ExpectedConditions.elementToBeClickable(driver.findElement(By.cssSelector("input[data-aid=\"CONTACT_FORM_EMAIL\"]"))));
        driver.findElement(By.cssSelector("input[data-aid=\"CONTACT_FORM_EMAIL\"]")).sendKeys("[email protected]");
        wdwait.until(ExpectedConditions.elementToBeClickable(By.cssSelector("button[data-aid=\"CONTACT_SUBMIT_BUTTON_REND\"]")));
        driver.findElement(By.cssSelector("button[data-aid=\"CONTACT_SUBMIT_BUTTON_REND\"]")).click();
        wdwait.until(ExpectedConditions.elementToBeClickable(By.xpath("//span[contains(text(), \"inquiry\")]")));
        Assert.assertEquals("Thank you for your inquiry! We will get back to you within 48 hours.", driver.findElement(By.xpath("//span[contains(text(), \"inquiry\")]")).getText());

Problem 2: I have a similar problem in another test on this page: (https://demoqa.com/automation-practice-form). I want to assert input field border color change to red. Selenium is too fast and does the assert before border color changes, so test breaks with rbg color mismatch. Also, I am using Javascript Executor to click on Submit button, because there is a google ads banner that cannot be closed or minimized (shows only on Chrome).

Submit button hiden behind the ad image

  1. As using Thread.sleep is not a good practice, is there any other way I can resolve the issue using wdwait?
  2. Also, is there another way to click the element in this case without using Javascript Executor?

My code for Problem 2:

@Test
    public void demoQANegativeRegistrationTest() throws InterruptedException {
        driver.get("https://demoqa.com/automation-practice-form");
        driver.findElement(By.id("userNumber")).sendKeys("12345");
        jse.executeScript("arguments[0].click();", driver.findElement(By.id("submit")));
        Thread.sleep(2000);
        //wdwait.until(ExpectedConditions.elementToBeClickable(By.xpath("//*[@class=\"was-validated\"]")));
        WebElement icon = driver.findElement(By.id("userNumber"));
        String userNumberBorderColor = icon.getCssValue("border-color");
        Assert.assertEquals("rgb(220, 53, 69)", userNumberBorderColor);

Problem 1: I tried introducting several different WebDriverWaits like presenceOfElement, elementToBeClickable, refreshed, but each time the result is the same (some of them are commented in the code above). If I add Thread.sleep after jse, test runs perfectly.

Problem 2: I identified class element with value "was-validated" appears after border color changes to red, so I tried waiting on that before doing the assert, but every time test gets wrong rbg color (default grey one). You can find it commented in the code. When I introduce a short Thread.sleep before the assert, test runs perfectly every time.

For clicking on Submit button I tried going fullscreen and using actions.moveToElement but in both cases click would get intercepted by google ads banner. I only managed to perform the click through jse script.

6
  • Sleep vs wait isn't causing the stale element reference, it's the re-render in between that's causing that. Commented Nov 19, 2023 at 8:59
  • @pguardiario how come sleep resolves the issue and wait doesn't? Sorry if the questions sounds stupid enough, but I would like to understand better. Commented Nov 19, 2023 at 13:20
  • You just got lucky with the timing (sleep took a little longer?) Commented Nov 20, 2023 at 1:23
  • Thread.sleep is there for a reason. I wouldn't discount it as sometimes it's exactly what you want to do. (And webdriverwaits use them inside their polling loops...) You can functionize your method, catch thrown StaleElement Exceptions and re-call if needed. Or use a while loop of some kind. There are also new Expected Conditions you can use to check stalenessOf an element. selenium.dev/selenium/docs/api/java/org/openqa/selenium/support/… That will help you with case2. Commented Nov 20, 2023 at 17:44
  • basically if Stale Element Exception is thrown, it means you have to re-get the element reference. See my answer here for what I use: stackoverflow.com/questions/57396173/merge-two-webelements-list/… There's another technique which uses time() and loops for a certain amount of time (continues if stale element is caught) and breaks when a certain amount of time goes by... see here: stackoverflow.com/questions/66820416/… Commented Nov 20, 2023 at 18:02

1 Answer 1

0

In your first case your are getting StaleElementReferenceException as far as input is re-rendered after load. To avoid it, you should scroll to from first and wait for email input to be rendered.

Send button itself has debounce (that means that it has delay on click after data was entered by fronted logic), so you should wait for some time after data was entered.

For scroll you can use moveToElement from Selenium Actions

Actions actions = new Actions(driver);

WebDriverWait wdwait = new WebDriverWait(driver, 10);
driver.get("https://candymapper.com/");
WebElement popupClose = driver.findElement(By.id("popup-widget37723-close-icon"));
popupClose.click();
wdwait.until(ExpectedConditions.invisibilityOf(popupClose));
WebElement form = driver.findElement(By.cssSelector("[data-aid=CONTACT_FORM_CONTAINER_REND]"));
actions.moveToElement(form).perform();
WebElement formEmail = wdwait.until(ExpectedConditions.visibilityOfElementLocated(By.cssSelector("input[data-aid=\"CONTACT_FORM_EMAIL\"]")));
actions.moveToElement(formEmail).click().perform();
String email = "[email protected]";
actions.sendKeys(email).sendKeys(Keys.TAB).sendKeys(Keys.TAB).pause(1000).sendKeys(Keys.ENTER).perform();

In second case same as in first - you call assertion too early. Properties all applied async, so making assertion after changes can not work / be stable, as far as changes have not applied yet.

The solution here is to wait for property to be equal or wait for property does not change during some time interval. There are many approaches how to do it in Java / JS, I share one of them to use combination of ExpectedConditions and JS functions

This function in JS returns your border-color.

return window.getComputedStyle(document.querySelector('#userNumber'))['border-color']

So you can just call it inside jsReturnsValue expectations:

WebElement icon = driver.findElement(By.id("userNumber"));
wdwait.until(ExpectedConditions.jsReturnsValue("return window.getComputedStyle(document.querySelector('#userNumber'))['border-color'] === 'rgb(220, 53, 69)' ?? undefined"));
Sign up to request clarification or add additional context in comments.

5 Comments

I used the solution for problem 2 and it is now completely resolved. I am curious what does this part of code exactly mean and refer to "?? undefined"?
Regarding problem 1, I tried solution (changing scroll to actions.moveToElement), but now I am getting some other problems. Email gets typed in, but test ignores next step (clicking on Submit button) and breaks on assert wait, or if I remove assert, it passes successfully which doesn't make sense. Because click on the button didn't happen, I would at least expect some locator or intercept error, but there is none. Again, by adding sleep before button click, it works 100% which means general code is ok but something is missing or something is weird with page candymapper.com. Any ideas?
@nerull7 return expression ?? undefined means that that if first expression is truthy then return first expression, else return undefined (Expected condition would try again in this case as far as undefined was returned). In first case button has debounce logic on it, it is not clickable for a small time after valid data was entered. So you need to wait a bit for button to be clickable. Debounce is internal frontend logic, it doesn't handled with ElementToBeClickable. I suggest to use smth pause that I defined in updated snippet.
it works with pause thanks. From what I could see .pause() contains thread.sleep() in itself. Is it just more fancy looking method or they are different in the nature?
Pause is more fency thread.sleep() that can be called in Actions chain. In any case you can't avoid usage for components with debounce-like logic. In automation you usually know timeout that was set up for component, so you include it in your test. In scrapping you can get to it by own research or use retry with wait.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.