Publish curated Knowledge Hub source from 2ed833f290b439a7bc91e5fd19b042c269e3bee9
This commit is contained in:
1 parent
10f2edcd29
commit
59f15b2d08
218 files changed
+23995
-1849
No files matched your search
@@ -1,4 +1,6 @@
|
||||
import { execFileSync } from "node:child_process";
|
||||
import { mkdirSync, rmSync, writeFileSync } from "node:fs";
|
||||
import path from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import AxeBuilder from "@axe-core/playwright";
|
||||
import { expect, test, type Page } from "@playwright/test";
|
||||
import { authenticatedStatePath } from "../global-setup";
|
||||
@@ -17,35 +19,52 @@ import { expectAuthenticatedShell } from "./support/v2";
|
||||
|
||||
test.use({ storageState: authenticatedStatePath });
|
||||
|
||||
// Keep the ordinary local-stack name as the zero-configuration default, while
|
||||
// allowing clean-room/release verification to target its isolated app container.
|
||||
// Without this override the fixture is written into a different healthy stack and
|
||||
// the preflight correctly rejects the path as absent from its own imports mount.
|
||||
const CONTAINER_NAME =
|
||||
process.env.KNOWLEDGE_HUB_E2E_APP_CONTAINER ?? "itworx-knowledge-hub-app-1";
|
||||
const IMPORTS_ROOT = "/var/lib/knowledge-hub/imports";
|
||||
const testDirectory = path.dirname(fileURLToPath(import.meta.url));
|
||||
const expectedImportsRoot = path.resolve(testDirectory, "../../../.local-appdata/e2e-imports");
|
||||
const seededFixtureDirectories = new Set<string>();
|
||||
|
||||
function seedLeafWikiFixture(relativeDir: string, title: string) {
|
||||
// Writes a minimal, synthetic Markdown fixture directly into the running app
|
||||
// container's imports volume (never a real LeafWiki source — CLAUDE.md's
|
||||
// read-only-source rule only ever applies to a real snapshot, which this is not).
|
||||
// Uses `docker exec` from the test runner rather than a Playwright API call, since
|
||||
// the import endpoint deliberately only ever reads a server-side mounted path, by
|
||||
// design (docs/17-leafwiki-migration.md) — there is no upload endpoint to seed it
|
||||
// through.
|
||||
execFileSync("docker", [
|
||||
"exec",
|
||||
CONTAINER_NAME,
|
||||
"sh",
|
||||
"-c",
|
||||
`mkdir -p '${IMPORTS_ROOT}/${relativeDir}' && printf '# ${title}\\n\\nDit is een synthetische testpagina voor de V2-M7 importproef.\\n' > '${IMPORTS_ROOT}/${relativeDir}/welkom.md'`
|
||||
]);
|
||||
// The import API reads its configured server-side fixture mount. Fail closed unless
|
||||
// both the API and this test are explicitly pointed at this repository-local test
|
||||
// directory; never discover or fall back to a Docker container, which could be a
|
||||
// production deployment.
|
||||
const configuredImportsRoot = process.env.KNOWLEDGE_HUB_E2E_IMPORTS_ROOT;
|
||||
if (!configuredImportsRoot || path.resolve(configuredImportsRoot) !== expectedImportsRoot) {
|
||||
throw new Error(
|
||||
"Set KNOWLEDGE_HUB_E2E_IMPORTS_ROOT to this checkout's absolute .local-appdata/e2e-imports path before running the LeafWiki import browser test."
|
||||
);
|
||||
}
|
||||
|
||||
const fixtureDirectory = path.resolve(expectedImportsRoot, relativeDir);
|
||||
if (!fixtureDirectory.startsWith(expectedImportsRoot + path.sep)) {
|
||||
throw new Error("The synthetic LeafWiki fixture must stay inside .local-appdata/e2e-imports.");
|
||||
}
|
||||
|
||||
mkdirSync(fixtureDirectory, { recursive: true });
|
||||
seededFixtureDirectories.add(fixtureDirectory);
|
||||
writeFileSync(
|
||||
path.join(fixtureDirectory, "welkom.md"),
|
||||
"# " + title + "\n\nDit is een synthetische testpagina voor de V2-M7 importproef.\n",
|
||||
{ encoding: "utf8", flag: "wx" }
|
||||
);
|
||||
}
|
||||
|
||||
test.afterEach(() => {
|
||||
for (const fixtureDirectory of seededFixtureDirectories) {
|
||||
if (
|
||||
path.dirname(fixtureDirectory) === expectedImportsRoot
|
||||
&& path.basename(fixtureDirectory).startsWith("e2e-leafwiki-")
|
||||
) {
|
||||
rmSync(fixtureDirectory, { recursive: true, force: true });
|
||||
}
|
||||
seededFixtureDirectories.delete(fixtureDirectory);
|
||||
}
|
||||
});
|
||||
|
||||
test.describe("V2-M7 admin actions: recovery tokens, trash purge, LeafWiki import", () => {
|
||||
test("an administrator can issue a one-time recovery token for another user from the admin UI", async ({ page }) => {
|
||||
const targetEmail = `e2e-recovery-target-${Date.now()}@itworx.e2e`;
|
||||
await page.goto("/admin");
|
||||
await page.goto("/admin/users");
|
||||
await expectAuthenticatedShell(page);
|
||||
await expect(page.getByRole("heading", { name: "Gebruikers", exact: true })).toBeVisible();
|
||||
|
||||
@@ -72,12 +91,19 @@ test.describe("V2-M7 admin actions: recovery tokens, trash purge, LeafWiki impor
|
||||
});
|
||||
|
||||
test("an administrator can trigger a trash purge from the admin UI, and it honestly reports nothing eligible yet", async ({ page }) => {
|
||||
await page.goto("/admin");
|
||||
await page.goto("/admin/backups");
|
||||
await expectAuthenticatedShell(page);
|
||||
await expect(page.getByRole("heading", { name: "Prullenbak" })).toBeVisible();
|
||||
|
||||
await page.getByRole("button", { name: "Prullenbak legen" }).click();
|
||||
|
||||
const purgeConfirmation = page.getByRole("group", { name: "Definitieve verwijdering bevestigen" });
|
||||
await expect(purgeConfirmation).toBeVisible();
|
||||
await expect(
|
||||
purgeConfirmation.getByText("Dit verwijdert definitief alleen pagina's waarvan de bewaartermijn is verstreken. Deze actie kan niet ongedaan worden gemaakt.")
|
||||
).toBeVisible();
|
||||
await purgeConfirmation.getByRole("button", { name: "Bevestig definitieve verwijdering" }).click();
|
||||
|
||||
// PurgeTrashUseCase only removes pages whose retention window has already
|
||||
// elapsed — a fresh test run has nothing eligible, and the UI must say so
|
||||
// honestly rather than implying something was deleted.
|
||||
@@ -100,7 +126,7 @@ test.describe("V2-M7 admin actions: recovery tokens, trash purge, LeafWiki impor
|
||||
const pageTitle = `Welkom bij LeafWiki ${relativeDir}`;
|
||||
seedLeafWikiFixture(relativeDir, pageTitle);
|
||||
|
||||
await page.goto("/admin");
|
||||
await page.goto("/admin/knowledge");
|
||||
await expectAuthenticatedShell(page);
|
||||
await expect(page.getByRole("heading", { name: "LeafWiki-import" })).toBeVisible();
|
||||
|
||||
@@ -184,9 +210,10 @@ async function findImportedPage(page: Page, title: string) {
|
||||
// finished, which a bare locator check on the title alone could not distinguish from
|
||||
// "the request has not come back yet".
|
||||
await expect(
|
||||
page.getByRole("list", { name: "Zoekresultaten" }).or(page.getByText("Geen resultaten gevonden.")),
|
||||
page.getByRole("region", { name: "Zoekresultaten", exact: true }),
|
||||
).toBeVisible();
|
||||
return page.getByRole("button", { name: new RegExp(escapeForRegExp(title)) });
|
||||
return page.getByRole("region", { name: "Zoekresultaten", exact: true })
|
||||
.getByRole("link", { name: new RegExp(escapeForRegExp(title)) });
|
||||
}
|
||||
|
||||
function escapeForRegExp(value: string) {
|
||||
|
||||
Reference in new issue
Block a user