'Custom wait in Testcafe
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:
- Robot clicks the button from the lef menu
- When the subpage appears it clicks the button to expand list of buttons
- 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 ?
Solution 1:[1]
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);
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | Rob C |
