import test from "node:test";
import assert from "node:assert/strict";
import { readFile, mkdir } from "node:fs/promises";
import { chromium } from "playwright";
async function fixture(options = {}) {
const browser = await chromium.launch({ headless: true, channel: process.env.PLAYWRIGHT_CHANNEL });
const context = await browser.newContext({ viewport: options.viewport || { width: 1280, height: 900 } });
if (options.optOut) await context.addInitScript(() => Object.defineProperty(navigator, "globalPrivacyControl", { value: true }));
const state = { verified: false, handle: null, taken: true, statusFailure: false, requests: [], events: [], resends: 0 };
await context.route("**/*", async route => {
const request = route.request(), url = new URL(request.url());
if (url.pathname === "/style.css") return route.fulfill({ contentType: "text/css", body: await readFile(new URL("../public/aesthetic.computer/style.css", import.meta.url), "utf8") });
if (url.pathname === "/api/signup-track") {
state.events.push(JSON.parse(request.postData()));
return route.fulfill({ status: 204 });
}
if (url.pathname === "/api/signup-status") {
state.requests.push(url.pathname);
assert.equal(request.headers().authorization, "Bearer fixture-token");
if (state.statusFailure) return route.fulfill({ status: 503, json: { message: "unavailable" } });
if (request.method() === "POST") { state.resends++; return route.fulfill({ json: { sent: true } }); }
return route.fulfill({ json: { verified: state.verified, handle: state.handle } });
}
if (url.pathname === "/handle" && request.method() === "POST") {
state.requests.push(JSON.parse(request.postData()));
if (state.taken) return route.fulfill({ status: 500, json: { message: "taken" } });
state.handle = JSON.parse(request.postData()).handle;
return route.fulfill({ json: { handle: state.handle } });
}
if (url.pathname.endsWith(".mjs")) {
const filename = url.pathname.split("/").at(-1);
assert.ok(["signup-flow.mjs", "signup-model.mjs", "visit-model.mjs", "text.mjs"].includes(filename));
return route.fulfill({ contentType: "text/javascript", body: await readFile(new URL(`../public/aesthetic.computer/lib/${filename}`, import.meta.url), "utf8") });
}
return route.fulfill({ contentType: "text/html", body: `AC test piece
` });
});
const page = await context.newPage();
await page.goto("https://aesthetic.computer/laer-klokken");
await page.waitForFunction(() => window.ready);
return { browser, context, page, state };
}
test("signup survives the redirect, verifies email, handles a taken name and returns to the original piece", async () => {
const f = await fixture();
try {
const { page, state } = f;
await page.evaluate(() => { acSignup.remember(location.href); history.pushState({}, '', '/prompt'); });
await page.getByRole("button", { name: "I'm new" }).click();
await page.goto("https://aesthetic.computer/?code=private-code&state=private-state");
await page.waitForFunction(() => window.ready);
await page.evaluate(async () => { acSignup.track('auth_returned'); await acSignup.resume(auth, { email: 'private@example.com' }); });
await page.getByRole("heading", { name: "Check your email" }).waitFor();
await page.getByRole("button", { name: "Resend email" }).click();
await page.getByRole("status").filter({ hasText: "Verification email sent" }).waitFor();
assert.equal(state.resends, 1);
assert.equal(await page.getByRole("button", { name: "Resend email" }).isDisabled(), true);
state.verified = true;
await page.getByRole("button", { name: "I’ve verified my email" }).click();
await page.getByRole("heading", { name: "Choose your @handle" }).waitFor();
await page.getByLabel("Handle", { exact: true }).fill("@new.pal");
await page.getByRole("button", { name: "Create handle", exact: true }).click();
await page.getByRole("status").filter({ hasText: "That handle is taken" }).waitFor();
state.taken = false;
await page.getByLabel("Handle", { exact: true }).fill("new.pal2");
if (process.env.AC_SIGNUP_EVIDENCE_DIR) {
await mkdir(process.env.AC_SIGNUP_EVIDENCE_DIR, { recursive: true });
await page.screenshot({ path: `${process.env.AC_SIGNUP_EVIDENCE_DIR}/handle-desktop.png` });
await page.setViewportSize({ width: 390, height: 844 });
await page.screenshot({ path: `${process.env.AC_SIGNUP_EVIDENCE_DIR}/handle-mobile.png` });
}
await page.getByRole("button", { name: "Create handle", exact: true }).click();
await page.waitForURL("https://aesthetic.computer/laer-klokken");
assert.equal(await page.evaluate(() => sessionStorage.getItem('ac:signup:v1')), null);
assert.equal(await page.evaluate(() => sessionStorage.getItem('fixture:handle-created')), 'yes');
const completed = state.events.find(row => row.stages.includes("completed"));
assert.ok(completed);
for (const stage of ["started", "auth_returned", "verification_shown", "verification_resent", "verified", "handle_shown", "handle_failed", "completed"])
assert.ok(completed.stages.includes(stage), stage);
assert.equal(new Set(state.events.map(row => row.id)).size, 1);
assert.doesNotMatch(JSON.stringify(state.events), /private|new\.pal|fixture-token/);
assert.ok(state.events.every(row => row.automated));
} finally { await f.browser.close(); }
});
test("privacy opt-out retains navigation without sending funnel events; status failures are recoverable", async () => {
const f = await fixture({ optOut: true });
try {
const { page, state } = f;
await page.getByRole("button", { name: "I'm new" }).click();
state.statusFailure = true;
await page.evaluate(() => acSignup.resume(auth, {}));
await page.getByRole("heading", { name: "Finish signing up" }).waitFor();
state.statusFailure = false; state.verified = true;
await page.getByRole("button", { name: "Try again" }).click();
await page.getByRole("heading", { name: "Choose your @handle" }).waitFor();
assert.equal(state.events.length, 0);
assert.equal(await page.evaluate(() => JSON.parse(sessionStorage.getItem('ac:signup:v1')).id), null);
await page.getByLabel("Handle", { exact: true }).fill("bad name");
await page.getByRole("button", { name: "Create handle", exact: true }).click();
assert.ok(!state.requests.some(row => typeof row === "object"));
await page.getByRole("button", { name: "Close", exact: true }).click();
assert.equal(await page.getByRole("dialog").count(), 0);
} finally { await f.browser.close(); }
});
test("ordinary and expired sessions do not open onboarding, and cancellation stops verification polling", async () => {
const f = await fixture();
try {
const { page, state } = f;
await page.evaluate(() => acSignup.resume(auth, {}));
assert.equal(state.requests.length, 0);
await page.evaluate(() => sessionStorage.setItem('ac:signup:v1', JSON.stringify({at: Date.now()-86400001, mode:'signup',stages:[]})));
await page.evaluate(() => acSignup.resume(auth, {}));
assert.equal(state.requests.length, 0);
await page.clock.install();
await page.getByRole("button", { name: "I'm new" }).click();
await page.evaluate(() => acSignup.resume(auth, {}));
await page.getByRole("heading", { name: "Check your email" }).waitFor();
await page.getByRole("button", { name: "Close", exact: true }).click();
const count = state.requests.length;
await page.clock.runFor(31000);
assert.equal(state.requests.length, count);
} finally { await f.browser.close(); }
});
test("closing while account status is in flight does not reopen onboarding", async () => {
const f = await fixture();
try {
const { page } = f;
await page.getByRole("button", { name: "I'm new" }).click();
await page.evaluate(async () => {
let release;
const delayedAuth = {getTokenSilently: () => new Promise(resolve => { release = resolve; })};
const pending = acSignup.resume(delayedAuth, {});
acSignup.close();
release('fixture-token');
await pending;
});
assert.equal(await page.getByRole("dialog").count(), 0);
} finally { await f.browser.close(); }
});