From b1f2105a2e1bf9e61b2673c86ffa545deed2f875 Mon Sep 17 00:00:00 2001 From: Claude Fable 5 Date: Thu, 9 Jul 2026 17:01:41 +0200 Subject: [PATCH] Modernize stale editor e2e pack to live-collab semantics MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit editor.spec.ts still asserted the M2 REST-autosave UI ("saved" indicator, save-failure retry) that #36/#38 retired for live collaboration — the pack (not part of CI) has been failing locally ever since. The tests now assert the collab connection status via the language-neutral data-status attribute: connected on entry, honest "offline" while disconnected, and offline edits reaching the server after reconnect (proven across a reload). Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01PGdhRiwU1WRL4XxJfZYipY --- apps/web/e2e/editor.spec.ts | 38 ++++++++++++++++++++++++++----------- 1 file changed, 27 insertions(+), 11 deletions(-) diff --git a/apps/web/e2e/editor.spec.ts b/apps/web/e2e/editor.spec.ts index 01d08de..f63a0d4 100644 --- a/apps/web/e2e/editor.spec.ts +++ b/apps/web/e2e/editor.spec.ts @@ -3,10 +3,13 @@ import { expect, test } from '@playwright/test'; import { contextForUser } from './helpers'; /** - * TipTap editor pack (issue #25). Runs against the local dev stack (api + - * web); no Mailpit needed. Creates its own page per test via the api (the - * sidebar/"new page" flow is issue #26) and navigates straight to - * `/p/:pondSlug/:pageSlug`. + * TipTap editor pack (issue #25, modernized for live collaboration after + * #36/#38 — the REST autosave and its "saved" indicator are retired; the + * editor syncs over the collab server and reports a connection status). + * Runs against the local dev stack (api + web + collab); no Mailpit needed. + * Creates its own page per test via the api and navigates straight to + * `/p/:pondSlug/:pageSlug`. Status assertions use the language-neutral + * `data-status` attribute — the UI language follows the user's locale. */ const BASE_URL = process.env.E2E_BASE_URL ?? 'http://localhost:5173'; @@ -31,11 +34,14 @@ test('typing persists across reload and undo/redo work', async ({ browser }) => await page.goto(`/p/${pondSlug}/${pageSlug}`); await page.getByRole('button', { name: /edit|bearbeiten/i }).click(); + const status = page.locator('.editor-connection'); + await expect(status).toHaveAttribute('data-status', 'connected', { timeout: 10000 }); + const content = page.locator('.ProseMirror'); await expect(content).toHaveAttribute('contenteditable', 'true'); await content.click(); await page.keyboard.type('Hello editor'); - await expect(page.getByRole('status')).toHaveText(/saved|gespeichert/i, { timeout: 10000 }); + await expect(content).toContainText('Hello editor'); await page.reload(); await page.getByRole('button', { name: /edit|bearbeiten/i }).click(); @@ -75,23 +81,33 @@ test('edit mode hides the sidebar; leaving edit mode restores it', async ({ brow await context.close(); }); -test('a save failure shows a truthful error and retries once online again', async ({ browser }) => { +test('going offline is reported honestly and edits sync after reconnect', async ({ browser }) => { const context = await contextForUser(browser, BASE_URL, 'fixture-user'); const { pondSlug, pageSlug } = await createPage(context, `E2E Offline ${Date.now()}`); const page = await context.newPage(); await page.goto(`/p/${pondSlug}/${pageSlug}`); await page.getByRole('button', { name: /edit|bearbeiten/i }).click(); - await page.locator('.ProseMirror').click(); + const status = page.locator('.editor-connection'); + await expect(status).toHaveAttribute('data-status', 'connected', { timeout: 10000 }); + + const content = page.locator('.ProseMirror'); + await content.click(); + + // Editing keeps working offline; the status tells the truth about it. await context.setOffline(true); await page.keyboard.type('offline text'); - await expect(page.getByRole('status')).toHaveText(/failed|retrying|fehlgeschlagen/i, { - timeout: 10000, - }); + await expect(content).toContainText('offline text'); + await expect(status).toHaveAttribute('data-status', 'offline', { timeout: 10000 }); + // Reconnect: the provider resumes and the offline edit reaches the server — + // proven by a reload, which resolves the page against the live document. await context.setOffline(false); - await expect(page.getByRole('status')).toHaveText(/saved|gespeichert/i, { timeout: 10000 }); + await expect(status).toHaveAttribute('data-status', 'connected', { timeout: 20000 }); + await page.reload(); + await page.getByRole('button', { name: /edit|bearbeiten/i }).click(); + await expect(content).toContainText('offline text'); await context.close(); });