mirror of
https://github.com/kevinveenbirkenbach/computer-playbook.git
synced 2026-11-09 20:26:33 +00:00
fix(test-e2e-playwright): stop the OIDC login helper waiting out probes that cannot match
Every dashboard persona login spent 25s times the onion multiplier in clickOidcLoginLink before it clicked anything. The markup probe matched a[href*='openid-connect'], and the dashboard header renders the Account dropdown's hidden Logout item, whose Keycloak end-session URL contains the same string, before its Login link. .first() pinned the hidden item until the 5s probe expired; the exact-name probe then missed for 20s because Playwright counts the Font Awesome ::before glyph in the link's accessible name. The probe now skips logout hrefs and resolves to the Login anchor the loose probe used to click. The adapter readiness wait cost every other role 15s times the multiplier, because only the dashboard sets __oidcLoginReady. The helper now waits for the load event and then only while the flag is false. The dashboard loader, an inline head script, declares it false before the Login link renders; oidc.js still sets it true once the click handler is wired. Traces of run 34421944328 show the two probes expiring at 5.07s + 20.00s on clearnet and 25.01s + 100.00s over Tor. Across runs 34421944328 and 34419507038 the Tor dashboard persona test took 150-156s in all 102 compose runs against 32-34s in the 92 clearnet ones. Replayed on the dashboard HTML those traces captured, the helper reaches the Keycloak authorize endpoint in 0.08s instead of 40.09s, and still waits for the adapter (1.52s) when the flag flips to true after 1.5s. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
60b9006506
commit
0b79afdc0c
2 files changed
+23
-10
No files matched your search
@@ -31,12 +31,25 @@ const { resolveTimeout } = require("../../timeouts");
|
||||
// SPOT for the role-side OIDC adapter readiness contract. A role whose
|
||||
// `templates/javascript/oidc.js.j2` wraps its Login link in a JS click
|
||||
// handler (e.g. `keycloak.login()` with PKCE) MUST set this flag on
|
||||
// `window` after the click interceptor is wired, so persona helpers can
|
||||
// click the link without racing the adapter.
|
||||
// `window` to `false` before the Login link renders and to `true` after
|
||||
// the click interceptor is wired, so persona helpers can click the link
|
||||
// without racing the adapter.
|
||||
const OIDC_LOGIN_READY_FLAG = "__oidcLoginReady";
|
||||
|
||||
const OIDC_TRIGGER_NAME = /sso|openid|single[\s-]?sign/i;
|
||||
|
||||
// Keycloak's end-session URL also contains `openid-connect`; a page that
|
||||
// renders a hidden Logout item before its Login link would otherwise pin
|
||||
// `.first()` to the hidden Logout item until the probe times out.
|
||||
const OIDC_ENTRY_SELECTOR = [
|
||||
"a[href*='openid_connect' i]:not([href*='logout' i])",
|
||||
"a[href*='openid-connect' i]:not([href*='logout' i])",
|
||||
"a[href*='/auth/auth/' i]:not([href*='logout' i])",
|
||||
"form[action*='openid' i] button",
|
||||
"a[data-testid*='oidc' i]",
|
||||
"button[data-testid*='oidc' i]",
|
||||
].join(", ");
|
||||
|
||||
async function performKeycloakLoginForm(target, username, password) {
|
||||
const usernameField = target
|
||||
.getByRole("textbox", { name: /username|email/i })
|
||||
@@ -73,8 +86,8 @@ async function performKeycloakLogin(page, username, password, canonicalDomain) {
|
||||
// the role's adapter to signal readiness (OIDC_LOGIN_READY_FLAG) before
|
||||
// clicking, so the click hits the JS-wrapped handler (which stores
|
||||
// PKCE state) and not the raw `href` (which would skip PKCE and break
|
||||
// the post-login token exchange on PKCE-enforced clients). The 15s
|
||||
// fallback covers roles whose Login link is purely static. Returns the
|
||||
// the post-login token exchange on PKCE-enforced clients). Roles that
|
||||
// never declare the flag only wait for the page's load event. Returns the
|
||||
// Page that reached `openid-connect/auth` — the opener, or the popup for
|
||||
// roles that hand the IdP off via `window.open` — else null.
|
||||
//
|
||||
@@ -88,11 +101,7 @@ async function performKeycloakLogin(page, username, password, canonicalDomain) {
|
||||
// flow instead of the role's own auth chain.
|
||||
async function clickOidcLoginLink(page, strictLink, looseLink) {
|
||||
for (let attempt = 0; attempt < 2; attempt += 1) {
|
||||
let oidcLink = page
|
||||
.locator(
|
||||
"a[href*='openid_connect' i], a[href*='openid-connect' i], a[href*='/auth/auth/' i], form[action*='openid' i] button, a[data-testid*='oidc' i], button[data-testid*='oidc' i]",
|
||||
)
|
||||
.first();
|
||||
let oidcLink = page.locator(OIDC_ENTRY_SELECTOR).first();
|
||||
if (attempt > 0) {
|
||||
oidcLink = oidcLink
|
||||
.or(page.getByRole("button", { name: OIDC_TRIGGER_NAME }))
|
||||
@@ -120,9 +129,10 @@ async function clickOidcLoginLink(page, strictLink, looseLink) {
|
||||
}
|
||||
}
|
||||
|
||||
await page.waitForLoadState("load", { timeout: resolveTimeout(15_000) }).catch(() => {});
|
||||
await page
|
||||
.waitForFunction(
|
||||
(flag) => window[flag] === true,
|
||||
(flag) => window[flag] !== false,
|
||||
OIDC_LOGIN_READY_FLAG,
|
||||
{ timeout: resolveTimeout(15_000) },
|
||||
)
|
||||
|
||||
@@ -11,6 +11,9 @@
|
||||
];
|
||||
// Cache buster (highest mtime computed during deploy)
|
||||
const VERSION = "{{ javascript_file_version }}";
|
||||
{% if 'oidc.js' in DASHBOARD_JS_FILES %}
|
||||
window.__oidcLoginReady = false;
|
||||
{% endif %}
|
||||
|
||||
// 2) Helper to load a <script> with proper query param
|
||||
function loadScriptSequential(url) {
|
||||
|
||||
Reference in new issue
Block a user