From d429b479ce131dd02efa8876cc9e6e121bc62803 Mon Sep 17 00:00:00 2001 From: zacharias-ona Date: Sat, 18 Apr 2026 07:24:02 +0000 Subject: [PATCH 1/2] test: add E2E tests for editor auto-save persistence (#221) Co-authored-by: Ona --- e2e/editor-auto-save.spec.ts | 77 ++++++++++++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100644 e2e/editor-auto-save.spec.ts diff --git a/e2e/editor-auto-save.spec.ts b/e2e/editor-auto-save.spec.ts new file mode 100644 index 00000000..4088beaf --- /dev/null +++ b/e2e/editor-auto-save.spec.ts @@ -0,0 +1,77 @@ +import { test, expect } from "./fixtures/auth"; +import { navigateToEditorPage } from "./fixtures/editor-helpers"; + +/** + * Wait for a successful Supabase PATCH to /rest/v1/pages (content auto-save). + * Resolves once the response arrives with a 2xx status. + */ +function waitForContentSave(page: import("@playwright/test").Page) { + return page.waitForResponse( + (resp) => + resp.url().includes("/rest/v1/pages") && + resp.request().method() === "PATCH" && + resp.status() >= 200 && + resp.status() < 300, + { timeout: 10_000 } + ); +} + +test.describe("Editor auto-save", () => { + test("content typed in the editor persists after page reload", async ({ + authenticatedPage: page, + }) => { + await navigateToEditorPage(page); + + const editor = page.locator('[contenteditable="true"]'); + await expect(editor).toBeVisible({ timeout: 10_000 }); + + // Use a unique string so we can verify it survives the reload + const uniqueText = `autosave-test-${Date.now()}`; + + // Type content into the editor + await editor.click(); + await page.keyboard.press("End"); + await page.keyboard.press("Enter"); + await page.keyboard.type(uniqueText); + + // Wait for the debounced auto-save PATCH to complete + const saveResponse = waitForContentSave(page); + await saveResponse; + + // Reload the page + await page.reload({ waitUntil: "domcontentloaded" }); + + // Wait for the editor to re-render with persisted content + const reloadedEditor = page.locator('[contenteditable="true"]'); + await expect(reloadedEditor).toBeVisible({ timeout: 10_000 }); + + // Verify the typed content survived the reload + await expect(reloadedEditor).toContainText(uniqueText, { + timeout: 10_000, + }); + }); + + test("save status indicator shows Saving and Saved after editing", async ({ + authenticatedPage: page, + }) => { + await navigateToEditorPage(page); + + const editor = page.locator('[contenteditable="true"]'); + await expect(editor).toBeVisible({ timeout: 10_000 }); + + // The save status container is below the editor + const saveIndicator = page.locator("main .text-muted-foreground").last(); + + // Type content to trigger auto-save + await editor.click(); + await page.keyboard.press("End"); + await page.keyboard.press("Enter"); + await page.keyboard.type("indicator-test"); + + // "Saving..." should appear immediately after the editor state changes + await expect(saveIndicator).toContainText("Saving", { timeout: 5_000 }); + + // After the debounced save completes, "Saved" should appear + await expect(saveIndicator).toContainText("Saved", { timeout: 10_000 }); + }); +}); From 82152a39fbc6ea5b52888cce7e37c27de7dc4455 Mon Sep 17 00:00:00 2001 From: zacharias-ona Date: Sat, 18 Apr 2026 07:33:48 +0000 Subject: [PATCH 2/2] fix: move waitForResponse listener before typing to prevent race condition The waitForContentSave promise was created after typing finished. If the 500ms debounce fired before Playwright registered the listener, the PATCH response would be missed causing a flaky 10s timeout. Matches the pattern used in e2e/search.spec.ts. Co-authored-by: Ona --- e2e/editor-auto-save.spec.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/e2e/editor-auto-save.spec.ts b/e2e/editor-auto-save.spec.ts index 4088beaf..f6ee5f60 100644 --- a/e2e/editor-auto-save.spec.ts +++ b/e2e/editor-auto-save.spec.ts @@ -28,6 +28,10 @@ test.describe("Editor auto-save", () => { // Use a unique string so we can verify it survives the reload const uniqueText = `autosave-test-${Date.now()}`; + // Register the response listener BEFORE typing so we never miss the + // PATCH response if the debounce fires quickly. + const saveResponse = waitForContentSave(page); + // Type content into the editor await editor.click(); await page.keyboard.press("End"); @@ -35,7 +39,6 @@ test.describe("Editor auto-save", () => { await page.keyboard.type(uniqueText); // Wait for the debounced auto-save PATCH to complete - const saveResponse = waitForContentSave(page); await saveResponse; // Reload the page