testplane
Version:
Tests framework based on mocha and wdio
147 lines • 6.46 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.waitForSelectorsToSettle = void 0;
const node_timers_1 = require("node:timers");
const debug_1 = __importDefault(require("debug"));
const debug = (0, debug_1.default)("testplane:screenshots:selectors-settle");
const SELECTORS_SETTLE_BROWSER_TIMEOUT_MS = 3000;
const SELECTORS_SETTLE_FALLBACK_INTERVAL_MS = 50;
const SELECTORS_SETTLE_FALLBACK_ATTEMPTS = 5;
async function waitForSelectorsToSettle(browser, selectors, options = {}) {
try {
const settleResult = await waitForSelectorsToSettleInBrowser(browser, selectors, options);
if (settleResult.success) {
return;
}
debug("Browser-side waitForSelectorsToSettle cannot run, using Node-side polling");
}
catch (err) {
const scriptTimeoutError = err;
const scriptTimeoutErrorText = err && typeof err === "object"
? [scriptTimeoutError.name, scriptTimeoutError.message, scriptTimeoutError.error].join(" ")
: "";
if (!/script timeout/i.test(scriptTimeoutErrorText)) {
throw err;
}
debug("Browser-side waitForSelectorsToSettle hit script timeout, using Node-side polling");
}
await waitForSelectorsToSettleInNode(browser, selectors);
}
exports.waitForSelectorsToSettle = waitForSelectorsToSettle;
async function waitForSelectorsToSettleInBrowser(browser, selectors, options) {
if (options.needsCompatLib) {
return { success: false };
}
const originalScriptTimeout = typeof browser.getTimeouts === "function" ? (await browser.getTimeouts())?.script : undefined;
const shouldRestoreScriptTimeout = typeof originalScriptTimeout === "number";
let executionError = null;
let result;
if (shouldRestoreScriptTimeout) {
await browser.setTimeout({ script: SELECTORS_SETTLE_BROWSER_TIMEOUT_MS });
}
else {
debug("Browser does not report script timeout, running browser-side polling without timeout override");
}
try {
result = await browser.execute(async (selectors) => {
let setTimeoutSource = "";
try {
setTimeoutSource = typeof setTimeout === "function" ? Function.prototype.toString.call(setTimeout) : "";
}
catch {
return { success: false };
}
if (!setTimeoutSource.includes("[native code]")) {
return { success: false };
}
const PAGE_SETTLE_MAX_WAIT_MS = 50;
const PAGE_SETTLE_MAX_ITERATIONS = 500;
const PAGE_SETTLE_MATCHES_THRESHOLD = 3;
const startedAt = performance.now();
let iterations = 0;
let matches = 0;
let lastBoundingClientRects = selectors.map(selector => {
const element = document.querySelector(selector);
return element ? element.getBoundingClientRect() : null;
});
while (performance.now() - startedAt < PAGE_SETTLE_MAX_WAIT_MS &&
iterations < PAGE_SETTLE_MAX_ITERATIONS &&
matches < PAGE_SETTLE_MATCHES_THRESHOLD) {
const currentBoundingClientRects = selectors.map(selector => {
const element = document.querySelector(selector);
return element ? element.getBoundingClientRect() : null;
});
if (currentBoundingClientRects.every((rect, index) => {
const lastRect = lastBoundingClientRects[index];
if (!rect || !lastRect) {
return rect === lastRect;
}
return rect.top === lastRect.top && rect.height === lastRect.height;
})) {
matches++;
}
else {
matches = 0;
}
lastBoundingClientRects = currentBoundingClientRects;
iterations++;
await new Promise(resolve => setTimeout(resolve, 5));
}
return { success: true };
}, selectors);
}
catch (err) {
executionError = err;
}
if (shouldRestoreScriptTimeout) {
try {
await browser.setTimeout({ script: originalScriptTimeout });
}
catch (restoreError) {
if (executionError) {
debug("Failed to restore original script timeout after selectors settle error: %s", restoreError instanceof Error ? restoreError.message : String(restoreError));
}
else {
throw restoreError;
}
}
}
if (executionError) {
throw executionError;
}
return result ?? { success: true };
}
async function waitForSelectorsToSettleInNode(browser, selectors) {
const PAGE_SETTLE_MATCHES_THRESHOLD = 3;
let matches = 0;
let lastBoundingClientRects;
for (let attempt = 0; attempt <= SELECTORS_SETTLE_FALLBACK_ATTEMPTS && matches < PAGE_SETTLE_MATCHES_THRESHOLD; attempt++) {
if (lastBoundingClientRects) {
await new Promise(resolve => {
const timeoutId = (0, node_timers_1.setTimeout)(resolve, SELECTORS_SETTLE_FALLBACK_INTERVAL_MS);
timeoutId.unref();
});
}
const previousBoundingClientRects = lastBoundingClientRects;
const currentBoundingClientRects = await browser.execute(function (selectors) {
return selectors.map(function (selector) {
const element = document.querySelector(selector);
const rect = element ? element.getBoundingClientRect() : null;
return rect ? { top: rect.top, height: rect.height } : null;
});
}, selectors);
if (previousBoundingClientRects) {
matches = currentBoundingClientRects.every((rect, index) => {
const previousRect = previousBoundingClientRects[index];
return rect?.top === previousRect?.top && rect?.height === previousRect?.height;
})
? matches + 1
: 0;
}
lastBoundingClientRects = currentBoundingClientRects;
}
}
//# sourceMappingURL=wait-for-selectors-to-settle.js.map