Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions procs/config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/*
© 2025 Jonathan Robert Pool.

Licensed under the MIT License. See LICENSE file at the project root or
https://opensource.org/license/mit/ for details.

SPDX-License-Identifier: MIT
*/

/*
config
Shared configuration values for Testaro.
*/

// Timeout multiplier from environment variable.
// Set TIMEOUT_MULTIPLIER > 1 for slow networks/sites, < 1 for fast environments.
const timeoutMultiplier = Number.parseFloat(process.env.TIMEOUT_MULTIPLIER) || 1;

// Helper to apply multiplier to a timeout value.
// Use for navigation, interaction, and long-running operation timeouts.
// Do NOT use for very short "fail-fast" timeouts (< 100ms).
const applyMultiplier = (baseTimeout) => Math.round(baseTimeout * timeoutMultiplier);

module.exports = {
timeoutMultiplier,
applyMultiplier
};
7 changes: 6 additions & 1 deletion procs/screenShot.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,19 @@
firefox browser type.
*/

// IMPORTS

// Shared configuration for timeout multiplier.
const {applyMultiplier} = require('./config');

// FUNCTIONS

// Creates and returns a screenshot.
exports.screenShot = async (page, exclusion = null) => {
const options = {
fullPage: true,
omitBackground: true,
timeout: 4000
timeout: applyMultiplier(4000)
};
if (exclusion) {
options.mask = [exclusion];
Expand Down
34 changes: 17 additions & 17 deletions run.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ const {standardize} = require('./procs/standardize');
const {identify} = require('./procs/identify');
// Module to send a notice to an observer.
const {tellServer} = require('./procs/tellServer');
// Shared configuration for timeout multiplier.
const {applyMultiplier, timeoutMultiplier} = require('./procs/config');
// Module to create child processes.
const {fork} = require('child_process');
// Module to set operating-system constants.
Expand Down Expand Up @@ -85,8 +87,6 @@ const timeLimits = {
ibm: 30,
testaro: 150 + Math.round(6 * waits / 1000)
};
// Timeout multiplier.
const timeoutMultiplier = Number.parseFloat(process.env.TIMEOUT_MULTIPLIER) || 1;

// ########## VARIABLES

Expand Down Expand Up @@ -397,7 +397,7 @@ const launch = exports.launch = async (
// Reassign the page variable to a new page (tab) of the context (window).
page = await browserContext.newPage();
// Wait until it is stable.
await page.waitForLoadState('domcontentloaded', {timeout: 5000});
await page.waitForLoadState('domcontentloaded', {timeout: applyMultiplier(5000)});
// Add a script to the page to mask automation detection.
await page.addInitScript(() => {
Object.defineProperty(navigator, 'webdriver', {get: () => undefined});
Expand Down Expand Up @@ -1113,7 +1113,7 @@ const doActs = async (report, opts = {}) => {
if (what === 'url') {
// Wait for the URL to be the exact text.
try {
await page.waitForURL(which, {timeout: 15000});
await page.waitForURL(which, {timeout: applyMultiplier(15000)});
result.found = true;
result.url = page.url();
}
Expand All @@ -1135,7 +1135,7 @@ const doActs = async (report, opts = {}) => {
which,
{
polling: 1000,
timeout: 5000
timeout: applyMultiplier(5000)
}
);
result.found = true;
Expand All @@ -1159,7 +1159,7 @@ const doActs = async (report, opts = {}) => {
which,
{
polling: 2000,
timeout: 15000
timeout: applyMultiplier(15000)
}
);
result.found = true;
Expand All @@ -1177,7 +1177,7 @@ const doActs = async (report, opts = {}) => {
// Wait for it.
const stateIndex = ['loaded', 'idle'].indexOf(act.which);
await page.waitForLoadState(
['domcontentloaded', 'networkidle'][stateIndex], {timeout: [10000, 15000][stateIndex]}
['domcontentloaded', 'networkidle'][stateIndex], {timeout: applyMultiplier([10000, 15000][stateIndex])}
)
// If the wait times out:
.catch(async error => {
Expand All @@ -1199,7 +1199,7 @@ const doActs = async (report, opts = {}) => {
// Wait for a page to be created and identify it as current.
page = await browserContext.waitForEvent('page');
// Wait until it is idle.
await page.waitForLoadState('networkidle', {timeout: 15000});
await page.waitForLoadState('networkidle', {timeout: applyMultiplier(15000)});
// Add the resulting URL to the act.
const result = {
url: page.url()
Expand Down Expand Up @@ -1319,8 +1319,8 @@ const doActs = async (report, opts = {}) => {
const move = isClick ? 'click' : 'Enter keypress';
try {
await isClick
? selection.click({timeout: 4000})
: selection.press('Enter', {timeout: 4000});
? selection.click({timeout: applyMultiplier(4000)})
: selection.press('Enter', {timeout: applyMultiplier(4000)});
act.result.success = true;
act.result.move = move;
}
Expand All @@ -1331,7 +1331,7 @@ const doActs = async (report, opts = {}) => {
}
if (act.result.success) {
try {
await page.context().waitForEvent('networkidle', {timeout: 10000});
await page.context().waitForEvent('networkidle', {timeout: applyMultiplier(10000)});
act.result.idleTimely = true;
}
catch(error) {
Expand All @@ -1345,13 +1345,13 @@ const doActs = async (report, opts = {}) => {
// FUNCTION DEFINITION END
// If the move is a button click, perform it.
if (type === 'button') {
await selection.click({timeout: 3000});
await selection.click({timeout: applyMultiplier(3000)});
act.result.success = true;
act.result.move = 'clicked';
}
// Otherwise, if it is checking a radio button or checkbox, perform it.
else if (['checkbox', 'radio'].includes(type)) {
await selection.waitForElementState('stable', {timeout: 2000})
await selection.waitForElementState('stable', {timeout: applyMultiplier(2000)})
.catch(error => {
console.log(`ERROR waiting for stable ${type} (${error.message})`);
act.result.success = false;
Expand All @@ -1362,7 +1362,7 @@ const doActs = async (report, opts = {}) => {
if (isEnabled) {
await selection.check({
force: true,
timeout: 2000
timeout: applyMultiplier(2000)
})
.catch(error => {
console.log(`ERROR checking ${type} (${error.message})`);
Expand All @@ -1383,7 +1383,7 @@ const doActs = async (report, opts = {}) => {
}
// Otherwise, if it is focusing the element, perform it.
else if (type === 'focus') {
await selection.focus({timeout: 2000});
await selection.focus({timeout: applyMultiplier(2000)});
act.result.success = true;
act.result.move = 'focused';
}
Expand All @@ -1402,9 +1402,9 @@ const doActs = async (report, opts = {}) => {
else {
// Click the link and wait for the resulting navigation.
try {
await selection.click({timeout: 5000});
await selection.click({timeout: applyMultiplier(5000)});
// Wait for the new content to load.
await page.waitForLoadState('domcontentloaded', {timeout: 6000});
await page.waitForLoadState('domcontentloaded', {timeout: applyMultiplier(6000)});
act.result.success = true;
act.result.move = 'clicked';
act.result.newURL = page.url();
Expand Down
4 changes: 3 additions & 1 deletion testaro/hover.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
const {getBasicResult, getVisibleCountChange} = require('../procs/testaro');
// Module to perform Playwright operations.
const playwright = require('playwright');
// Shared configuration for timeout multiplier.
const {applyMultiplier} = require('../procs/config');

// FUNCTIONS

Expand Down Expand Up @@ -71,7 +73,7 @@ exports.reporter = async (page, withItems) => {
const elementCount0 = await loc0.count();
try {
// Hover over the element.
await loc.hover({timeout: 400});
await loc.hover({timeout: applyMultiplier(400)});
// Get the change in the count of the visible elements in the observation tree.
const changeData = await getVisibleCountChange(rootLoc, elementCount0, 400, 75);
const {change, elapsedTime} = changeData;
Expand Down
9 changes: 7 additions & 2 deletions testaro/tabNav.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,11 @@
This test reports nonstandard keyboard navigation among tab elements in visible tab lists. Standards are based on https://www.w3.org/TR/wai-aria-practices-1.1/#tabpanel.
*/

// IMPORTS

// Shared configuration for timeout multiplier.
const {applyMultiplier} = require('../procs/config');

// CONSTANTS

const data = {};
Expand Down Expand Up @@ -111,7 +116,7 @@ const testKey = async (
let pressed = true;
// Click the tab element, to make the focus on it effective.
await tabElement.click({
timeout: 500
timeout: applyMultiplier(500)
})
.catch(async error => {
console.log(
Expand All @@ -135,7 +140,7 @@ const testKey = async (
if (pressed) {
// Refocus the tab element and press the specified key (page.keyboard.press may fail).
await tabElement.press(keyName, {
timeout: 1000
timeout: applyMultiplier(1000)
})
.catch(error => {
console.log(`ERROR: could not press ${keyName} (${error.message})`);
Expand Down
3 changes: 2 additions & 1 deletion tests/alfa.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ const {cap, tidy} = require('../procs/job');
const {getIdentifiers} = require('../procs/standardize');
const {getNormalizedXPath} = require('../procs/identify');
const {Playwright} = require('@siteimprove/alfa-playwright');
const {applyMultiplier} = require('../procs/config');

// FUNCTIONS

Expand Down Expand Up @@ -57,7 +58,7 @@ exports.reporter = async (page, report, actIndex) => {
}
try {
// Wait for a stable page to make the page and its alfa version consistent.
await page.waitForLoadState('networkidle', {timeout: 2000});
await page.waitForLoadState('networkidle', {timeout: applyMultiplier(6000)});
const doc = await page.evaluateHandle('document');
const alfaPage = await Playwright.toPage(doc);
// Test the page content with the specified rules.
Expand Down
4 changes: 3 additions & 1 deletion tests/aslint.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ const fs = require('fs/promises');
const {getElementData} = require('../procs/getElementData');
// Function to normalize an XPath.
const {getNormalizedXPath} = require('../procs/identify');
// Shared configuration for timeout multiplier.
const {applyMultiplier} = require('../procs/config');

// CONSTANTS

Expand Down Expand Up @@ -194,7 +196,7 @@ exports.reporter = async (page, report, actIndex) => {
// Wait for the test results to be attached to the page.
const waitOptions = {
state: 'attached',
timeout: 20000
timeout: applyMultiplier(20000)
};
await reportLoc.waitFor(waitOptions);
}
Expand Down
3 changes: 2 additions & 1 deletion tests/testaro.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@

// Function to launch a browser.
const {launch} = require('../run');
// Shared configuration for timeout multiplier.
const {timeoutMultiplier} = require('../procs/config');

// CONSTANTS

Expand Down Expand Up @@ -401,7 +403,6 @@ const allRules = [
defaultOn: false
}
];
const timeoutMultiplier = Number.parseFloat(process.env.TIMEOUT_MULTIPLIER) || 1;

// ERROR HANDLER
process.on('unhandledRejection', reason => {
Expand Down