176 lines
5.5 KiB
TypeScript
176 lines
5.5 KiB
TypeScript
import { resolve } from "@std/path";
|
|
import type { RouteScenario, Scenario } from "../../../../tools/web-ux/src/types.ts";
|
|
const root = resolve(import.meta.dirname!, "../../../..");
|
|
const selector = (selector: string) => ({ kind: "selector" as const, selector });
|
|
const click = (selector: string) => ({ action: "click" as const, selector });
|
|
const press = (selector: string, key: string) => ({ action: "press" as const, selector, key });
|
|
const route = (
|
|
id: string,
|
|
path: string,
|
|
ready: string,
|
|
dataState: string,
|
|
points = [{ id: "initial", label: "Initial state" }],
|
|
): RouteScenario => ({
|
|
id,
|
|
label: id,
|
|
path: `${path}?fixture=${id}`,
|
|
goal: "Verify production shell UI projection, not server authorization.",
|
|
dataState,
|
|
ready: selector(ready),
|
|
capturePoints: points,
|
|
});
|
|
const routes: RouteScenario[] = [
|
|
...["on", "off"].map((policy) =>
|
|
route(
|
|
`admin-${policy}`,
|
|
"/hosting/settings",
|
|
'button:has-text("Edit registration policy")',
|
|
`Synthetic admin; policy ${policy}.`,
|
|
[
|
|
{ id: "initial", label: "Loaded hosting-wide policy" },
|
|
{
|
|
id: "edit",
|
|
label: "Keyboard edit focus",
|
|
interaction: [press('button:has-text("Edit registration policy")', "Enter")],
|
|
ready: selector('input[type="checkbox"]:focus'),
|
|
},
|
|
{
|
|
id: "saved",
|
|
label: "Confirmed save",
|
|
interaction: [
|
|
press('input[type="checkbox"]', "Space"),
|
|
click('button:has-text("Save settings")'),
|
|
],
|
|
ready: selector('[role="status"]:has-text("Hosting settings saved.")'),
|
|
},
|
|
] as RouteScenario["capturePoints"],
|
|
)
|
|
),
|
|
route(
|
|
"ordinary",
|
|
"/hosting/settings",
|
|
'p:has-text("Hosting administrator access is required.")',
|
|
"Synthetic authenticated ordinary user, no admin controls.",
|
|
),
|
|
route(
|
|
"anonymous",
|
|
"/hosting/settings",
|
|
'a:has-text("Open Account")',
|
|
"Synthetic anonymous visitor; sign-in guidance, no controls.",
|
|
),
|
|
route(
|
|
"admin-loading",
|
|
"/hosting/settings",
|
|
'[role="status"]:has-text("Loading hosting settings")',
|
|
"Settings GET intentionally held pending; shell remains usable.",
|
|
),
|
|
route(
|
|
"admin-load-error",
|
|
"/hosting/settings",
|
|
'button:has-text("Retry loading settings")',
|
|
"Intentional HTTP503; expected console resource error and visible alert.",
|
|
),
|
|
...["saving", "save-error"].map((state) =>
|
|
route(
|
|
`admin-${state}`,
|
|
"/hosting/settings",
|
|
'button:has-text("Edit registration policy")',
|
|
state === "saving"
|
|
? "PUT held pending; last loaded policy retained, edit controls disabled."
|
|
: "Intentional PUT HTTP503; outcome unknown, retry blocked until reload.",
|
|
[
|
|
{
|
|
id: state,
|
|
label: state,
|
|
interaction: [
|
|
click('button:has-text("Edit registration policy")'),
|
|
press('input[type="checkbox"]', "Space"),
|
|
click('button:has-text("Save settings")'),
|
|
],
|
|
ready: selector(state === "saving" ? 'button:has-text("Saving…")' : '[role="alert"]'),
|
|
},
|
|
] as RouteScenario["capturePoints"],
|
|
)
|
|
),
|
|
route(
|
|
"account-on",
|
|
"/account",
|
|
'button:has-text("Register new account"):enabled',
|
|
"Anonymous policy on; registration and login available.",
|
|
),
|
|
route(
|
|
"account-off",
|
|
"/account",
|
|
'p:has-text("New account registration is disabled.")',
|
|
"Anonymous policy off; no registration action, login available.",
|
|
),
|
|
route(
|
|
"admin-account",
|
|
"/account",
|
|
'button:has-text("Add passkey to alice")',
|
|
"Authenticated admin; own passkey addition and global settings link.",
|
|
),
|
|
route(
|
|
"ordinary-account-off",
|
|
"/account",
|
|
'button:has-text("Add passkey to alice")',
|
|
"Authenticated ordinary user with policy off; own passkey addition available without admin nav.",
|
|
),
|
|
route(
|
|
"account-error",
|
|
"/account",
|
|
'button:has-text("Retry registration availability")',
|
|
"Intentional policy HTTP503; expected console resource error and visible alert; login retained.",
|
|
),
|
|
route(
|
|
"account-loading",
|
|
"/account",
|
|
'[role="status"]:has-text("Checking registration availability")',
|
|
"Policy request held pending; login remains available after session loads.",
|
|
),
|
|
];
|
|
for (const account of routes.filter((item) => item.path.startsWith("/account"))) {
|
|
account.capturePoints[0].fullPage = true;
|
|
}
|
|
for (const theme of ["light", "dark"] as const) {
|
|
const scenario: Scenario = {
|
|
schemaVersion: 1,
|
|
id: `hosting-settings-${theme}`,
|
|
title: "Hosting settings: synthetic UI conditions on production shell",
|
|
baseUrl: "http://127.0.0.1:15378",
|
|
colorScheme: theme,
|
|
reducedMotion: "reduce",
|
|
personas: [{
|
|
id: "fixture",
|
|
label: "Synthetic API projection; no real authentication",
|
|
auth: { kind: "anonymous" },
|
|
}],
|
|
viewports: [{ label: "desktop", width: 1440, height: 900 }, {
|
|
label: "narrow",
|
|
width: 768,
|
|
height: 900,
|
|
}, { label: "mobile", width: 390, height: 844 }],
|
|
routes,
|
|
processes: [{
|
|
id: "hosting-ui-fixture",
|
|
command: "deno",
|
|
cwd: "../../..",
|
|
args: [
|
|
"run",
|
|
"--config",
|
|
"tools/web-ux/deno.json",
|
|
"--allow-read",
|
|
"--allow-net",
|
|
"web/workspace/test/hosting-browser/fixture-server.ts",
|
|
"15378",
|
|
"web/workspace/build",
|
|
],
|
|
readyUrl: "http://127.0.0.1:15378/health",
|
|
}],
|
|
};
|
|
await Deno.writeTextFile(
|
|
`${root}/tools/web-ux/scenarios/hosting-settings-${theme}.json`,
|
|
JSON.stringify(scenario, null, 2) + "\n",
|
|
);
|
|
}
|