Files
computer-playbook/roles/web-app-docs/files/playwright/_shared.js
T
kevinveenbirkenbachandClaude Opus 5 6fbc92bac8 fix(web-app-docs): let the poll report before its own test times out
The docs suite of run 36745666606 ends on

    Timed out waiting 3540s for the test suite to run
    6 did not run

and says nothing about what it waited for. `test-languages.js:15` sets
`test.setTimeout(resolveTimeout(5_400_000))` and `_shared.js:40` capped the poll
with the same `resolveTimeout`, so both land on `budget - SUITE_RESERVE_MS` and
expire together. Playwright's test timeout wins the race, and the poll's own
message, "Expected the German site of the deployed working tree to be built",
is never printed.

`pollStatus` now keeps 60 s of its budget back, with a 30 s floor so a small
budget cannot go negative. The reserve sits in the helper rather than at its
three call sites, the same shape `SUITE_RESERVE_MS` uses in
`test-e2e-playwright/files/timeouts.js` to stop one test from consuming the
whole suite.

This does not make the cluster pass. The `deployed` build takes longer than the
suite budget allows, so the job stays red; it stops being red for an unstated
reason. Curing it means building that version before the suite runs or making
the build fit, which is role surgery and not this change.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-30 22:55:12 +02:00

65 lines
1.9 KiB
JavaScript

const { expect } = require("@playwright/test");
const { resolveTimeout } = require("./timeouts");
const {
normalizeBaseUrl,
requireDotenvValue,
} = require("./personas");
const appBaseUrl = normalizeBaseUrl(requireDotenvValue(process.env.APP_BASE_URL, "APP_BASE_URL"));
const canonicalDomain = requireDotenvValue(process.env.CANONICAL_DOMAIN, "CANONICAL_DOMAIN");
const RELEASE_TAG = /^v\d+\.\d+\.\d+$/;
async function fetchVersions(request) {
const response = await request.get(`${appBaseUrl}/api/versions`, { timeout: resolveTimeout(30_000) });
expect(response.status(), "Expected the versions api to answer").toBe(200);
return response.json();
}
async function versionState(request, name) {
const version = (await fetchVersions(request)).find((candidate) => candidate.name === name);
return version ? version.state : "unknown";
}
let lastTransportError = null;
async function statusOf(request, url) {
try {
const response = await request.get(url, { failOnStatusCode: false, maxRedirects: 0, timeout: resolveTimeout(30_000) });
lastTransportError = null;
return response.status();
} catch (error) {
lastTransportError = error;
return 0;
}
}
const POLL_REPORT_RESERVE_MS = 60_000;
async function pollStatus(request, url, expected, message, timeout) {
lastTransportError = null;
const budget = Math.max(
30_000,
resolveTimeout(timeout) - POLL_REPORT_RESERVE_MS,
);
try {
await expect
.poll(() => statusOf(request, url), { message, timeout: budget, intervals: [30_000] })
.toBe(expected);
} catch (failure) {
if (!lastTransportError) throw failure;
throw new Error(
`${failure.message}\nlast transport error reaching ${url}: ${lastTransportError.message}`,
{ cause: failure },
);
}
}
module.exports = {
appBaseUrl,
canonicalDomain,
RELEASE_TAG,
fetchVersions,
versionState,
pollStatus,
};