e2e: Playwright suite for homepage + guard chain; RBAC gate on /admin; fix stale service links
This commit is contained in:
1 parent
e513007aa4
commit
dfd9c8d1c0
18 files changed
+497
-6
No files matched your search
@@ -0,0 +1,39 @@
|
||||
import { readFileSync } from "node:fs";
|
||||
|
||||
// Parses tests/e2e/.env.local (git-ignored) into a plain record. Returns {}
|
||||
// when the file is absent so config load never crashes before a useful error.
|
||||
export function loadEnvFile(): Record<string, string> {
|
||||
try {
|
||||
const raw = readFileSync(new URL("../.env.local", import.meta.url), "utf8");
|
||||
const out: Record<string, string> = {};
|
||||
for (const line of raw.split("\n")) {
|
||||
const m = line.match(/^\s*([A-Z0-9_]+)\s*=\s*(.*)\s*$/);
|
||||
if (m) out[m[1]] = m[2];
|
||||
}
|
||||
return out;
|
||||
} catch {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
const env = loadEnvFile();
|
||||
|
||||
export const BASE_URL = process.env.E2E_BASE_URL || env.E2E_BASE_URL || "https://www.franksdank.cloud";
|
||||
|
||||
// Anubis challenges obvious headless UAs; present as a stock Chrome.
|
||||
export const REAL_UA =
|
||||
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36";
|
||||
|
||||
function credential(name: string): string {
|
||||
const v = process.env[name] || env[name];
|
||||
if (!v) throw new Error(`missing ${name} — set it in tests/e2e/.env.local or the environment`);
|
||||
return v;
|
||||
}
|
||||
|
||||
export function standardCredentials(): { user: string; pass: string } {
|
||||
return { user: credential("E2E_STANDARD_USER"), pass: credential("E2E_STANDARD_PASS") };
|
||||
}
|
||||
|
||||
export function adminCredentials(): { user: string; pass: string } {
|
||||
return { user: credential("E2E_ADMIN_USER"), pass: credential("E2E_ADMIN_PASS") };
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
import { expect, type Page } from "@playwright/test";
|
||||
import { REAL_UA } from "./env";
|
||||
|
||||
// Authentik's default login flow is two stages (identification -> password),
|
||||
// both rendered inside open shadow roots. Playwright's CSS engine pierces
|
||||
// open shadow roots, so plain input[name=...] locators work. Each login runs
|
||||
// in a FRESH browser context: reusing a half-finished flow poisons authentik's
|
||||
// pending_user stage and the second login hangs (see the
|
||||
// authentik-flow-ui-automation skill). Anubis sits in front and solves its
|
||||
// proof-of-work automatically in a real Chromium, which is why the
|
||||
// uidField wait can legitimately take a while on a cold challenge.
|
||||
export async function akLogin(page: Page, baseUrl: string, user: string, pass: string): Promise<void> {
|
||||
await page.goto(baseUrl + "/");
|
||||
const uid = page.locator('input[name="uidField"]');
|
||||
await uid.waitFor({ state: "visible", timeout: 90_000 });
|
||||
await uid.fill(user);
|
||||
await page.locator('button[type="submit"]').first().click();
|
||||
|
||||
const pwd = page.locator('input[name="password"]');
|
||||
await pwd.waitFor({ state: "visible", timeout: 30_000 });
|
||||
await pwd.fill(pass);
|
||||
await page.locator('button[type="submit"]').first().click();
|
||||
|
||||
// Back on the homepage with panels rendered.
|
||||
await page.waitForURL(/:\/\/www\.franksdank\.cloud\//, { timeout: 60_000 });
|
||||
await expect(page.locator("#grid .panel").first()).toBeVisible({ timeout: 30_000 });
|
||||
}
|
||||
|
||||
export function launchOptions() {
|
||||
return {
|
||||
userAgent: REAL_UA,
|
||||
ignoreHTTPSErrors: true,
|
||||
viewport: { width: 1440, height: 900 },
|
||||
};
|
||||
}
|
||||
Reference in new issue
Block a user