import AxeBuilder from "@axe-core/playwright"; import { expect, test } from "@playwright/test"; import { authenticatedStatePath } from "../global-setup"; import { expectAuthenticatedShell, fillMarkdown, openNewKnowledgePage, readMarkdown } from "./support/v2"; // Reuses the single shared login performed once in global-setup.ts (the login // endpoint is rate-limited — see that file's comment) rather than each test signing // in for itself; only auth-journey.spec.ts exercises the login endpoint directly. test.use({ storageState: authenticatedStatePath }); test.describe("knowledge page editing journey", () => { test("managed import markers stay stored and editable but never appear in the reader view", async ({ page }) => { const uniqueTitle = `E2E-beheerde-bron ${Date.now()}`; const notes = "Mijn byte-beschermde notitie."; const markdown = [ "", "# Veilige broninhoud", "", "", "", "", "", notes, "" ].join("\n"); await page.goto("/"); await expectAuthenticatedShell(page); await openNewKnowledgePage(page); await page.getByLabel("Paginatitel").fill(uniqueTitle); await fillMarkdown(page, markdown); await page.getByLabel(/Labels/).fill("source:canvas, course:zc0026, managed-source"); await page.getByRole("button", { name: "Publiceren", exact: true }).click(); const article = page.locator("article.page-content"); await expect(article).toContainText("Veilige broninhoud"); await expect(article).toContainText(notes); await expect(article).toContainText("unknown-control"); await expect(article).not.toContainText("managed-source"); await expect(article).not.toContainText("user-notes"); await expect(article.locator("script")).toHaveCount(0); await expect(page.getByText("Broninformatie en beheerstatus")).toBeVisible(); await expect(page.locator(".tag-chip", { hasText: "source:canvas" })).toHaveCount(0); await page.getByRole("button", { name: "Bewerken" }).click(); await expect(page.getByRole("note")).toContainText("Laat de vier importmarkeringen"); expect(await readMarkdown(page)).toBe(markdown); const republished = markdown.replace("# Veilige broninhoud", "# Veilige broninhoud bijgewerkt"); await fillMarkdown(page, republished); await page.getByRole("button", { name: "Wijzigingen publiceren" }).click(); await expect(article).toContainText("Veilige broninhoud bijgewerkt"); await expect(article).toContainText(notes); await expect(article).not.toContainText("managed-source"); }); test("a page can be created, edited, moved to the trash and restored", async ({ page }) => { const uniqueTitle = `E2E-testpagina ${Date.now()}`; await page.goto("/"); await expectAuthenticatedShell(page); // --- Create ----------------------------------------------------------------- await openNewKnowledgePage(page); const createAccessibility = await new AxeBuilder({ page }).analyze(); expect(createAccessibility.violations).toEqual([]); await page.getByLabel("Paginatitel").fill(uniqueTitle); await fillMarkdown(page, "# Inleiding\n\nDit is een testpagina voor de E2E-suite."); await page.getByRole("button", { name: "Publiceren", exact: true }).click(); await expect(page.getByRole("heading", { name: uniqueTitle })).toBeVisible(); await expect(page.getByText("Inleiding")).toBeVisible(); const viewAccessibility = await new AxeBuilder({ page }).analyze(); expect(viewAccessibility.violations).toEqual([]); // --- Edit --------------------------------------------------------------------- await page.getByRole("button", { name: "Bewerken" }).click(); await expect(page.getByRole("heading", { name: `Bewerken: ${uniqueTitle}` })).toBeVisible(); const updatedTitle = `${uniqueTitle} (bijgewerkt)`; await page.getByLabel("Paginatitel").fill(updatedTitle); await page.getByRole("button", { name: "Wijzigingen publiceren" }).click(); await expect(page.getByRole("heading", { name: updatedTitle })).toBeVisible(); // --- Trash and restore --------------------------------------------------------- await page.getByRole("button", { name: "Meer pagina-acties" }).click(); await page.getByRole("menuitem", { name: "Naar prullenbak" }).click(); await expect(page.getByText("Deze pagina staat in de prullenbak.")).toBeVisible(); await page.getByRole("button", { name: "Herstellen" }).click(); await expect(page.getByText("Deze pagina staat in de prullenbak.")).not.toBeVisible(); }); test("publishing against a stale version shows an explicit conflict resolution flow", async ({ page, context }) => { const uniqueTitle = `E2E-conflictpagina ${Date.now()}`; await page.goto("/"); await expectAuthenticatedShell(page); await openNewKnowledgePage(page); await page.getByLabel("Paginatitel").fill(uniqueTitle); await fillMarkdown(page, "Oorspronkelijke inhoud."); await page.getByRole("button", { name: "Publiceren", exact: true }).click(); await expect(page.getByRole("heading", { name: uniqueTitle })).toBeVisible(); // A second tab opens the same shareable V2 deep link, simulating two concurrent // editors without relying on transient component state. const pageUrl = page.url(); const secondPage = await context.newPage(); await secondPage.goto(pageUrl); await expectAuthenticatedShell(secondPage); await expect(secondPage.getByRole("heading", { name: uniqueTitle })).toBeVisible(); await secondPage.getByRole("button", { name: "Bewerken" }).click(); await secondPage.getByLabel("Paginatitel").fill(`${uniqueTitle} (tweede editor)`); // The first tab publishes first, advancing the page's revision/ETag. await page.getByRole("button", { name: "Bewerken" }).click(); await page.getByLabel("Paginatitel").fill(`${uniqueTitle} (eerste editor)`); await page.getByRole("button", { name: "Wijzigingen publiceren" }).click(); await expect(page.getByRole("heading", { name: `${uniqueTitle} (eerste editor)` })).toBeVisible(); // The second tab's publish attempt now targets a stale ETag and must surface an // explicit conflict rather than silently overwriting the first editor's change. await secondPage.getByRole("button", { name: "Wijzigingen publiceren" }).click(); await expect(secondPage.getByText("Versieconflict")).toBeVisible(); const conflictAccessibility = await new AxeBuilder({ page: secondPage }).analyze(); expect(conflictAccessibility.violations).toEqual([]); await secondPage.getByRole("button", { name: "Serverversie gebruiken" }).click(); await expect(secondPage.getByLabel("Paginatitel")).toHaveValue(`${uniqueTitle} (eerste editor)`); await secondPage.close(); }); });