test(redesign): establish preservation baseline

Map 57 capability groups before restructuring. Add behavioral regression coverage and a standalone TypeScript gate; retain the native SQLite package failure as separate baseline debt.
This commit is contained in:
cesnimda
2026-09-10 12:53:06 +02:00
parent 2d0f131a99
commit d6d3dd977d
14 changed files with 835 additions and 3 deletions
@@ -0,0 +1,188 @@
import { expect, test as base, type APIRequestContext } from "@playwright/test";
const apiUrl = "http://localhost:5302/api";
const password = "E2ePassword123!";
const canonical = {
version: "1", contact: { fullName: "Åse Eksempel", headline: "Canonical engineer" },
jobs: [{ title: "Engineer", company: "Nordlys AS", bullets: ["Built reliable APIs"] }],
skills: ["C#", "Norsk"],
};
async function login(api: APIRequestContext, email: string) {
expect((await api.post(`${apiUrl}/auth/login`, { data: { email, password } })).status()).toBe(200);
const me = await (await api.get(`${apiUrl}/auth/me`)).json();
const csrf = (await api.storageState()).cookies.find(cookie => cookie.name === "XSRF-TOKEN")!.value;
return { "X-CSRF-TOKEN": csrf, "X-Session-Context": me.sessionContext as string };
}
// Every case gets a real non-admin account in Playwright's disposable local API database.
// No API responses are mocked; browser traffic cannot leave the local test environment.
const test = base.extend<{ headers: Record<string, string> }>({
headers: async ({ playwright, page, context }, provide) => {
const admin = await playwright.request.newContext();
try {
const adminHeaders = await login(admin, "e2e@example.test");
const email = `pres-${Date.now()}-${Math.random().toString(36).slice(2)}@example.test`;
expect((await admin.post(`${apiUrl}/users`, { headers: adminHeaders, data: { email, password, roles: [] } })).status()).toBe(200);
const headers = await login(page.request, email);
await context.route("**/*", route => {
const url = new URL(route.request().url());
return ["localhost", "127.0.0.1"].includes(url.hostname) || ["data:", "blob:", "about:"].includes(url.protocol)
? route.continue() : route.abort();
});
await context.addInitScript(() => localStorage.setItem("uiLanguage", "en"));
expect((await page.request.put(`${apiUrl}/career/profile`, { headers, data: { profile: canonical } })).status()).toBe(200);
await provide(headers);
} finally { await admin.dispose(); }
},
});
test("[PRES-C01 PRES-C06 PRES-C07 PRES-C10] mobile CV override persists without changing the canonical profile or sibling CV", async ({ page, headers }) => {
const create = async (name: string) => {
const response = await page.request.post(`${apiUrl}/cv/variants`, { headers, data: { name } });
expect(response.status()).toBe(200);
return response.json();
};
const selected = await create("Selected CV");
const sibling = await create("Sibling CV");
const profileBefore = await (await page.request.get(`${apiUrl}/career/profile`)).json();
await page.setViewportSize({ width: 390, height: 844 });
await page.goto(`/career/builder/${selected.id}`);
await page.getByRole("tab", { name: "Content", exact: true }).click();
await page.getByLabel("Headline override", { exact: true }).fill("Reviewed CV-only headline");
await page.getByRole("button", { name: "Save now", exact: true }).click();
await expect(page.getByText("Saved", { exact: true })).toBeVisible();
await page.getByRole("button", { name: "Preview", exact: true }).click();
await expect(page.getByTitle("CV preview page 1")).toBeVisible();
await page.getByRole("button", { name: "Edit", exact: true }).click();
await expect(page.getByLabel("Headline override", { exact: true })).toHaveValue("Reviewed CV-only headline");
await page.reload();
await page.getByRole("tab", { name: "Content", exact: true }).click();
await expect(page.getByLabel("Headline override", { exact: true })).toHaveValue("Reviewed CV-only headline");
await page.setViewportSize({ width: 1440, height: 900 });
await expect(page.getByTitle("CV preview page 1")).toBeVisible();
expect((await (await page.request.get(`${apiUrl}/cv/variants/${selected.id}`)).json()).settings.headline).toBe("Reviewed CV-only headline");
expect(await (await page.request.get(`${apiUrl}/cv/variants/${sibling.id}`)).json()).toEqual(sibling);
expect((await (await page.request.get(`${apiUrl}/career/profile`)).json()).profile).toEqual(profileBefore.profile);
});
test("[PRES-W01 PRES-W05 PRES-W06 PRES-W07 PRES-W08] legacy workspace preserves frozen documents after current sources change", async ({ page, headers }) => {
const companyResponse = await page.request.post(`${apiUrl}/companies`, { headers, data: { name: "Preservation AS" } });
expect(companyResponse.status()).toBe(201);
const company = await companyResponse.json();
const jobResponse = await page.request.post(`${apiUrl}/jobapplications`, { headers, data: { companyId: company.id, jobTitle: "Preserved workspace", status: "Saved" } });
expect(jobResponse.ok()).toBe(true);
const job = await jobResponse.json();
const variantResponse = await page.request.post(`${apiUrl}/cv/variants`, { headers, data: { name: "Submitted CV", jobApplicationId: job.id } });
expect(variantResponse.status()).toBe(200);
const variant = await variantResponse.json();
const jobUrl = `${apiUrl}/jobapplications/${job.id}`;
expect((await page.request.put(`${jobUrl}/cv`, { headers, data: { variantId: variant.id } })).status()).toBe(200);
expect((await page.request.put(`${jobUrl}/cover-letter`, { headers, data: { text: "Original reviewed letter", source: "manual" } })).status()).toBe(200);
const bytes = Buffer.from("Original attachment — æøå\n", "utf8");
const upload = await page.request.post(`${apiUrl}/attachments`, { headers, multipart: { jobId: String(job.id), files: { name: "evidence.txt", mimeType: "text/plain", buffer: bytes } } });
expect(upload.ok()).toBe(true);
const [attachment] = await (await page.request.get(`${apiUrl}/attachments/${job.id}`)).json();
await page.goto(`/applications/${job.id}?section=cv`);
await expect(page).toHaveURL(new RegExp(`/jobs/${job.id}\\?section=cv$`));
const captured = page.waitForResponse(response => response.url() === `${jobUrl}/submitted-packages` && response.request().method() === 'POST');
await page.getByRole("button", { name: "Freeze current package", exact: true }).click();
const captureResponse = await captured;
// P2-D01: native SQLite rejects the service's DateTimeOffset ORDER BY. Only
// this known response is expected; setup failures and later archive failures
// after a successful capture must remain ordinary failing assertions.
if (captureResponse.status() === 500) {
test.fail(true, 'P2-D01: SQLite submitted-package capture; phase-2/BASELINE_DEFECTS.md');
}
expect(captureResponse.status()).toBe(200);
await expect(page.getByText("Submission package v1", { exact: true })).toBeVisible();
const [snapshot] = await (await page.request.get(`${jobUrl}/submitted-packages`)).json();
const before = await (await page.request.get(`${jobUrl}/submitted-packages/${snapshot.id}`)).json();
expect(before.coverLetterText).toBe("Original reviewed letter");
expect(before.renderedCvHtml).toContain("Canonical engineer");
expect(snapshot.cvVariantId).toBe(variant.id);
expect(snapshot.attachments).toHaveLength(1);
expect((await page.request.put(`${jobUrl}/cover-letter`, { headers, data: { text: "Later letter", source: "manual" } })).status()).toBe(200);
expect((await page.request.put(`${apiUrl}/cv/variants/${variant.id}`, { headers, data: { name: "Later CV", settings: { ...variant.settings, headline: "Later headline" } } })).status()).toBe(200);
expect((await page.request.delete(`${apiUrl}/attachments/${attachment.id}`, { headers })).status()).toBe(204);
expect(await (await page.request.get(`${jobUrl}/submitted-packages/${snapshot.id}`)).json()).toEqual(before);
const archived = await page.request.get(`${jobUrl}/submitted-packages/${snapshot.id}/attachments/${snapshot.attachments[0].id}`);
expect(archived.status()).toBe(200);
expect(await archived.body()).toEqual(bytes);
await page.reload();
await page.getByRole("button", { name: "View preserved package", exact: true }).click();
await expect(page.getByRole("dialog").getByText("Original reviewed letter", { exact: true })).toBeVisible();
});
test("[PRES-S03 PRES-W11] responsive settings navigation keeps rule edits and persists notification preferences", async ({ page, headers }) => {
expect((await page.request.get(`${apiUrl}/auth/me`, { headers })).status()).toBe(200);
await page.setViewportSize({ width: 390, height: 844 });
await page.goto("/settings");
await page.getByLabel("Settings section", { exact: true }).click();
await page.getByRole("option", { name: "Follow-ups", exact: true }).click();
const field = page.getByLabel("Applied: follow-up days", { exact: true });
await field.fill("5");
await page.setViewportSize({ width: 1440, height: 900 });
await expect(field).toHaveValue("5");
const saveRules = page.waitForResponse(response => response.url() === `${apiUrl}/rules` && response.request().method() === 'PUT');
await page.getByRole("button", { name: "Save Rules", exact: true }).click();
expect((await saveRules).status()).toBe(204);
await expect.poll(async () => (await (await page.request.get(`${apiUrl}/rules`)).json()).appliedFollowUpDays).toBe(5);
await page.getByRole("tab", { name: "Notifications", exact: true }).click();
await page.getByLabel("Email reminders for follow-ups", { exact: true }).uncheck();
await page.getByRole("button", { name: "Save notification settings", exact: true }).click();
await expect.poll(async () => (await (await page.request.get(`${apiUrl}/notification-settings`)).json()).emailFollowUpRemindersEnabled).toBe(false);
await page.reload();
await page.getByRole("tab", { name: "Notifications", exact: true }).click();
await expect(page.getByLabel("Email reminders for follow-ups", { exact: true })).not.toBeChecked();
});
test("[PRES-S03] rule changes persist through the real API", async ({ page, headers }) => {
const settings = await (await page.request.get(`${apiUrl}/rules`)).json();
const saved = await page.request.put(`${apiUrl}/rules`, { headers, data: { ...settings, appliedFollowUpDays: 5 } });
expect(saved.status()).toBe(204);
expect((await (await page.request.get(`${apiUrl}/rules`)).json()).appliedFollowUpDays).toBe(5);
});
test("[PRES-E07 PRES-W01 PRES-J04] keyboard command navigation and legacy workspace entry preserve the selected job", async ({ page, headers }) => {
const companyResponse = await page.request.post(`${apiUrl}/companies`, { headers, data: { name: "Command AS" } });
expect(companyResponse.status()).toBe(201);
const jobResponse = await page.request.post(`${apiUrl}/jobapplications`, { headers,
data: { companyId: (await companyResponse.json()).id, jobTitle: "Command engineer", status: "Saved" } });
expect(jobResponse.ok()).toBe(true);
const job = await jobResponse.json();
await page.goto("/dashboard");
await expect(page.getByRole("heading", { name: "Dashboard", exact: true })).toBeVisible();
await page.keyboard.press("Control+k");
await page.getByRole("dialog").getByRole("textbox").fill("Command engineer");
await page.getByText("Command AS - Command engineer", { exact: true }).click();
await expect(page).toHaveURL(new RegExp(`/jobs/${job.id}$`));
await expect(page.getByRole("heading", { name: "Command engineer", exact: true })).toBeVisible();
await page.goto(`/applications/${job.id}?section=match`);
await expect(page).toHaveURL(new RegExp(`/jobs/${job.id}\\?section=match$`));
await expect(page.getByRole("tab", { name: "Analysis", exact: true })).toHaveAttribute("aria-selected", "true");
await page.keyboard.press("Control+k");
await page.getByRole("dialog").getByText("Add new job", { exact: true }).click();
await expect(page.getByRole("dialog")).toBeVisible();
expect((await (await page.request.get(`${apiUrl}/jobapplications`)).json()).total).toBe(1);
});
test("[PRES-A01 PRES-S04 PRES-E07] non-admin navigation cannot grant API privileges or unlock CV AI", async ({ page, headers }) => {
await page.goto("/dashboard");
await expect(page.getByRole("heading", { name: "Dashboard", exact: true })).toBeVisible();
for (const name of ["Users", "Audit log", "System"]) await expect(page.getByRole("button", { name, exact: true })).toHaveCount(0);
const denied = page.waitForResponse(response => response.url() === `${apiUrl}/users`);
await page.goto("/admin/users");
expect((await denied).status()).toBe(403);
await expect(page.getByText("e2e@example.test", { exact: true })).toHaveCount(0);
expect((await page.request.post(`${apiUrl}/users`, { headers, data: { email: "must-not-exist@example.test", password, roles: ["Admin"] } })).status()).toBe(403);
const created = await page.request.post(`${apiUrl}/cv/variants`, { headers, data: { name: "Free CV" } });
expect(created.status()).toBe(200);
await page.goto(`/career/builder/${(await created.json()).id}`);
await page.getByRole("tab", { name: "AI Tools", exact: true }).click();
const actions = page.getByRole("button", { name: "Review suggestion", exact: true });
await expect(actions).toHaveCount(3);
for (const action of await actions.all()) await expect(action).toBeDisabled();
expect((await page.request.post(`${apiUrl}/cv/ai/assist`, { headers, data: { action: "improve", text: "Safe test text" } })).status()).toBe(403);
expect((await (await page.request.get(`${apiUrl}/auth/me`)).json()).roles).toEqual([]);
});