import { expect, test, type Page } from "@playwright/test"; async function install(page: Page) { const errors: string[] = []; page.on("pageerror", error => { errors.push(error.message); console.error("Attachment fixture:", error.message); }); let revision = 1; const zip = { enabled: true, archives: [{ id: "zip-1", name: "recipient.zip", method: "zip_standard", password_enabled: true }] }; let raw = { campaign: { name: "Fixture" }, template: { subject: "Fixture", text: "" }, server: {}, attachments: { base_paths: [{ id: "source-1", name: "Source", path: ".", source: "managed:user:user-1", allow_individual: true }], global: [], zip } }; const writes: Record[] = []; const version = () => ({ id: "version-attachments", campaign_id: "campaign-attachments", version_number: 1, edit_revision: revision, strong_etag: `"version-attachments:${revision}"`, editor_state: {}, current_flow: "manual", current_step: "files", workflow_state: "editing", is_complete: false, updated_at: "2026-09-07T10:00:00Z", raw_json: raw }); await page.route((url) => url.pathname.startsWith("/api/"), async route => { const request = route.request(); const url = new URL(request.url()); if (request.method() === "GET") { if (url.pathname.endsWith("/workspace/delta")) return route.fulfill({ json: { campaign: { id: "campaign-attachments", name: "Fixture", current_version_id: "version-attachments", status: "draft" }, versions: [version()], current_version: version(), summary: null, deleted: [], full: true, has_more: false, watermark: `w${revision}` } }); if (url.pathname === "/api/v1/files/spaces") return route.fulfill({ json: { spaces: [{ id: "space-1", label: "My files", space_type: "managed", owner_type: "user", owner_id: "user-1" }] } }); if (url.pathname === "/api/v1/files/folders") return route.fulfill({ json: { folders: [], next_cursor: null } }); if (url.pathname === "/api/v1/files") return route.fulfill({ json: { files: [], next_cursor: null } }); if (url.pathname === "/api/v1/files/delta") return route.fulfill({ json: { files: [], folders: [{ id: "folder-1", owner_type: "user", owner_id: "user-1", path: "letters", name: "letters" }], deleted: [], full: true, has_more: false, watermark: "files-w1" } }); if (url.pathname.endsWith("/versions/version-attachments")) return route.fulfill({ json: version() }); if (url.pathname.endsWith("/archive-encryption-policy")) return route.fulfill({ json: { available: true, allowed_password_encryption_methods: ["aes"], allowed_password_delivery_channels: ["separate_mail"], policy_hash: "fixture", source_path: [], diagnostics: [], reason: "Legacy ZipCrypto is blocked by policy.", legacy_label: "Legacy ZipCrypto" } }); return route.fulfill({ json: {} }); } if (request.method() === "POST" && url.pathname.endsWith("/autosave")) { const body = request.postDataJSON(); writes.push(body); raw = body.campaign_json; revision++; return route.fulfill({ json: version() }); } return route.abort(); }); await page.goto("/?campaign-attachments"); await expect(page.locator("#campaign-attachment-sources .chooser-display-input")).toBeEnabled(); return { writes, errors, zip }; } test("actual Files chooser opens repeatedly from attachment path by click and keyboard", async ({ page }) => { const fixture = await install(page); const input = page.locator("#campaign-attachment-sources .chooser-display-input"); for (const action of ["click", "Enter", "Space", "click"]) { if (action === "click") await input.click(); else { await input.focus(); await page.keyboard.press(action); } await expect(page.getByRole("dialog")).toBeVisible(); await expect(page.getByRole("dialog").getByRole("button", { name: /Use.*folder|Select.*folder/i })).toBeEnabled(); await page.keyboard.press("Escape"); await expect(page.getByRole("dialog")).toHaveCount(0); } expect(fixture.writes).toHaveLength(0); expect(fixture.errors).toEqual([]); }); test("temporarily unavailable Files capability does not silently turn managed paths into text edits", async ({ page }) => { const fixture = await install(page); await page.getByRole("button", { name: "Toggle Files capability" }).click(); await expect(page.locator("#campaign-attachment-sources .chooser-display-input")).toBeDisabled(); await expect(page.getByText(/The Files browser is currently unavailable/).first()).toBeVisible(); await page.getByRole("button", { name: "Toggle Files capability" }).click(); await page.locator("#campaign-attachment-sources .chooser-display-input").click(); await expect(page.getByRole("dialog")).toBeVisible(); await page.keyboard.press("Escape"); expect(fixture.writes).toHaveLength(0); expect(fixture.errors).toEqual([]); }); test("attachment source corrections save without changing or reauthorizing legacy ZIP configuration", async ({ page }) => { const fixture = await install(page); await page.locator('#campaign-attachment-sources input[placeholder="Campaign files"]').fill("Updated source name"); await page.getByRole("button", { name: "Save", exact: true }).click(); await expect.poll(() => fixture.writes.length).toBe(1); expect(fixture.writes[0].campaign_json.attachments.zip).toEqual(fixture.zip); expect(fixture.writes[0].campaign_json.attachments.base_paths[0].name).toBe("Updated source name"); await page.reload(); await expect(page.locator('#campaign-attachment-sources input[placeholder="Campaign files"]')).toHaveValue("Updated source name"); expect(fixture.errors).toEqual([]); });