This project uses Playwright for end-to-end testing. The E2E tests run in a real browser and test actual user workflows including UI interactions, persistence, and multi-page scenarios.
npm install)npm run test:e2e
npm run test:e2enow wraps Playwright withscripts/run-e2e.sh, which:
- Ensures Chromium (and its native dependencies) are installed locally.
- Automatically runs tests under
xvfb-runwhen no display is available (e.g., devcontainers/CI).- Uses the cached browser binaries in
node_modules/.cache/playwrightto avoid repeated downloads.
npm run test:e2e -- tests/e2e/multi-page-persistence.spec.ts
npm run test:e2e -- --headed
npm run test:e2e -- --ui
npm run test:e2e -- --debug
multi-page-persistence.spec.ts (New - for bug fixes)Tests the recently fixed persistence bugs:
Key tests:
Editing multiple pages: changes persist across all pagesReload page preserves edits from multiple sectionsSequential edits on same element do not create ghost editsBUG FIX: Changes on all pages are restored, not just last pageintegration-workflow.spec.tsGeneral workflow integration tests covering:
ui-accessibility.spec.tsTests for accessibility features and UI correctness.
multi-page-export.spec.tsEnd-to-end smoke that edits across the Debug and Document pages, verifying:
ChangesModule (via window.reviewDebug.operations()).ui-regression.spec.tsRegression tests to prevent breaking existing functionality.
Playwright is configured in playwright.config.ts:
- Base URL: http://127.0.0.1:5173 (served from the pre-rendered `example/_output`)
- Browser: Chromium (Firefox and WebKit can be enabled)
- Timeout: 45 seconds per test
- Reporters: HTML report
- Workers: 1 (single worker for stability)
The dev server is automatically started by Playwright before tests run.
The E2E tests simulate real user interactions:
/exampleExample:
// Find a paragraph
const para = page.locator('[data-review-type="Para"]').first();
// User double-clicks to open editor
await para.dblClick();
// Wait for editor modal
await page.waitForSelector('.review-editor-modal', { timeout: 3000 });
// Edit the content
const textarea = page.locator('.review-editor-content textarea').first();
const content = await textarea.inputValue();
await textarea.fill(content + ' [NEW TEXT]');
// Save changes
const saveBtn = page.locator('button:has-text("Save")').first();
await saveBtn.click();
// Verify changes persisted
const newText = await para.textContent();
expect(newText).toContain('[NEW TEXT]');
The test Sequential edits on same element do not create ghost edits verifies:
// Edit 1: Real change
await textarea.fill(content + ' [REAL_CHANGE]');
// ... save ...
// Edit 2: Open and close without changes
await para.dblClick();
// ... don't modify ...
await cancelBtn.click();
// Verify no change
expect(textAfterNoEdit).toBe(originalEditedText);
The test BUG FIX: Changes on all pages are restored, not just last page verifies:
// Edit multiple paragraphs
for (const target of editTargets) {
// ... make edits ...
}
// Reload page
await page.reload();
// Verify all edits persisted
for (const target of editTargets) {
const text = await paras.nth(target.index).textContent();
expect(text).toContain(target.marker);
}
After tests run, an HTML report is generated:
npx playwright show-report
This opens an interactive report showing:
If tests fail with “connection refused”, the dev server may not be starting properly:
# Manual start:
npm run serve:e2e
# In another terminal:
npm run test:e2e
postCreateCommand already installs Playwright (npx playwright install chromium).scripts/run-e2e.sh automatically wraps the test command via xvfb-run when $DISPLAY is unavailable, so you can simply run:npm run test:e2e
This avoids the need to start an X server manually. The helper server that backs npm run test:e2e (npm run serve:e2e) serves the rendered Quarto example from example/_output and maps /example to index.html, so no additional preview steps are required.
Note: Use
npx playwright install --with-deps chromiumonly on self-managed machines where you have sudo/root access and still need Playwright to pull in missing system libraries. Inside the devcontainer those libraries are preinstalled.
If tests timeout waiting for elements, check:
data-review-id, data-review-type)If tests sometimes pass and sometimes fail:
await page.waitForSelector(..., { timeout: 10000 })npm run test:e2e -- --headed
Shows the browser while tests run.
npm run test:e2e -- --ui
Opens UI mode where you can step through tests.
npm run test:e2e -- --debug
Pauses on each action and opens inspector.
// In tests
console.log('Current text:', await para.textContent());
page.screenshot({ path: 'debug-screenshot.png' });
.spec.ts file in tests/e2e/{ test, expect } from @playwright/testtest.describe() for test suites and test() for individual tests.goto(), .click(), .fill(), .locator(), etc.expect().toBe(), expect().toContain(), etc.Example:
import { test, expect } from '@playwright/test';
test.describe('My Feature', () => {
test('should work correctly', async ({ page }) => {
await page.goto('/example');
// ... test code ...
expect(result).toBe(expected);
});
});
page.goto(), page.reload(), page.goBack()page.locator(), page.click(), page.fill(), page.dblClick()page.waitForSelector(), page.waitForTimeout(), page.waitForNavigation()expect(value).toBe(), .toContain(), .toBeVisible(), .toHaveText()page.screenshot(), page.pause(), console.log()npm run test:e2e in your CI workflow; no extra xvfb setup is required because the helper script handles it.- run: npx playwright install chromium
- run: npm run test:e2e