Modernize stale editor e2e pack to live-collab semantics
All checks were successful
CD / Build and push images (push) Successful in 1m58s
CI / Lint, typecheck, test (push) Successful in 2m29s
CI / Auth e2e pack (push) Successful in 2m55s
CI / Build container images (push) Has been skipped
CD / Deploy to Test (push) Successful in 9s
CD / Smoke tests against Test (push) Successful in 1m11s
CD / Promote to Int (push) Successful in 11s

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 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PGdhRiwU1WRL4XxJfZYipY
This commit is contained in:
Claude Fable 5 2026-07-09 17:01:41 +02:00
parent 0c6494f209
commit b1f2105a2e

View File

@ -3,10 +3,13 @@ import { expect, test } from '@playwright/test';
import { contextForUser } from './helpers'; import { contextForUser } from './helpers';
/** /**
* TipTap editor pack (issue #25). Runs against the local dev stack (api + * TipTap editor pack (issue #25, modernized for live collaboration after
* web); no Mailpit needed. Creates its own page per test via the api (the * #36/#38 the REST autosave and its "saved" indicator are retired; the
* sidebar/"new page" flow is issue #26) and navigates straight to * editor syncs over the collab server and reports a connection status).
* `/p/:pondSlug/:pageSlug`. * 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'; 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.goto(`/p/${pondSlug}/${pageSlug}`);
await page.getByRole('button', { name: /edit|bearbeiten/i }).click(); 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'); const content = page.locator('.ProseMirror');
await expect(content).toHaveAttribute('contenteditable', 'true'); await expect(content).toHaveAttribute('contenteditable', 'true');
await content.click(); await content.click();
await page.keyboard.type('Hello editor'); 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.reload();
await page.getByRole('button', { name: /edit|bearbeiten/i }).click(); 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(); 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 context = await contextForUser(browser, BASE_URL, 'fixture-user');
const { pondSlug, pageSlug } = await createPage(context, `E2E Offline ${Date.now()}`); const { pondSlug, pageSlug } = await createPage(context, `E2E Offline ${Date.now()}`);
const page = await context.newPage(); const page = await context.newPage();
await page.goto(`/p/${pondSlug}/${pageSlug}`); await page.goto(`/p/${pondSlug}/${pageSlug}`);
await page.getByRole('button', { name: /edit|bearbeiten/i }).click(); 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 context.setOffline(true);
await page.keyboard.type('offline text'); await page.keyboard.type('offline text');
await expect(page.getByRole('status')).toHaveText(/failed|retrying|fehlgeschlagen/i, { await expect(content).toContainText('offline text');
timeout: 10000, 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 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(); await context.close();
}); });