Files
govoplan-core/webui/conformance/tests/campaign-delivery-policy.spec.ts
T

108 lines
6.2 KiB
TypeScript

import { expect, test, type Page } from "@playwright/test";
async function fixture(page: Page, options: { language?: string; scope?: string; readonly?: boolean; ceiling?: number; reject?: number; hold?: boolean } = {}) {
let value: number | null = null;
let revision = 1;
let reject = options.reject;
let release: (() => void) | undefined;
let rejectRead = false;
const writes: any[] = [];
const errors: string[] = [];
const inherited = options.ceiling ?? 25;
const maximum = options.ceiling ?? 500;
page.on("pageerror", (cause) => errors.push(cause.message));
const state = () => ({ scope: options.scope ?? "system", synchronous_send_max_recipients: value,
revision: String(revision).padStart(64, "0"), max_configurable_recipients: maximum,
effective_max_recipients: value ?? inherited, inherited_max_recipients: inherited,
absolute_max_recipients: 500, deployment_ceiling_explicit: Boolean(options.ceiling), deployment_max_recipients: maximum });
await page.route((url) => url.pathname.startsWith("/api/"), async (route) => {
if (!route.request().url().includes("/campaigns/settings/delivery-policy/")) return route.fulfill({ status: 404, json: { detail: "Not found" } });
if (route.request().method() === "GET") return route.fulfill(rejectRead ? { status: 503, json: { detail: "Fixture reload unavailable" } } : { json: state() });
expect(route.request().method()).toBe("PUT");
const body = route.request().postDataJSON(); writes.push(body);
if (options.hold) await new Promise<void>((resolve) => { release = resolve; });
if (reject) {
const status = reject; reject = undefined;
return route.fulfill({ status, json: { detail: "Fixture policy save rejected; draft was not saved" } });
}
value = body.synchronous_send_max_recipients; revision += 1;
return route.fulfill({ json: state() });
});
await page.goto(`/?campaign-delivery-policy&language=${options.language ?? "en"}&scope=${options.scope ?? "system"}${options.readonly ? "&readonly" : ""}`);
await expect(page.getByRole("spinbutton")).toHaveValue(String(inherited));
return { writes, errors, release: () => release?.(), failRead: () => { rejectRead = true; } };
}
for (const language of ["en", "de"]) {
test(`administrator deliberately raises implicit 25 to 200 and reloads saved policy in ${language}`, async ({ page }) => {
const f = await fixture(page, { language });
const save = page.getByRole("button", { name: language === "de" ? "Speichern" : "Save", exact: true });
await expect(save).toBeDisabled();
await page.getByRole("checkbox").press("Space");
await page.getByRole("spinbutton").fill("200");
await save.click();
await expect(save).toBeDisabled();
expect(f.writes).toEqual([{ synchronous_send_max_recipients: 200, expected_revision: "1".padStart(64, "0") }]);
await page.getByRole("button", { name: language === "de" ? "Gespeicherte Versandrichtlinie neu laden" : "Reload saved delivery policy", exact: true }).click();
await expect(page.getByRole("spinbutton")).toHaveValue("200");
await expect(page.getByRole("checkbox")).not.toBeChecked();
expect(f.errors).toEqual([]);
});
}
for (const status of [422, 409]) {
test(`policy save ${status} retains draft for an explicit retry`, async ({ page }) => {
const f = await fixture(page, { reject: status });
await page.getByRole("checkbox").press("Space");
await page.getByRole("spinbutton").fill("200");
await page.getByRole("button", { name: "Save", exact: true }).click();
await expect(page.getByRole("alert").filter({ hasText: "Fixture policy save rejected" })).toBeVisible();
await expect(page.getByRole("spinbutton")).toHaveValue("200");
await page.getByRole("button", { name: "Save", exact: true }).click();
await expect(page.getByRole("button", { name: "Save", exact: true })).toBeDisabled();
expect(f.writes).toHaveLength(2);
expect(f.writes[1].synchronous_send_max_recipients).toBe(200);
});
}
test("explicit ceiling prevents invalid save and clearing override submits inheritance", async ({ page }) => {
const f = await fixture(page, { scope: "tenant", ceiling: 40 });
await page.getByRole("checkbox").press("Space");
await page.getByRole("spinbutton").fill("41");
await expect(page.getByRole("button", { name: "Save", exact: true })).toBeDisabled();
expect(f.writes).toHaveLength(0);
await page.getByRole("spinbutton").fill("0");
await page.getByRole("button", { name: "Save", exact: true }).click();
await expect(page.getByRole("button", { name: "Save", exact: true })).toBeDisabled();
await page.getByRole("checkbox").press("Space");
await page.getByRole("button", { name: "Save", exact: true }).click();
await expect(page.getByRole("button", { name: "Save", exact: true })).toBeDisabled();
expect(f.writes.map((body) => body.synchronous_send_max_recipients)).toEqual([0, null]);
});
test("read-only policy cannot be edited or saved", async ({ page }) => {
const f = await fixture(page, { readonly: true });
await expect(page.getByRole("checkbox")).toBeDisabled();
await expect(page.getByRole("spinbutton")).toBeDisabled();
await expect(page.getByRole("button", { name: "Save", exact: true })).toBeDisabled();
expect(f.writes).toHaveLength(0);
});
test("pending policy save disables edits and reload; failed refresh preserves acknowledged value", async ({ page }) => {
const f = await fixture(page, { hold: true });
await page.getByRole("checkbox").press("Space");
await page.getByRole("spinbutton").fill("200");
await page.getByRole("button", { name: "Save", exact: true }).click();
await expect.poll(() => f.writes.length).toBe(1);
await expect(page.getByRole("spinbutton")).toBeDisabled();
await expect(page.getByRole("button", { name: "Reload saved delivery policy", exact: true })).toBeDisabled();
f.release();
await expect(page.getByRole("spinbutton")).toBeEnabled();
f.failRead();
await page.getByRole("button", { name: "Reload saved delivery policy", exact: true }).click();
await expect(page.getByRole("alert").filter({ hasText: "Fixture reload unavailable" })).toBeVisible();
await expect(page.getByRole("spinbutton")).toHaveValue("200");
await expect(page.getByRole("button", { name: "Save", exact: true })).toBeDisabled();
expect(f.writes).toHaveLength(1);
});