mirror of
https://github.com/kevinveenbirkenbach/computer-playbook.git
synced 2026-11-06 10:59:59 +00:00
Sixteen role specs decided something immediately after an action that had not settled, because locator.isVisible() discards the timeout it is handed. Three shapes: an assertion that could never fire, a login outcome read off the DOM before the app re-rendered, and a branch that selected a locator by whether an iframe had mounted yet. The ones that could never fire are the worst of them. taiga test-seaweedfs polled for a rejection toast right after saveAction.click(), so a real upload rejection was swallowed and misreported as a SeaweedFS storage failure; friendica ldapauth decided whether to run its wrong-password test at the instant a fresh context finished goto, so a slow render skipped a security check silently. wordpress test-discourse-roundtrip picked titleBox by whether the Gutenberg iframe had mounted and, when it had not, bound the wrong one and then burned the full budget failing with a misleading message. Every conversion keeps the control flow: the .catch(() => false) fallback stays, so a waitFor that throws still yields the boolean the caller expects. Twenty of the twenty-two waits use base 2000, one 3000, one 5000. resolveTimeout multiplies by five on an onion target, so the common case is ten seconds there. Across thirteen onion traces the largest wait of any kind that ever succeeded was 790 ms, so ten seconds is a twelvefold margin; the bases started higher and were pulled down because a swallowed deadline is a duration in disguise - where the optional element is normally absent, the full timeout is paid on every run, including green ones. Not exercised: no spec was run. Three of these make a test stricter, so a job that turns red on them is surfacing a real failure that was previously hidden, not regressing. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
106 lines
4.5 KiB
JavaScript
106 lines
4.5 KiB
JavaScript
const { test, expect } = require("@playwright/test");
|
|
const { resolveTimeout } = require("../timeouts");
|
|
const { skipUnlessAddonEnabled } = require("../addon-gating");
|
|
const shared = require("../_shared");
|
|
const { gotoOnion } = require("../personas");
|
|
|
|
test("integration integration_matrix: per-user login drives a real session against the partner homeserver", async ({ browser }) => {
|
|
skipUnlessAddonEnabled("integration_matrix");
|
|
test.setTimeout(resolveTimeout(120_000));
|
|
|
|
const context = await browser.newContext({ ignoreHTTPSErrors: true });
|
|
const page = await context.newPage();
|
|
|
|
try {
|
|
await shared.loginToStandaloneNextcloud(page);
|
|
|
|
await gotoOnion(page,
|
|
new URL("settings/admin/connected-accounts", shared.env.nextcloudBaseUrl).toString(),
|
|
{ waitUntil: "domcontentloaded", timeout: resolveTimeout(60_000) }
|
|
);
|
|
await shared.dismissBlockingNextcloudModals(page, page);
|
|
|
|
const adminPanel = page.locator("#matrix_prefs, #matrix-content").first();
|
|
await expect(
|
|
adminPanel,
|
|
"the Matrix integration admin panel must render when integration_matrix is enabled — its absence means the app failed to enable/configure against the partner homeserver"
|
|
).toBeVisible({ timeout: resolveTimeout(30_000) });
|
|
|
|
let configuredInstanceUrl = null;
|
|
const adminInputs = adminPanel.locator("input[type='text'], input[type='url'], input:not([type])");
|
|
const adminCount = await adminInputs.count();
|
|
for (let i = 0; i < adminCount; i += 1) {
|
|
const value = (await adminInputs.nth(i).inputValue().catch(() => "")) || "";
|
|
if (/^https?:\/\//i.test(value.trim())) {
|
|
configuredInstanceUrl = value.trim();
|
|
break;
|
|
}
|
|
}
|
|
expect(
|
|
configuredInstanceUrl,
|
|
"the Matrix admin homeserver field must be populated with the partner URL"
|
|
).toBeTruthy();
|
|
|
|
const partnerHost = new URL(configuredInstanceUrl).host;
|
|
const nextcloudHost = new URL(shared.env.nextcloudBaseUrl).host;
|
|
expect(partnerHost, "the homeserver must be the partner, not Nextcloud").not.toBe(nextcloudHost);
|
|
|
|
const partnerHits = [];
|
|
page.on("request", (req) => {
|
|
try {
|
|
const h = new URL(req.url()).host;
|
|
if (h === partnerHost && /\/_matrix\/client\//.test(req.url())) {
|
|
partnerHits.push(req.url());
|
|
}
|
|
} catch {}
|
|
});
|
|
|
|
await gotoOnion(page,
|
|
new URL("settings/user/connected-accounts", shared.env.nextcloudBaseUrl).toString(),
|
|
{ waitUntil: "domcontentloaded", timeout: resolveTimeout(60_000) }
|
|
);
|
|
await shared.dismissBlockingNextcloudModals(page, page);
|
|
|
|
const userPanel = page.locator("#matrix_prefs, #matrix-content").first();
|
|
await expect(
|
|
userPanel,
|
|
"the personal Matrix panel must render so a user can link their Matrix account"
|
|
).toBeVisible({ timeout: resolveTimeout(30_000) });
|
|
|
|
const loginField = userPanel
|
|
.locator("input[type='text']:not([readonly]), input:not([type]):not([readonly])")
|
|
.first();
|
|
const passwordField = userPanel.locator("input[type='password']").first();
|
|
const connectButton = userPanel
|
|
.getByRole("button", { name: /connect|log ?in|sign ?in/i })
|
|
.first();
|
|
|
|
const connectResponsePromise = page
|
|
.waitForResponse((resp) => /\/apps\/integration_matrix\//.test(resp.url()), { timeout: resolveTimeout(30_000) })
|
|
.catch(() => null);
|
|
|
|
if (await passwordField.waitFor({ state: "visible", timeout: resolveTimeout(2_000) }).then(() => true).catch(() => false)) {
|
|
await loginField.fill(shared.env.loginUsername || "admin").catch(() => {});
|
|
await passwordField.fill(shared.env.loginPassword || "").catch(() => {});
|
|
await connectButton.click({ timeout: resolveTimeout(10_000) }).catch(() => {});
|
|
} else if (await connectButton.waitFor({ state: "visible", timeout: resolveTimeout(2_000) }).then(() => true).catch(() => false)) {
|
|
await connectButton.click({ timeout: resolveTimeout(10_000) }).catch(() => {});
|
|
}
|
|
|
|
const connectResponse = await connectResponsePromise;
|
|
await page.waitForTimeout(resolveTimeout(2_500));
|
|
|
|
const reachedPartner = partnerHits.length > 0;
|
|
const droveServerSide = Boolean(connectResponse);
|
|
expect(
|
|
reachedPartner || droveServerSide,
|
|
"the per-user connect must drive a real login against the partner Matrix homeserver " +
|
|
"(a browser C-S API call to the partner, or a nextcloud integration_matrix connect round-trip), " +
|
|
"not merely render a configured URL"
|
|
).toBe(true);
|
|
} finally {
|
|
await page.close().catch(() => {});
|
|
await context.close().catch(() => {});
|
|
}
|
|
});
|