Initial curated itworx-knowledge-hub source from 3b1751d0d9dee551aa6994eee0093704dc334817

This commit is contained in:
Public Source Publisher committed 2026-10-01 01:37:37 +02:00
commit ad8ad252a9
825 files changed
+124216

No files matched your search

+232
View File
@@ -0,0 +1,232 @@
import AxeBuilder from "@axe-core/playwright";
import { expect, test, type Page } from "@playwright/test";
import type {
IngestionProposalDetail,
IngestionProposalSummary,
PaginatedIngestionProposals,
} from "../../../src/frontend/src/api/intakeTypes";
import type { SessionResponse } from "../../../src/frontend/src/api/types";
import { authenticatedStatePath } from "../global-setup";
test.use({ storageState: authenticatedStatePath });
const proposalId = "8b8df97b-6015-4f7b-9bd0-9d9b2fb0e54a";
const correlationId = "e2e-v3-intake-pilot";
const proposalEndpoint = "/api/v1/ingestion-proposals";
function createProposalFixture(): IngestionProposalDetail {
const now = new Date().toISOString();
return {
id: proposalId,
correlationId,
status: "ReadyForReview",
revisionNumber: 7,
sourceCount: 1,
submitterId: "91d38469-1c14-43c5-ae32-c806b4906bd0",
createdAt: now,
destination: {
pageId: "e3f5b858-cd07-4e6e-9bb8-63e1e1712ae4",
parentPageId: null,
title: "Runbook bestandsherstel",
},
metadata: {
documentType: "Runbook",
documentStatus: "Draft",
projectId: null,
risk: "medium",
},
mutationKind: "UpdatePage",
manifest: "# Bestandsherstel\n\nControleer eerst de meest recente back-up.",
analysis: "De wijziging voegt herstelstappen toe en verwijst naar een controleerbare back-up.",
reviewerComment: "Controleer de herstelvolgorde en de genoemde revisie.",
retryCount: 0,
maxPublishRetries: 3,
isRetryEligible: false,
failedLifecycleStatus: null,
failedProcessingStage: null,
failureErrorType: null,
failureMessage: null,
sources: [{
id: "6dce39d5-d022-48b6-90d6-6f1ea8ad0c93",
fileName: "herstelstappen.md",
mimeType: "text/markdown",
byteSize: 512,
digestSha256: "a".repeat(64),
scanResult: "accepted",
attachmentReferenceId: "08ab9571-5fb5-4a77-b151-8bdc4ef302cc",
}],
revisions: [],
etag: "proposal-revision-7",
publishedPageId: null,
publishedPageRevisionNumber: null,
attachments: [],
expectedTargetETag: "page-revision-12",
citations: [{
label: "Back-upcontrole",
pageId: "e3f5b858-cd07-4e6e-9bb8-63e1e1712ae4",
excerpt: "Vergelijk de hash met de vorige volledige back-up.",
confidence: 0.94,
}],
warnings: [{
code: "manual-review",
message: "Handmatige beoordeling vereist voor deze wijziging.",
severity: "medium",
}],
duplicateCandidates: [],
conflictCandidates: [{
pageId: "e3f5b858-cd07-4e6e-9bb8-63e1e1712ae4",
title: "Runbook bestandsherstel",
kind: "target-page",
}],
diff: {
before: "De huidige proceduretekst.",
after: "De voorgestelde herstelprocedure met hashcontrole.",
summary: "Voegt controle van de herstelhash toe.",
},
permissions: { canEdit: true, canDecide: true, canPublish: false },
};
}
async function installProposalFixture(page: Page): Promise<void> {
let currentDetail = createProposalFixture();
const toSummary = (detail: IngestionProposalDetail): IngestionProposalSummary => ({
id: detail.id,
correlationId: detail.correlationId,
status: detail.status,
revisionNumber: detail.revisionNumber,
sourceCount: detail.sourceCount,
submitterId: detail.submitterId ?? "91d38469-1c14-43c5-ae32-c806b4906bd0",
createdAt: detail.createdAt ?? new Date().toISOString(),
destination: detail.destination ?? null,
metadata: detail.metadata,
mutationKind: detail.mutationKind,
});
const queue: PaginatedIngestionProposals = {
items: [toSummary(currentDetail)],
nextCursor: null,
page: 1,
pageSize: 50,
total: 1,
correlationId,
};
// Keep the pilot flag disabled by default in the running service; this synthetic
// UI contract explicitly opts the authenticated fixture session into the route.
await page.route("**/api/v1/auth/session", async (route) => {
const response = await route.fetch();
if (!response.ok()) {
await route.fulfill({ response });
return;
}
const session = await response.json() as SessionResponse;
await route.fulfill({
response,
json: { ...session, features: { ...session.features, ingestionProposals: true } },
});
});
await page.route(/\/api\/v1\/ingestion-proposals(?:\/|\?|$)/, async (route) => {
const request = route.request();
const pathname = new URL(request.url()).pathname;
if (request.method() === "GET" && pathname === proposalEndpoint) {
await route.fulfill({ status: 200, json: { ...queue, items: [toSummary(currentDetail)] } });
return;
}
if (request.method() === "GET" && pathname === `${proposalEndpoint}/${proposalId}`) {
await route.fulfill({ status: 200, json: currentDetail });
return;
}
if (request.method() === "POST" && pathname === `${proposalEndpoint}/${proposalId}/approve`) {
currentDetail = {
...currentDetail,
status: "Approved",
revisionNumber: currentDetail.revisionNumber + 1,
etag: "proposal-revision-8",
};
await route.fulfill({ status: 200, json: currentDetail });
return;
}
await route.continue();
});
}
test.describe("V3 intakepilot: beoordeling op desktop en mobiel", () => {
test("reviewer kan waarschuwingen, verschil, bestemming en beslissing toetsen met toetsenbord", async ({ page }, testInfo) => {
const isMobile = testInfo.project.name === "chromium-mobile";
await page.setViewportSize(isMobile ? { width: 390, height: 844 } : { width: 1440, height: 1000 });
await installProposalFixture(page);
await page.goto("/");
await expect(page.getByRole("heading", { name: "Operationeel overzicht" })).toBeVisible();
await expect(page.getByRole("region", { name: "Kennisintake" })).toHaveCount(0);
if (isMobile) await page.getByRole("button", { name: "Navigatie openen" }).click();
const intakeNavigationLink = page.getByRole("navigation", { name: "Hoofdnavigatie" }).getByRole("link", { name: "Intake", exact: true });
await expect(intakeNavigationLink).toHaveAttribute("href", "/knowledge/intake");
await intakeNavigationLink.click();
await expect(page.getByRole("heading", { name: "Intakewachtrij" })).toBeVisible();
const proposalLink = page.getByRole("link", { name: "Open voorstel Runbook bestandsherstel" });
await proposalLink.focus();
await page.keyboard.press("Enter");
await expect(page.getByLabel("Voorsteloverzicht").getByRole("heading", { name: "Voorstel beoordelen" })).toBeVisible();
await expect(page.locator('[data-status="ReadyForReview"]')).toContainText("Klaar voor beoordeling");
await expect(page.getByRole("heading", { name: "Bronnen en herkomst" })).toBeVisible();
if (isMobile) {
await expect(page.locator('[data-testid="ingestion-progress"]')).toHaveText("Stap 1 van 3: Bronnen");
expect(await page.evaluate(() => document.documentElement.scrollWidth <= window.innerWidth)).toBe(true);
await page.getByRole("button", { name: "Volgende stap" }).press("Enter");
await expect(page.locator('[data-testid="ingestion-progress"]')).toHaveText("Stap 2 van 3: Onderbouwing");
await expect(page.locator('[data-mobile-step="sources"]')).toBeHidden();
await expect(page.locator('[data-mobile-step="evidence"]')).toBeVisible();
await expect(page.getByRole("heading", { name: "Waarschuwingen en conflicten" })).toBeVisible();
await expect(page.getByText("Handmatige beoordeling vereist voor deze wijziging.")).toBeVisible();
await expect(page.getByText("Tegenstrijdige pagina: Runbook bestandsherstel")).toBeVisible();
await expect(page.getByRole("heading", { name: "Voorgestelde inhoud" })).toBeVisible();
await expect(page.getByText("De huidige proceduretekst.", { exact: true })).toBeVisible();
await expect(page.getByText("De voorgestelde herstelprocedure met hashcontrole.", { exact: true })).toBeVisible();
await expect(page.getByRole("heading", { name: "Bestemming en beoordeling" })).toBeVisible();
await expect(page.getByText("Runbook bestandsherstel", { exact: true })).toBeVisible();
await expect(page.getByText("Verwachte paginaversie: page-revision-12")).toBeVisible();
expect(await page.evaluate(() => document.documentElement.scrollWidth <= window.innerWidth)).toBe(true);
const evidenceAccessibility = await new AxeBuilder({ page }).analyze();
expect(evidenceAccessibility.violations).toEqual([]);
// The mobile step moves focus to the active pane; normal Tab order then enters
// the reviewer controls after the read-only warning and diff evidence.
await page.keyboard.press("Tab");
await expect(page.getByRole("textbox", { name: "Voorstel (beoordelaar)" })).toBeFocused();
await page.keyboard.press("Tab");
await expect(page.getByRole("textbox", { name: "Onderbouwing (beoordelaar)" })).toBeFocused();
await page.getByRole("button", { name: "Volgende stap" }).press("Enter");
await expect(page.locator('[data-testid="ingestion-progress"]')).toHaveText("Stap 3 van 3: Beslissing");
await expect(page.locator('[data-mobile-step="evidence"]')).toBeHidden();
await expect(page.locator('[data-mobile-step="decision"]')).toBeVisible();
await expect(page.getByRole("button", { name: "Goedkeuren" })).toBeVisible();
const decisionAccessibility = await new AxeBuilder({ page }).analyze();
expect(decisionAccessibility.violations).toEqual([]);
} else {
await expect(page.getByRole("heading", { name: "Waarschuwingen en conflicten" })).toBeVisible();
await expect(page.getByRole("heading", { name: "Voorgestelde inhoud" })).toBeVisible();
await expect(page.getByRole("heading", { name: "Bestemming en beoordeling" })).toBeVisible();
}
expect(await page.evaluate(() => document.documentElement.scrollWidth <= window.innerWidth)).toBe(true);
const accessibility = await new AxeBuilder({ page }).analyze();
expect(accessibility.violations).toEqual([]);
await page.getByRole("button", { name: "Goedkeuren" }).focus();
await page.keyboard.press("Enter");
await expect(page.locator('[data-status="Approved"]')).toContainText("Goedgekeurd");
});
});