Initial curated projectbrain source from 8a7b1e0651dbdab97d744cbd2e653ab3970ac6c5

This commit is contained in:
Public Source Publisher committed 2026-10-01 01:43:26 +02:00
commit 5aa3e8d685
700 files changed
+132801

No files matched your search

+112
View File
@@ -0,0 +1,112 @@
import AxeBuilder from "@axe-core/playwright";
import { expect, test } from "@playwright/test";
import type { AutoModeState } from "../src/shared/api/autonomyApi";
const projectId = "widefrog";
const envelope = (data: unknown) => ({ data, meta: { requestId: "attention-auto-e2e", timestamp: new Date().toISOString() } });
const autoModeState = (): AutoModeState => ({
version: 1,
enabled: true,
status: "running",
autoBrainstorm: true,
maxBrainstormIterations: 5,
maxConsecutiveFailures: 3,
leaseMinutes: 30,
maxWorkItemsPerRun: 8,
maxRunMinutes: 45,
roadmap: {
id: "roadmap-e2e",
title: "E2E-roadmap",
sourceMarkdown: "# E2E-roadmap\n\n## Eerste fase\n- [ ] Exact werkitem",
status: "active",
currentPhaseIndex: 0,
importedAt: "2026-09-01T00:00:00.000Z",
phases: [{
id: "phase-e2e",
title: "Eerste fase",
objective: "Eerste fase",
status: "active",
brainstormIterations: 0,
brainstormConverged: false,
validationFailures: 0,
items: [{ id: "item-e2e", title: "Exact werkitem", status: "pending", attempts: 0 }],
}],
},
completedLeases: [],
events: [],
updatedAt: "2026-09-01T00:00:00.000Z",
updatedBy: "owner",
});
test("attention inbox exposes every visible item as an actionable destination", async ({ page }) => {
await page.goto(`/projects/${projectId}/attention`);
await expect(page.getByRole("heading", { name: "Jouw aandacht", exact: true })).toBeVisible();
await expect(page.getByText(/item\(s\) vragen jouw aandacht|Niets wacht op jou/)).toBeVisible();
const reviewAction = page.getByRole("button", { name: /Beoordelen/ }).first();
if (await reviewAction.isVisible()) {
await reviewAction.click();
await expect(page).toHaveURL(/\/review-queue\?reviewItemId=/);
}
const accessibility = await new AxeBuilder({ page }).analyze();
expect(accessibility.violations.filter((violation) => ["serious", "critical"].includes(violation.impact ?? ""))).toEqual([]);
});
test("Auto Mode distinguishes queued work from real execution and protects roadmap progress", async ({ page }) => {
let state = autoModeState();
let roadmapImports = 0;
await page.route(`**/api/projects/${projectId}/autonomy**`, async (route) => {
const request = route.request();
const url = new URL(request.url());
if (request.method() === "GET") {
await route.fulfill({ status: 200, contentType: "application/json", body: JSON.stringify(envelope(state)) });
return;
}
if (request.method() === "POST" && url.pathname.endsWith("/roadmap")) {
roadmapImports += 1;
await route.fulfill({ status: 201, contentType: "application/json", body: JSON.stringify(envelope(state)) });
return;
}
await route.fulfill({ status: 200, contentType: "application/json", body: JSON.stringify(envelope(state)) });
});
await page.goto(`/projects/${projectId}/auto`);
await expect(page.getByRole("heading", { name: "Auto Mode", exact: true })).toBeVisible();
await expect(page.getByText("Klaar voor uitvoering", { exact: true }).first()).toBeVisible();
await expect(page.getByText("Werk staat veilig in de wachtrij")).toBeVisible();
await expect(page.getByText("ProjectBrain werkt", { exact: true })).toHaveCount(0);
await page.getByText("Autonomiebeleid en geavanceerde instellingen", { exact: true }).click();
await page.getByText("Geavanceerde grenzen", { exact: true }).click();
await expect(page.getByLabel("Werkitems per run")).toHaveValue("8");
await expect(page.getByLabel("Looptijd per run")).toHaveValue("45");
await expect(page.getByText("Roadmap handmatig importeren", { exact: true })).toBeVisible();
await page.getByLabel("Roadmap").fill("# Nieuwe roadmap\n\n## Nieuwe fase\n- [ ] Nieuw werk");
await page.getByRole("button", { name: "Roadmap bijwerken" }).click();
await expect(page.getByText("Bestaande roadmap en voortgang vervangen?")).toBeVisible();
expect(roadmapImports).toBe(0);
await page.getByRole("button", { name: "Annuleren" }).click();
state = {
...state,
activeLease: {
token: "00000000-0000-4000-8000-000000000001",
kind: "implement",
actor: "chatgpt-agent",
roadmapId: "roadmap-e2e",
phaseId: "phase-e2e",
itemId: "item-e2e",
createdAt: new Date(Date.now() - 60_000).toISOString(),
expiresAt: new Date(Date.now() + 30 * 60_000).toISOString(),
},
};
await page.getByRole("button", { name: "Vernieuwen" }).click();
await expect(page.getByText("ProjectBrain werkt", { exact: true })).toBeVisible();
await expect(page.getByText("Exact werkitem", { exact: true }).first()).toBeVisible();
const accessibility = await new AxeBuilder({ page }).analyze();
expect(accessibility.violations.filter((violation) => ["serious", "critical"].includes(violation.impact ?? ""))).toEqual([]);
});
+267
View File
@@ -0,0 +1,267 @@
import AxeBuilder from "@axe-core/playwright";
import { expect, test, type Page } from "@playwright/test";
const projectId = "widefrog";
const workspaceHeading = /Wat wil je dat ProjectBrain bereikt\?/i;
const activityHeading = /Activiteit/i;
const assertHealthyPage = async (page: Page, heading: RegExp) => {
const consoleErrors: string[] = [];
const failedRequests: string[] = [];
page.on("console", (message) => { if (message.type() === "error") consoleErrors.push(message.text()); });
page.on("requestfailed", (request) => failedRequests.push(`${request.method()} ${request.url()}`));
await expect(page.getByRole("heading", { name: heading }).first()).toBeVisible();
await expect(page.locator("body")).not.toContainText("Something went wrong");
const overflow = await page.evaluate(() => document.documentElement.scrollWidth - document.documentElement.clientWidth);
expect(overflow).toBeLessThanOrEqual(1);
const accessibility = await new AxeBuilder({ page }).analyze();
expect(accessibility.violations.filter((violation) => ["serious", "critical"].includes(violation.impact ?? ""))).toEqual([]);
expect(consoleErrors).toEqual([]);
expect(failedRequests).toEqual([]);
};
test("1. opens the project portfolio and reopens persistent project memory", async ({ page }) => {
test.setTimeout(75_000);
await page.goto("/projects");
await assertHealthyPage(page, /Projectcatalogus/i);
await page.goto(`/projects/${projectId}/workspace`);
await assertHealthyPage(page, workspaceHeading);
await expect(page.getByRole("button", { name: /WideFrog/i }).first()).toBeVisible();
await page.reload();
await assertHealthyPage(page, workspaceHeading);
await expect(page.getByRole("button", { name: /WideFrog/i }).first()).toBeVisible();
});
test("2. capture and review surfaces form one navigable intake journey", async ({ page }) => {
await page.goto(`/projects/${projectId}/capture`);
await assertHealthyPage(page, /Capture Center/i);
await page.goto(`/projects/${projectId}/review-queue`);
await assertHealthyPage(page, /Te beoordelen/i);
});
test("3. review and accepted-truth history surfaces remain recoverable", async ({ page }) => {
await page.goto(`/projects/${projectId}/review-queue`);
await assertHealthyPage(page, /Te beoordelen/i);
expect(await page.locator('[aria-live="polite"], [role="status"], [role="alert"]').count()).toBeGreaterThan(0);
await page.goto(`/projects/${projectId}/history`);
await assertHealthyPage(page, activityHeading);
});
test("9. critical route remains operable on a constrained connection", async ({ page }) => {
await page.route("**/*.js", async (route) => {
await new Promise((resolve) => setTimeout(resolve, 75));
await route.continue();
});
await page.goto(`/projects/${projectId}/review-queue`);
await assertHealthyPage(page, /Te beoordelen/i);
await page.getByText("Proces, bewijs en beheer", { exact: true }).click();
await expect(page.getByRole("button", { name: "Zichtbare reviews exporteren" })).toBeEnabled();
});
test("10. action surfaces expose one primary action while system status stays diagnostic", async ({ page }) => {
for (const route of ["workspace", "review-queue", "execution"]) {
await page.goto(`/projects/${projectId}/${route}`);
await expect(page.locator('[data-page-primary="true"]:visible')).toHaveCount(1);
}
await page.goto(`/projects/${projectId}/runtime`);
await expect(page.getByRole("heading", { name: "Systeemstatus", exact: true })).toBeVisible();
await expect(page.getByText("ProjectBrain API", { exact: true })).toBeVisible();
await expect(page.getByText("Database", { exact: true }).first()).toBeVisible();
await expect(page.getByText("Runners", { exact: true }).first()).toBeVisible();
await expect(page.getByRole("button", { name: "Bijwerken", exact: true })).toBeVisible();
});
test("4. context, handoff and session-pack chain renders end to end", async ({ page }) => {
for (const [route, heading] of [["context-packs", /Context Packs/i], ["handoffs", /Codex Handoffs/i], ["session-packs", /Session Packs/i]] as const) {
await page.goto(`/projects/${projectId}/${route}`);
await assertHealthyPage(page, heading);
}
});
test("5. execution feedback links back to durable project history", async ({ page }) => {
await page.goto(`/projects/${projectId}/execution-feedback`);
await assertHealthyPage(page, /Execution Feedback/i);
await page.goto(`/projects/${projectId}/history`);
await assertHealthyPage(page, activityHeading);
});
test("6. release evidence and release center surfaces render", async ({ page }) => {
await page.goto(`/projects/${projectId}/runtime`);
await assertHealthyPage(page, /Systeemstatus/i);
await page.goto(`/projects/${projectId}/release`);
await assertHealthyPage(page, /Releasecentrum/i);
});
test("7. mock-mode development identity survives reload", async ({ page }) => {
await page.goto(`/projects/${projectId}/workspace`);
await assertHealthyPage(page, workspaceHeading);
await expect(page.getByRole("button", { name: /WideFrog/i }).first()).toBeVisible();
await page.reload();
await expect(page.getByRole("heading", { name: workspaceHeading })).toHaveCount(1);
await expect(page.getByRole("button", { name: /WideFrog/i }).first()).toBeVisible();
await expect(page.getByRole("heading", { name: /Aanmelden/i })).toHaveCount(0);
});
test("8. keyboard, route focus, reduced motion and 200% zoom stay usable", async ({ page }) => {
await page.emulateMedia({ reducedMotion: "reduce" });
await page.goto(`/projects/${projectId}/workspace`);
await page.keyboard.press("Tab");
const skipLink = page.getByRole("link", { name: "Ga naar hoofdinhoud" });
await expect(skipLink).toBeFocused();
await skipLink.press("Enter");
await expect(page.locator("#projectbrain-main")).toBeFocused();
await page.evaluate(() => { document.documentElement.style.zoom = "2"; });
const overflow = await page.evaluate(() => document.documentElement.scrollWidth - document.documentElement.clientWidth);
expect(overflow).toBeLessThanOrEqual(1);
await expect(page.getByRole("heading", { name: workspaceHeading })).toHaveCount(1);
await page.evaluate(() => { document.documentElement.style.zoom = "1"; });
const moreButton = page.getByRole("button", { name: "Meer" });
if (await moreButton.isVisible()) {
await moreButton.click();
const navigationDialog = page.getByRole("dialog", { name: "Projectnavigatie" });
await expect(navigationDialog).toBeVisible();
await expect(navigationDialog).toContainText("ProjectBrain");
await page.keyboard.press("Escape");
await expect(navigationDialog).toBeHidden();
await expect(moreButton).toBeFocused();
}
await page.goto(`/projects/${projectId}/capture`);
await expect(page.locator("#projectbrain-main")).toBeFocused();
});
test("11. development workbench creates an isolated branch, commits a file and opens a pull request", async ({ page }) => {
await page.goto(`/projects/${projectId}/develop`);
await expect(page.getByRole("heading", { name: /WideFrog · repository/i })).toBeVisible();
const localRepositoryBridge = page.getByRole("button", { name: "Repository voorbereiden" });
if (await localRepositoryBridge.isVisible()) {
await expect(page.getByText("Werk lokaal verder in Repositorybouwer")).toBeVisible();
await expect(localRepositoryBridge).toBeEnabled();
return;
}
let branchCreated = false;
let fileSaved = false;
let validationSynced = false;
let pullRequestCreated = false;
const envelope = (data: unknown) => ({ data, meta: { requestId: "e2e-workbench", timestamp: new Date().toISOString() } });
const link = { id: "link-1", projectId, projectSlug: projectId, projectName: "WideFrog", owner: "Jens", repository: "widefrog", fullName: "Jens/widefrog", defaultBranch: "main", accessMode: "read_write", role: "primary", isPrimary: true };
const delivery = () => ({
id: "delivery-1", projectId, title: "Continue WideFrog", intent: "Deliver a reviewed repository change", priority: "important", approvalState: "approved",
spec: {
version: 1, problem: "The repository needs an updated readme", desiredOutcome: "The readme is current", users: ["maintainer"], goals: ["Update README"], nonGoals: [],
acceptanceCriteria: ["README is updated"], implementationPlan: ["Edit README.md"], risks: [], validationCommands: ["pnpm test"],
status: pullRequestCreated ? "pull_request_open" : validationSynced ? "validating" : branchCreated ? "executing" : "approved",
branch: branchCreated ? "chatgpt/widefrog-work" : undefined, repositoryId: "link-1", changedFiles: fileSaved ? ["README.md"] : [], progressNotes: [],
validationResults: validationSynced ? [{ command: "pnpm test", status: "passed" }] : [],
validationEvidence: validationSynced ? { provider: "gitea_actions", runId: 42, runNumber: 42, url: "https://gitea.example/Jens/widefrog/actions/runs/42", workflowPath: ".gitea/workflows/ci.yml", headSha: "work-sha", conclusion: "success", verifiedAt: new Date().toISOString() } : undefined,
pullRequest: pullRequestCreated ? { number: 7, url: "https://gitea.example/Jens/widefrog/pulls/7", title: "ProjectBrain: continue WideFrog", state: "open" } : undefined,
updatedBy: "e2e", updatedAt: new Date().toISOString(),
},
createdAt: new Date().toISOString(), updatedAt: new Date().toISOString(),
});
await page.route(`**/api/projects/${projectId}/feature-deliveries`, async (route) => {
await route.fulfill({ status: 200, contentType: "application/json", body: JSON.stringify(envelope({ items: [delivery()] })) });
});
await page.route(`**/api/projects/${projectId}/feature-deliveries/delivery-1/validation/sync`, async (route) => {
validationSynced = true;
await route.fulfill({ status: 200, contentType: "application/json", body: JSON.stringify(envelope({ delivery: delivery() })) });
});
await page.route(`**/api/projects/${projectId}/gitea/**`, async (route) => {
const request = route.request();
const url = new URL(request.url());
if (url.pathname.endsWith("/repositories")) {
await route.fulfill({ status: 200, contentType: "application/json", body: JSON.stringify(envelope([link])) }); return;
}
if (url.pathname.endsWith("/workspace")) {
const ref = url.searchParams.get("ref") || "main";
await route.fulfill({ status: 200, contentType: "application/json", body: JSON.stringify(envelope({
link, path: url.searchParams.get("path") || "", ref,
entries: [{ type: "file", name: "README.md", path: "README.md", sha: "sha-1", size: 24, html_url: "https://gitea.example/Jens/widefrog/src/branch/main/README.md" }],
branches: [{ name: "main", commit: { id: "main-sha" }, protected: true }, ...(branchCreated ? [{ name: "chatgpt/widefrog-work", commit: { id: "work-sha" }, protected: false }] : [])],
pullRequests: pullRequestCreated ? [{ id: 1, number: 7, title: "ProjectBrain: continue WideFrog", state: "open", html_url: "https://gitea.example/Jens/widefrog/pulls/7", merged: false, created_at: new Date().toISOString(), updated_at: new Date().toISOString(), head: { ref: "chatgpt/widefrog-work", sha: "work-sha" }, base: { ref: "main", sha: "main-sha" } }] : [],
})) }); return;
}
if (url.pathname.endsWith("/file") && request.method() === "GET") {
await route.fulfill({ status: 200, contentType: "application/json", body: JSON.stringify(envelope({ path: "README.md", ref: url.searchParams.get("ref") || "main", sha: "sha-1", size: 24, content: fileSaved ? "# WideFrog\n\nUpdated." : "# WideFrog\n" })) }); return;
}
if (url.pathname.endsWith("/branches") && request.method() === "POST") {
branchCreated = true; await route.fulfill({ status: 201, contentType: "application/json", body: JSON.stringify(envelope({ repository: "Jens/widefrog", branch: { name: "chatgpt/widefrog-work", commit: { id: "work-sha" }, protected: false } })) }); return;
}
if (url.pathname.endsWith("/file") && request.method() === "PUT") {
expect(request.postDataJSON()).toMatchObject({ expectedSha: "sha-1" });
fileSaved = true; await route.fulfill({ status: 200, contentType: "application/json", body: JSON.stringify(envelope({ repository: "Jens/widefrog", branch: "chatgpt/widefrog-work", path: "README.md", action: "updated" })) }); return;
}
if (url.pathname.endsWith("/pull-requests") && request.method() === "POST") {
pullRequestCreated = true; await route.fulfill({ status: 201, contentType: "application/json", body: JSON.stringify(envelope({ repository: "Jens/widefrog", pullRequest: { number: 7 } })) }); return;
}
await route.abort();
});
await page.goto(`/projects/${projectId}/develop`);
await expect(page.getByRole("heading", { name: /WideFrog · repository/i })).toBeVisible();
await page.getByRole("textbox", { name: "Nieuwe deliverybranch" }).fill("chatgpt/widefrog-work");
await page.getByRole("button", { name: /Werkbranch maken/i }).click();
await expect(page.getByText("chatgpt/widefrog-work").first()).toBeVisible();
await page.getByRole("button", { name: /README.md/i }).click();
await page.getByRole("textbox", { name: "Repository file content" }).fill("# WideFrog\n\nUpdated.");
await page.getByRole("button", { name: /Bestand committen/i }).click();
expect(fileSaved).toBe(true);
await page.getByRole("button", { name: /Actions verifiëren/i }).click();
expect(validationSynced).toBe(true);
await page.getByRole("button", { name: /Pull request maken/i }).click();
expect(pullRequestCreated).toBe(true);
});
test("12. daily navigation hides specialist modules while keeping them searchable", async ({ page }) => {
await page.goto(`/projects/${projectId}/workspace`);
const moreButton = page.getByRole("button", { name: "Meer" });
if (await moreButton.isVisible()) await moreButton.click();
await expect(page.getByRole("link", { name: /Opdracht geven/i }).first()).toBeVisible();
await expect(page.getByRole("link", { name: /Repositoryanalyses/i })).toHaveCount(0);
const advancedModulesButton = page.getByRole("button", { name: /Technische details tonen/i });
if (await advancedModulesButton.isVisible()) {
await advancedModulesButton.click();
await expect(page.getByRole("link", { name: /Repositoryanalyses/i })).toBeVisible();
await page.getByRole("button", { name: /Technische details verbergen/i }).click();
await expect(page.getByRole("link", { name: /Repositoryanalyses/i })).toHaveCount(0);
return;
}
await page.getByRole("button", { name: "Open zoek- en opdrachtenmenu" }).click();
await page.getByRole("combobox", { name: "Zoek pagina's, projectitems en opdrachten" }).fill("Repositoryanalyses");
await page.getByText("Repositoryanalyses", { exact: true }).first().click();
await expect(page).toHaveURL(new RegExp(`/projects/${projectId}/analysis-jobs$`));
await expect(page.getByRole("link", { name: "Repositoryanalyses", exact: true })).toBeVisible();
});
test("13. mobile shell keeps five-intent route context and search available", async ({ page }) => {
await page.setViewportSize({ width: 390, height: 844 });
await page.goto("/projects/nebula/workspace");
await page.evaluate(() => {
window.history.pushState({}, "", "/projects/widefrog/findings");
window.dispatchEvent(new PopStateEvent("popstate"));
});
await expect(page).toHaveURL(/\/projects\/widefrog\/findings$/);
await expect(page.getByRole("link", { name: "Overzicht", exact: true })).toHaveAttribute("href", "/projects/widefrog/overview");
await expect(page.getByRole("link", { name: "Opdracht", exact: true })).toHaveAttribute("href", "/projects/widefrog/workspace");
await expect(page.getByRole("link", { name: "Aandacht", exact: true })).toHaveAttribute("href", "/projects/widefrog/attention");
await expect(page.getByRole("link", { name: "Resultaten", exact: true })).toHaveAttribute("href", "/projects/widefrog/results");
await expect(page.getByRole("link", { name: "Auto", exact: true })).toHaveCount(0);
await expect(page.getByRole("button", { name: "Meer" })).toHaveAttribute("aria-current", "page");
const searchButton = page.getByRole("button", { name: "Open zoek- en opdrachtenmenu" });
await expect(searchButton).toBeVisible();
await searchButton.click();
await expect(page.getByRole("dialog", { name: "Opdrachtenpalet" })).toBeVisible();
await expect(page.getByRole("combobox", { name: "Zoek pagina's, projectitems en opdrachten" })).toBeFocused();
});
+102
View File
@@ -0,0 +1,102 @@
import { expect, test, type Page } from "@playwright/test";
import type { AutoModeState } from "../src/shared/api/autonomyApi";
const stateFor = (project: string): AutoModeState => ({
version: 1, enabled: false, status: "off", autoBrainstorm: true, maxBrainstormIterations: 5,
maxConsecutiveFailures: 3, leaseMinutes: 30, maxWorkItemsPerRun: 8, maxRunMinutes: 45,
runs: [], events: [], updatedAt: new Date().toISOString(), updatedBy: "test",
roadmap: { id: project, title: `${project} roadmap`, sourceMarkdown: `## ${project}\n- [ ] ${project} task`, status: "active", currentPhaseIndex: 0, importedAt: new Date().toISOString(), phases: [
{ id: `${project}-phase`, title: project, objective: "Finish work", status: "active", brainstormIterations: 0, brainstormConverged: false, validationFailures: 0,
items: [{ id: `${project}-task`, title: `${project} task`, status: "pending", attempts: 1 }] },
] },
});
const envelope = (data: unknown) => ({ data, meta: { requestId: "platform-state", timestamp: new Date().toISOString() } });
const navigateWithoutReload = async (page: Page, path: string) => page.evaluate((next) => {
window.history.pushState({}, "", next);
window.dispatchEvent(new PopStateEvent("popstate"));
}, path);
const openAutoAdvancedSettings = async (page: Page) => {
const advanced = page.getByText("Autonomiebeleid en geavanceerde instellingen", { exact: true });
if (await advanced.locator("xpath=..").getAttribute("open") === null) await advanced.click();
};
const openManualRoadmapImport = async (page: Page) => {
await openAutoAdvancedSettings(page);
await expect(page.getByText("Roadmap handmatig importeren", { exact: true })).toBeVisible();
};
const openAutoAdvancedLimits = async (page: Page) => {
await openAutoAdvancedSettings(page);
const advancedLimits = page.getByText("Geavanceerde grenzen", { exact: true });
if (await advancedLimits.locator("xpath=..").getAttribute("open") === null) await advancedLimits.click();
};
test("project switches never carry an instruction into another project", async ({ page }) => {
await page.goto("/projects/widefrog/workspace");
await page.getByLabel("Jouw opdracht", { exact: true }).fill("Only change WideFrog onboarding");
await navigateWithoutReload(page, "/projects/nebula/workspace");
await expect(page.getByLabel("Jouw opdracht", { exact: true })).toHaveValue("");
await expect(page.getByRole("button", { name: "Aan ProjectBrain geven" })).toBeDisabled();
});
test("project switches discard old roadmap drafts and delayed responses", async ({ page }) => {
let delayWidefrog = false;
let releaseResponse!: () => void;
const responseGate = new Promise<void>((resolve) => { releaseResponse = resolve; });
let requestStarted!: () => void;
const started = new Promise<void>((resolve) => { requestStarted = resolve; });
await page.route("**/api/projects/*/autonomy", async (route) => {
const project = new URL(route.request().url()).pathname.split("/")[3];
if (project === "widefrog" && delayWidefrog) { requestStarted(); await responseGate; }
await route.fulfill({ json: envelope(stateFor(project)) });
});
await page.goto("/projects/widefrog/auto");
await openManualRoadmapImport(page);
await expect(page.getByLabel("Roadmap", { exact: true })).toContainText("widefrog");
await page.getByLabel("Roadmap", { exact: true }).fill("## Private draft\n- Only WideFrog");
delayWidefrog = true;
await page.getByRole("button", { name: "Vernieuwen" }).click();
await started;
const nebulaResponse = page.waitForResponse((response) => response.url().endsWith("/projects/nebula/autonomy"));
await navigateWithoutReload(page, "/projects/nebula/auto");
await nebulaResponse;
await openManualRoadmapImport(page);
await expect(page.getByLabel("Titel", { exact: true })).toHaveValue("nebula roadmap");
await expect(page.getByLabel("Roadmap", { exact: true })).toHaveValue("## nebula\n- [ ] nebula task");
const oldResponse = page.waitForResponse((response) => response.url().endsWith("/projects/widefrog/autonomy"));
releaseResponse();
await oldResponse;
await expect(page.getByLabel("Titel", { exact: true })).toHaveValue("nebula roadmap");
await expect(page.getByLabel("Roadmap", { exact: true })).toHaveValue("## nebula\n- [ ] nebula task");
await expect(page.getByText("widefrog task", { exact: true })).toHaveCount(0);
});
test("reimporting an unchanged roadmap preserves work and toggling Auto preserves unsaved policy", async ({ page }) => {
let state = stateFor("widefrog");
let imports = 0;
await page.route("**/api/projects/widefrog/autonomy**", async (route) => {
const request = route.request();
if (request.method() === "POST" && request.url().endsWith("/roadmap")) imports += 1;
if (request.method() === "PATCH" && request.url().endsWith("/autonomy")) {
state = { ...state, ...request.postDataJSON() };
}
await route.fulfill({ json: envelope(state) });
});
await page.goto("/projects/widefrog/auto");
await openManualRoadmapImport(page);
await page.getByRole("button", { name: "Roadmap bijwerken" }).click();
await expect(page.getByText("Deze roadmap is al opgeslagen. De voortgang blijft behouden.")).toBeVisible();
expect(imports).toBe(0);
await openAutoAdvancedLimits(page);
await page.getByLabel("Werkitems per run").fill("12");
await page.getByRole("switch", { name: "Auto", exact: true }).click();
await expect(page.getByRole("switch", { name: "Auto", exact: true })).toBeChecked();
await expect(page.getByLabel("Werkitems per run")).toHaveValue("12");
await expect(page.getByRole("button", { name: "Beleid opslaan" })).toBeEnabled();
});
+14
View File
@@ -0,0 +1,14 @@
import { expect, test } from "@playwright/test";
test("project search and sort survive reload and clearing preserves sorting", async ({ page }) => {
await page.goto("/projects?q=widefrog&sort=name");
await expect(page.getByRole("textbox", { name: "Projecten zoeken", exact: true })).toHaveValue("widefrog");
await expect(page.getByRole("button", { name: "Naam", exact: true })).toHaveAttribute("aria-pressed", "true");
await page.getByRole("textbox", { name: "Projecten zoeken", exact: true }).fill("no-project-matches-this");
await page.reload();
await expect(page.getByRole("textbox", { name: "Projecten zoeken", exact: true })).toHaveValue("no-project-matches-this");
await page.getByRole("button", { name: "Selectie wissen", exact: true }).click();
await expect(page.getByRole("textbox", { name: "Projecten zoeken", exact: true })).toHaveValue("");
await expect(page.getByRole("button", { name: "Naam", exact: true })).toHaveAttribute("aria-pressed", "true");
await expect(page).toHaveURL(/sort=name/);
});
+24
View File
@@ -0,0 +1,24 @@
import { expect, test } from "@playwright/test";
test("create, filter, complete and reload a local task", async ({ page }) => {
const errors: string[] = [];
page.on("pageerror", (error) => errors.push(error.message));
await page.goto("/projects/widefrog/tasks");
await expect(page.getByRole("heading", { name: "Tasks", exact: true })).toBeVisible();
await page.getByRole("button", { name: "New Task", exact: true }).click();
await page.getByLabel("Task Title").fill("Task queue browser regression");
await page.getByLabel("Linked Feature").click();
await page.getByRole("option").first().click();
await page.getByRole("button", { name: "Create Task", exact: true }).click();
await page.getByRole("textbox", { name: "Search tasks", exact: true }).fill("Task queue browser regression");
await expect(page.getByText("Task queue browser regression", { exact: true })).toBeVisible();
await page.getByRole("checkbox", { name: "Mark Task queue browser regression done", exact: true }).check();
await page.reload();
await expect(page.getByRole("checkbox", { name: "Mark Task queue browser regression done", exact: true })).toBeChecked();
await expect(page.getByRole("textbox", { name: "Search tasks", exact: true })).toHaveValue("Task queue browser regression");
await page.getByRole("textbox", { name: "Search tasks", exact: true }).fill("no-such-task-xyz");
await expect(page.getByText("No matching tasks", { exact: true })).toBeVisible();
await page.getByRole("button", { name: "Clear filters" }).click();
await expect(page.getByText("Task queue browser regression", { exact: true })).toBeVisible();
expect(errors).toEqual([]);
});