Files
computer-playbook/roles/web-app-jitsi/files/playwright/_shared.js
kevinveenbirkenbachandClaude Opus 4.8 4c91df5de7 test(e2e): forbid raw page.goto, migrate all specs to Tor-resilient gotoOnion
New lint test_no_raw_goto.py forbids raw `page.goto(...)` in role playwright specs + persona flows (the gotoOnion wrapper in personas/utils is exempt) so navigation retries transient Tor-transport errors over onion. Migrates every raw call across ~75 roles (343 sites) to `gotoOnion(page, ...)` and teaches gotoOnion to detect relative onion URLs (`isOnionCanonical`). gotoOnion is a behaviour-identical drop-in on clearnet (single attempt, same return). Verified: no-raw-goto lint green, eslint 0 errors, make test green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-08 20:59:38 +02:00

76 lines
2.8 KiB
JavaScript

const { expect } = require("@playwright/test");
const { resolveTimeout } = require("./timeouts");
const {
decodeDotenvQuotedValue,
gotoOnion,
normalizeBaseUrl,
runAdminFlow,
runBiberFlow,
runGuestFlow,
} = require("./personas");
const { skipUnlessServiceEnabled } = require("./service-gating");
const appBaseUrl = normalizeBaseUrl(process.env.APP_BASE_URL || "");
const canonicalDomain = decodeDotenvQuotedValue(process.env.CANONICAL_DOMAIN);
// Drive the Jitsi Meet pre-join chrome for an authenticated persona: navigate
// to a room URL and wait for the SPA's prejoin lobby to render. The spec MUST
// NOT actually start a media call (the headless Playwright runner has no
// camera permission) but reaching the prejoin lobby behind the oauth2-proxy
// gate proves the user is on the role's authenticated surface.
async function reachJitsiPrejoin(page, personaLabel, roomSuffix) {
const roomName = `e2e-${personaLabel}-${roomSuffix}`.toLowerCase().replace(/[^a-z0-9-]/g, "");
await gotoOnion(page, `${appBaseUrl}/${roomName}`, { waitUntil: "domcontentloaded" });
const prejoin = page
.getByRole("button", { name: /join meeting|join|beitreten/i })
.or(page.locator('[data-testid="prejoin.joinMeeting"], #premeeting-screen'))
.first();
await expect(prejoin, `${personaLabel}: prejoin surface must render`).toBeVisible({
timeout: resolveTimeout(60_000),
});
await expect
.poll(() => page.url(), {
timeout: resolveTimeout(30_000),
message: `${personaLabel}: URL must include the room path`,
})
.toContain(`/${roomName}`);
}
// Open the prejoin "more options" / Settings panel so the admin scenario lands
// on a surface that biber does NOT exercise. Jitsi exposes a Settings link in
// the prejoin and in-meeting toolbar; presence of the panel satisfies the
// "admin authorisation: Settings link visible in the DOM" rule from the
// per-role playwright contract.
async function openJitsiSettingsPanel(page, personaLabel) {
const settingsTrigger = page
.getByRole("button", { name: /settings|einstellungen|more options|optionen/i })
.or(page.locator('[aria-label*="settings" i], [aria-label*="einstellungen" i], [data-testid*="settings" i]'))
.first();
await expect(
settingsTrigger,
`${personaLabel}: a Settings / More-options control must be visible in the DOM`,
).toBeVisible({ timeout: resolveTimeout(30_000) });
}
async function beforeEach({ page }) {
await page.setViewportSize({ width: 1440, height: 1100 });
expect(appBaseUrl, "APP_BASE_URL must be set").toBeTruthy();
expect(canonicalDomain, "CANONICAL_DOMAIN must be set").toBeTruthy();
await page.context().clearCookies();
}
module.exports = {
env: {
appBaseUrl,
canonicalDomain,
},
reachJitsiPrejoin,
openJitsiSettingsPanel,
runGuestFlow,
runBiberFlow,
runAdminFlow,
skipUnlessServiceEnabled,
beforeEach,
};