Files
govoplan-core/webui/conformance/tests/campaign-recipient-order.spec.ts
T

120 lines
7.3 KiB
TypeScript

import { expect, test, type Page } from "@playwright/test";
async function install(page: Page, language = "en", rejectSave = false) {
let revision = 1;
let raw: any = null;
let rejected = false;
const writes: any[] = [];
const errors: string[] = [];
page.on("pageerror", (error) => errors.push(error.message));
const version = () => ({ id: "order-version", campaign_id: "order-campaign", version_number: 1,
edit_revision: revision, strong_etag: `"order-version:${revision}"`, editor_state: {},
workflow_state: "editing", current_flow: "manual", current_step: "recipients", is_complete: false,
updated_at: "2026-09-07T10:00:00Z", raw_json: raw });
await page.route((url) => url.pathname.startsWith("/api/"), async (route) => {
if (route.request().method() === "POST" && route.request().url().endsWith("/autosave")) {
const body = route.request().postDataJSON(); writes.push(body);
if (rejectSave && !rejected) {
rejected = true;
return route.fulfill({ status: 422, json: { detail: "Fixture policy rejected this save" } });
}
raw = body.campaign_json; revision += 1;
return route.fulfill({ json: version() });
}
if (route.request().method() === "GET") return route.fulfill({ json: version() });
return route.abort();
});
await page.goto(`/?campaign-recipient-order&language=${language}`);
await expect(page.getByTestId("recipient-order")).toContainText("alpha@example.test");
return { writes, errors, raw: () => raw };
}
async function moveLastToFirst(page: Page) {
const dialog = page.getByRole("dialog");
await dialog.getByRole("button", { name: "Move address up", exact: true }).nth(2).click();
await dialog.getByRole("button", { name: "Move address up", exact: true }).nth(1).click();
await expect(dialog.locator('input[type="email"]').first()).toHaveValue("zulu@example.test");
}
for (const language of ["en", "de"]) {
test(`individual recipient order survives dialog Save, reopen and actual campaign POST in ${language}`, async ({ page }) => {
const fixture = await install(page, language);
await page.getByRole("button", { name: "Edit individual addresses", exact: true }).click();
await moveLastToFirst(page);
await page.getByRole("dialog").getByRole("button", { name: language === "de" ? "Speichern" : "Save", exact: true }).click();
await expect(page.getByTestId("primary-address")).toHaveText("zulu@example.test");
await expect(page.getByTestId("recipient-dirty")).toHaveText("true");
expect(fixture.writes).toHaveLength(0); // Dialog Save applies the page draft, not a hidden network mutation.
await page.getByRole("button", { name: "Edit individual addresses", exact: true }).click();
await expect(page.getByRole("dialog").locator('input[type="email"]').first()).toHaveValue("zulu@example.test");
await page.getByRole("dialog").getByRole("button", { name: language === "de" ? "Abbrechen" : "Cancel", exact: true }).click();
await page.getByRole("button", { name: "Save campaign", exact: true }).click();
await expect(page.getByTestId("recipient-dirty")).toHaveText("false");
expect(fixture.writes[0].campaign_json.entries.inline[0].to.map((item: any) => item.email)).toEqual([
"zulu@example.test", "alpha@example.test", "beta@example.test"
]);
expect(fixture.raw().entries.inline[0].email).toBe("zulu@example.test");
await page.getByRole("button", { name: "Edit individual addresses", exact: true }).click();
await expect(page.getByRole("dialog").locator('input[type="email"]').first()).toHaveValue("zulu@example.test");
expect(fixture.errors).toEqual([]);
});
}
test("global header addresses preserve their explicitly selected order", async ({ page }) => {
const fixture = await install(page);
await page.getByRole("button", { name: "Edit global addresses", exact: true }).click();
await moveLastToFirst(page);
await page.getByRole("dialog").getByRole("button", { name: "Save", exact: true }).click();
await page.getByRole("button", { name: "Save campaign", exact: true }).click();
await expect(page.getByTestId("recipient-dirty")).toHaveText("false");
expect(fixture.raw().recipients.to.map((item: any) => item.email)).toEqual([
"zulu@example.test", "alpha@example.test", "beta@example.test"
]);
await page.getByRole("button", { name: "Edit global addresses", exact: true }).click();
await expect(page.getByRole("dialog").locator('input[type="email"]').first()).toHaveValue("zulu@example.test");
});
test("paste deduplicates without alphabetically rearranging existing or newly added addresses", async ({ page }) => {
const fixture = await install(page);
await page.getByRole("button", { name: "Edit individual addresses", exact: true }).click();
await moveLastToFirst(page);
await page.getByRole("dialog").locator(".recipient-address-category").evaluate((section) => {
const clipboard = new DataTransfer();
clipboard.setData("text/plain", "Zulu <ZULU@example.test>; Omega <omega@example.test>; Charlie <charlie@example.test>");
section.dispatchEvent(new ClipboardEvent("paste", { clipboardData: clipboard, bubbles: true, cancelable: true }));
});
await expect(page.getByRole("dialog").locator('input[type="email"]')).toHaveCount(5);
await page.getByRole("dialog").getByRole("button", { name: "Save", exact: true }).click();
await page.getByRole("button", { name: "Save campaign", exact: true }).click();
await expect(page.getByTestId("recipient-dirty")).toHaveText("false");
expect(fixture.raw().entries.inline[0].to.map((item: any) => item.email)).toEqual([
"zulu@example.test", "alpha@example.test", "beta@example.test", "omega@example.test", "charlie@example.test"
]);
});
test("failed campaign save retains the reordered dialog result for explicit retry", async ({ page }) => {
const fixture = await install(page, "en", true);
await page.getByRole("button", { name: "Edit individual addresses", exact: true }).click();
await moveLastToFirst(page);
await page.getByRole("dialog").getByRole("button", { name: "Save", exact: true }).click();
await page.getByRole("button", { name: "Save campaign", exact: true }).click();
await expect(page.getByTestId("recipient-error")).toContainText("Fixture policy rejected");
await expect(page.getByTestId("recipient-dirty")).toHaveText("true");
await page.getByRole("button", { name: "Edit individual addresses", exact: true }).click();
await expect(page.getByRole("dialog").locator('input[type="email"]').first()).toHaveValue("zulu@example.test");
await page.getByRole("dialog").getByRole("button", { name: "Cancel", exact: true }).click();
await page.getByRole("button", { name: "Save campaign", exact: true }).click();
await expect(page.getByTestId("recipient-dirty")).toHaveText("false");
expect(fixture.writes).toHaveLength(2);
expect(fixture.writes[1].campaign_json.entries.inline[0].to).toEqual(fixture.writes[0].campaign_json.entries.inline[0].to);
});
test("Cancel deliberately discards only the dialog's unconfirmed reorder", async ({ page }) => {
await install(page);
await page.getByRole("button", { name: "Edit individual addresses", exact: true }).click();
await moveLastToFirst(page);
await page.getByRole("dialog").getByRole("button", { name: "Cancel", exact: true }).click();
await expect(page.getByTestId("recipient-dirty")).toHaveText("false");
await expect(page.getByTestId("primary-address")).toHaveText("alpha@example.test");
});