mirror of
https://github.com/kevinveenbirkenbach/computer-playbook.git
synced 2026-11-06 10:59:59 +00:00
Over Tor the redirect chain behind a login/logout/OIDC click routinely exceeds Playwright's fixed 30s action timeout, so the click throws before the flow's scaled waits run. Sweep all role Playwright suites and the shared persona helpers: give every navigation/auth-transition click an onion-scaled timeout (.click({ timeout: resolveTimeout(30_000) })) and route the remaining raw millisecond literals (timeout options, waitForTimeout, sleep-promise setTimeout, positional wait args) through resolveTimeout. Local UI clicks (menus, toggles, modal dismissals, DOM clicks inside evaluate) stay untouched.
Two new lints keep it that way: test_no_raw_timeout_literals forbids raw numeric timeout literals in Playwright specs (opt-out: nocheck: raw-timeout), test_persona_clicks_scaled requires every click in the shared persona auth flows to carry a resolveTimeout timeout (opt-out: nocheck: unscaled-click).
Verified: node --check on all 133 changed JS files, all 13 playwright lints green, make autoformat clean, make test green (2745+196+163+4 tests OK).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
100 lines
3.8 KiB
JavaScript
100 lines
3.8 KiB
JavaScript
// Shared KIX Playwright spec state: env vars consumed by more than one
|
|
// scenario, the KIX-form login helper, the full OAuth2-proxy → Keycloak →
|
|
// KIX-LDAP login/logout flow used by both the administrator and the biber
|
|
// login tests, and the `beforeEach` env-presence guard. Per-test env
|
|
// (admin credentials, biber credentials, Keycloak admin API access, etc.)
|
|
// stays in the respective `test-*.js` file.
|
|
|
|
const { expect } = require("@playwright/test");
|
|
const { resolveTimeout } = require("./timeouts");
|
|
|
|
const { decodeDotenvQuotedValue, performKeycloakLoginForm, runBiberFlow, runGuestFlow, gotoOnion } = require("./personas");
|
|
const { isServiceEnabled, skipUnlessServiceEnabled } = require("./service-gating");
|
|
|
|
const ssoEnabled = isServiceEnabled("sso");
|
|
const ldapEnabled = isServiceEnabled("ldap");
|
|
|
|
const appBaseUrl = decodeDotenvQuotedValue(process.env.APP_BASE_URL || "").replace(/\/$/, "");
|
|
const canonicalDomain = decodeDotenvQuotedValue(process.env.CANONICAL_DOMAIN || "");
|
|
const oidcIssuerUrl = decodeDotenvQuotedValue(process.env.OIDC_ISSUER_URL || "").replace(/\/$/, "");
|
|
const logoutUrl = decodeDotenvQuotedValue(process.env.LOGOUT_URL || "").replace(/\/$/, "");
|
|
|
|
async function performKixLogin(page, username, password) {
|
|
const usernameInput = page.locator('input[type="text"], input[type="email"], input[name="UserLogin"], input[name="username"]').first();
|
|
const passwordInput = page.locator('input[type="password"]').first();
|
|
const submitButton = page.locator('button[type="submit"], input[type="submit"], button:has-text("Login")').first();
|
|
|
|
await usernameInput.waitFor({ state: "visible", timeout: resolveTimeout(30_000) });
|
|
await usernameInput.fill(username);
|
|
await passwordInput.fill(password);
|
|
await submitButton.click({ timeout: resolveTimeout(30_000) });
|
|
}
|
|
|
|
async function runKixLoginLogoutFlow(page, username, password) {
|
|
const expectedAuthUrl = `${oidcIssuerUrl}/protocol/openid-connect/auth`;
|
|
|
|
await gotoOnion(page, `${appBaseUrl}/`, { waitUntil: "domcontentloaded" });
|
|
|
|
await expect
|
|
.poll(() => page.url(), {
|
|
timeout: resolveTimeout(30_000),
|
|
message: `Expected redirect to Keycloak OIDC auth: ${expectedAuthUrl}`,
|
|
})
|
|
.toContain(expectedAuthUrl);
|
|
await performKeycloakLoginForm(page, username, password);
|
|
|
|
await expect
|
|
.poll(() => page.url(), {
|
|
timeout: resolveTimeout(60_000),
|
|
message: `Expected redirect back to canonical KIX URL on ${canonicalDomain}`,
|
|
})
|
|
.toContain(canonicalDomain);
|
|
|
|
await page.waitForLoadState("domcontentloaded");
|
|
await performKixLogin(page, username, password);
|
|
|
|
await expect
|
|
.poll(() => page.url(), {
|
|
timeout: resolveTimeout(30_000),
|
|
message: `Expected SPA to leave /auth after successful LDAP login as ${username}`,
|
|
})
|
|
.not.toContain("/auth");
|
|
|
|
await gotoOnion(page, logoutUrl, { waitUntil: "commit" }).catch(() => {});
|
|
|
|
await page.context().clearCookies();
|
|
await gotoOnion(page, `${appBaseUrl}/`, { waitUntil: "domcontentloaded" });
|
|
await expect
|
|
.poll(() => page.url(), {
|
|
timeout: resolveTimeout(30_000),
|
|
message: `Expected post-logout request to be re-gated to ${expectedAuthUrl}`,
|
|
})
|
|
.toContain(expectedAuthUrl);
|
|
}
|
|
|
|
async function beforeEach({ page }) {
|
|
expect(appBaseUrl, "APP_BASE_URL must be set").toBeTruthy();
|
|
expect(canonicalDomain, "CANONICAL_DOMAIN must be set").toBeTruthy();
|
|
expect(oidcIssuerUrl, "OIDC_ISSUER_URL must be set").toBeTruthy();
|
|
expect(logoutUrl, "LOGOUT_URL must be set").toBeTruthy();
|
|
await page.context().clearCookies();
|
|
}
|
|
|
|
module.exports = {
|
|
env: {
|
|
ssoEnabled,
|
|
ldapEnabled,
|
|
appBaseUrl,
|
|
canonicalDomain,
|
|
oidcIssuerUrl,
|
|
logoutUrl,
|
|
},
|
|
performKixLogin,
|
|
runKixLoginLogoutFlow,
|
|
beforeEach,
|
|
isServiceEnabled,
|
|
skipUnlessServiceEnabled,
|
|
runBiberFlow,
|
|
runGuestFlow,
|
|
};
|