0

While testing web application(next.js) with the menu on the left and functionalities like notifications at the top I experienced the problem with clicking on the dropdown button. Steps:

  1. Robot clicks the button from the lef menu
  2. When the subpage appears it clicks the button to expand list of buttons
  3. In the meantime notification appears and expanded list of buttons closes. - test fails

What is the best way in Testcafe to wait till the page is fully loaded after click ? I tried waitForReact but probably it cannot be used anytime in the test but only inside beforeEach hook. In Java to wait for page to be loaded I used a custom wait:

public void waitAllRequest() {
    waitUntilJSReady();
    ajaxComplete();
    waitUntilJQueryReady();
    waitUntilAngularReady();
}

Here is the implementation for the above methods(without waitUntilAngularReady):

public class JSWaiter {
    private static WebDriver jsWaitDriver;
    private static WebDriverWait jsWait;
    private static JavascriptExecutor jsExec;
    
    //Get the driver 
    public static void setDriver (WebDriver driver) {
        jsWaitDriver = driver;
        jsWait = new WebDriverWait(jsWaitDriver, 10);
        jsExec = (JavascriptExecutor) jsWaitDriver;
    }
    
   private void ajaxComplete() {
        jsExec.executeScript("var callback = arguments[arguments.length - 1];"
            + "var xhr = new XMLHttpRequest();" + "xhr.open('GET', '/Ajax_call', true);"
            + "xhr.onreadystatechange = function() {" + "  if (xhr.readyState == 4) {"
            + "    callback(xhr.responseText);" + "  }" + "};" + "xhr.send();");
    }
    
    private void waitForJQueryLoad() {
        try {
            ExpectedCondition<Boolean> jQueryLoad = driver -> ((Long) ((JavascriptExecutor) this.driver)
                .executeScript("return jQuery.active") == 0);
            boolean jqueryReady = (Boolean) jsExec.executeScript("return jQuery.active==0");
            if (!jqueryReady) {
                jsWait.until(jQueryLoad);
            }
        } catch (WebDriverException ignored) {
        }
    }

    private void waitUntilJSReady() {
        try {
            ExpectedCondition<Boolean> jsLoad = driver -> ((JavascriptExecutor) this.driver)
                .executeScript("return document.readyState").toString().equals("complete");
            boolean jsReady = jsExec.executeScript("return document.readyState").toString().equals("complete");
            if (!jsReady) {
                jsWait.until(jsLoad);
            }
        } catch (WebDriverException ignored) {
        }
    }
    
    private void waitUntilJQueryReady() {
        Boolean jQueryDefined = (Boolean) jsExec.executeScript("return typeof jQuery != 'undefined'");
        if (jQueryDefined) {
            poll(20);
            waitForJQueryLoad();
            poll(20);
        }
    }

How to implement such code in Testcafe ?

larry
  • 1
  • 1
  • 2
  • Instead of using `jsExec.executeScript`, you might be able to do something similar with TestCafe's ClientFunction: https://testcafe.io/documentation/402832/guides/basic-guides/obtain-client-side-info – Rob C Apr 07 '22 at 18:28

1 Answers1

0

I used a custom function for this. It waits for xhr requests to be less than 2 in ~0.5s, as well as JQuery to exist on the page before continuing the test:

/* global window */
/* eslint-disable no-await-in-loop */
import { t, ClientFunction, RequestLogger } from 'testcafe';

const xhrLogger = RequestLogger(
    { url, method },
    {
      logRequestHeaders: false,
      logRequestBody: true,
      stringifyRequestBody: true,
    }
  );

const getRequests = xhrLogger(/some.url/, 'get');
const checkForJquery = ClientFunction(() => window.jQuery);

export default async function confirmPageLoadComplete() {
  await t.addRequestHooks(getRequests);
  for (let i = 0; i < 20; i += 1) {
    await t.wait(500);
    if (getRequests.requests.length < 2 && (await checkForJquery())) {
      await t.removeRequestHooks(getRequests);
      return;
    }
    getRequests.clear();
  }
}

It doesn't have an assertion, so if the page isn't ready after ~10s, it continues the test and would fail at the next step in the test code.

import confirmPageLoadComplete from './confirmPageLoadComplete';

fixture('This is a demo fixture').page(baseUrl);

test('This is a demo test', async t => {
  await t
    .navigateTo('/some/page')
    .expect(page.element.visible)
    .ok()
    .click(page.button);
  await confirmPageLoadComplete();
  await t
    .expect(page.element.value)
    .eql('loaded')
    .click(page.anotherButton);
Rob C
  • 662
  • 5
  • 15
  • Rob, thank you so much, I suppose this is what I needed. Currently I am working on other tasks but I will confirm here when I test your code snippet – larry May 09 '22 at 22:18