mirror of
https://github.com/kevinveenbirkenbach/computer-playbook.git
synced 2026-11-06 10:59:59 +00:00
Playwright declares the timeout option on locator.isVisible() and isHidden() as deprecated and ignored: both return immediately and never wait. Passing resolveTimeout(10_000) to one therefore changes nothing at runtime and only tells the reader a wait happened that never did. That reading is what let7538b8a0bconclude two genuine call sites were fine, and what hid the false negativeb97cf8888had to fix. Sixty call sites across fifty-eight role-local specs lose the argument and nothing else. Every added line in this commit is a bare isVisible(); a grep for added lines that are not one returns zero, so a behaviour change cannot have entered here. Files whose diff also alters waiting semantics are deliberately not part of this commit. A repository lint added inb97cf8888keeps the construct out. It matches isVisible and isHidden only, because isChecked, isDisabled, isEditable and isEnabled take a real timeout. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
88 lines
5.0 KiB
JavaScript
88 lines
5.0 KiB
JavaScript
const { test, expect } = require("@playwright/test");
|
|
const { resolveTimeout } = require("./timeouts");
|
|
const { decodeDotenvQuotedValue, normalizeBaseUrl, gotoOnion } = require("./personas");
|
|
const { isServiceEnabled } = require("./service-gating");
|
|
|
|
const lamEnabled = isServiceEnabled("lam");
|
|
const lamOauth2Fronted = String(process.env.LAM_OAUTH2_FRONTED || "").toLowerCase() === "true";
|
|
const lamBaseUrl = normalizeBaseUrl(process.env.LAM_BASE_URL || "");
|
|
const lamPassword = decodeDotenvQuotedValue(process.env.LAM_PASSWORD);
|
|
const ldapAdminPassword = decodeDotenvQuotedValue(process.env.LDAP_ADMIN_PASSWORD);
|
|
const lamLoginPassword = ldapAdminPassword || lamPassword;
|
|
|
|
exports.register = function (shared) {
|
|
test.describe("keycloak → ldap write-through, verified via LAM", () => {
|
|
test.skip(!shared.env.ssoEnabled, "SSO shared service disabled");
|
|
test.skip(!lamEnabled, "LAM not deployed (LAM_SERVICE_ENABLED=false)");
|
|
|
|
test("middleName edited in Keycloak appears in LDAP via LAM", async ({ page, context }) => {
|
|
expect(shared.env.oidcIssuerUrl, "OIDC_ISSUER_URL must be set in env").toBeTruthy();
|
|
expect(shared.env.oidcClientId, "OIDC_CLIENT_ID must be set in env").toBeTruthy();
|
|
expect(lamBaseUrl, "LAM_BASE_URL must be set in env").toBeTruthy();
|
|
|
|
const probe = `LAM-${Date.now()}`;
|
|
|
|
await gotoOnion(page, `${shared.env.oidcIssuerUrl}/.well-known/openid-configuration`);
|
|
const restResult = await page.evaluate(shared.setMiddleNameViaAccountRest, {
|
|
issuer: shared.env.oidcIssuerUrl,
|
|
clientId: shared.env.oidcClientId,
|
|
username: shared.env.biberUsername,
|
|
password: shared.env.biberPassword,
|
|
middleName: probe,
|
|
withRestore: false,
|
|
});
|
|
|
|
expect(
|
|
restResult.stage,
|
|
`Keycloak write must succeed: stage=${restResult.stage} status=${restResult.status}`
|
|
).toBe("ok");
|
|
|
|
const lamPage = await context.newPage();
|
|
await lamPage.goto(`${lamBaseUrl}/lam/templates/login.php`, { waitUntil: "load" });
|
|
|
|
if (lamOauth2Fronted) {
|
|
const kcUsername = lamPage.locator("input[name='username'], input#username").first();
|
|
await expect(kcUsername, "Keycloak login form must render for OAuth2-fronted LAM").toBeVisible({ timeout: resolveTimeout(30_000) });
|
|
await kcUsername.fill(shared.env.adminUsername);
|
|
await lamPage.locator("input[name='password'], input#password").first().fill(shared.env.adminPassword);
|
|
await lamPage.locator("button[type='submit'], input[name='login'], input[type='submit']").first().click({ timeout: resolveTimeout(30_000) });
|
|
await lamPage.waitForLoadState("networkidle");
|
|
|
|
const lamPwAfterSso = lamPage.locator("input[name='passwd'], input[name='password'], input#passwd").first();
|
|
if (lamLoginPassword && await lamPwAfterSso.isVisible().catch(() => false)) {
|
|
await lamPwAfterSso.fill(lamLoginPassword);
|
|
await lamPage.locator("button[type='submit'], input[type='submit']").first().click({ timeout: resolveTimeout(30_000) });
|
|
await lamPage.waitForLoadState("networkidle");
|
|
}
|
|
} else {
|
|
expect(lamLoginPassword, "LDAP_ADMIN_PASSWORD or LAM_PASSWORD must be set when LAM is not OAuth2-fronted").toBeTruthy();
|
|
const lamPwInput = lamPage.locator("input[name='passwd'], input[name='password'], input#passwd").first();
|
|
await expect(lamPwInput, "LAM native login form must render").toBeVisible({ timeout: resolveTimeout(30_000) });
|
|
await lamPwInput.fill(lamLoginPassword);
|
|
await lamPage.locator("button[type='submit'], input[type='submit']").first().click({ timeout: resolveTimeout(30_000) });
|
|
await lamPage.waitForLoadState("networkidle");
|
|
}
|
|
|
|
await lamPage.goto(`${lamBaseUrl}/lam/templates/lists/list.php?type=user`, { waitUntil: "load" });
|
|
const filter = lamPage.locator("input[type='text'][name='filter_uid'], input[type='text'][name^='filter_']").first();
|
|
if (await filter.isVisible().catch(() => false)) {
|
|
await filter.fill(shared.env.biberUsername);
|
|
await filter.press("Enter");
|
|
await lamPage.waitForLoadState("networkidle");
|
|
}
|
|
const biberRow = lamPage.locator(`tr:has(td:text-is("${shared.env.biberUsername}"))`).first();
|
|
await expect(biberRow, "biber must appear in LAM user list").toBeVisible({ timeout: resolveTimeout(30_000) });
|
|
const biberEdit = biberRow.locator(`a[href*="edit.php"][href*="${shared.env.biberUsername}"]`).first();
|
|
await expect(biberEdit, "biber row must expose an Edit link").toBeVisible({ timeout: resolveTimeout(10_000) });
|
|
await biberEdit.click({ timeout: resolveTimeout(30_000) });
|
|
await lamPage.waitForLoadState("networkidle");
|
|
|
|
const initialsField = lamPage.locator("input[name='initials']").first();
|
|
await expect(
|
|
initialsField,
|
|
`LAM-rendered LDAP entry for ${shared.env.biberUsername} must contain probe "${probe}" in its initials field`
|
|
).toHaveValue(probe, { timeout: resolveTimeout(30_000) });
|
|
});
|
|
});
|
|
};
|