super-productivity/e2e/CLAUDE.md
johannesjo eddcea7070 docs: trim CLAUDE.md and split E2E reference into e2e/CLAUDE.md
Drop the architecture overview (derivable from code and already
covered in docs/sync-and-op-log/ and ARCHITECTURE-DECISIONS.md),
collapse the long sync invariants into one-liners with pointers,
and dedupe the anti-patterns table against the rule lists. Move
the SuperSync docker-compose block and E2E iteration tips into
e2e/CLAUDE.md where the rest of the E2E reference lives. Tighten
the new docs/documentation-guide.md by dropping content that
duplicates docs/wiki/0.00-Wiki-Structure-and-Organization.md.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 23:04:03 +02:00

5.5 KiB

E2E Test Reference

Run Tests

npm run e2e:file tests/path/to/test.spec.ts   # Single test (~20s/test)
npm run e2e                                    # All tests
npm run e2e:supersync:file <path>              # SuperSync E2E (auto-starts server)

When iterating, prefer single-file runs with --retries=0 to avoid long waits, and --grep "test name" for one test:

npm run e2e:file <path> -- --retries=0 --grep "should X"

SuperSync full suite

The npm run e2e:supersync script buffers output, which is unhelpful for live debugging. Run Playwright directly with a line reporter instead:

# Start the server
docker compose -f docker-compose.yaml -f docker-compose.supersync.yaml up -d supersync && \
  until curl -s http://localhost:1901/health > /dev/null 2>&1; do sleep 1; done && \
  echo 'Server ready!'

# Run with line reporter for real-time output
npx playwright test --config e2e/playwright.config.ts --grep @supersync --reporter=line

# Stop the server when done
docker compose -f docker-compose.yaml -f docker-compose.supersync.yaml down supersync

Test Template

// Import path depends on test depth (see Import Paths below)
import { expect, test } from '../../fixtures/test.fixture';

test.describe('Feature', () => {
  test('should X when Y', async ({ workViewPage, taskPage }) => {
    await workViewPage.waitForTaskList();
    await workViewPage.addTask('Task Name');

    const task = taskPage.getTaskByText('Task Name');
    await expect(task).toBeVisible();
  });
});

Import Paths

Test Location Import Path
tests/feature/test.spec.ts ../../fixtures/test.fixture
tests/feature/sub/test.spec.ts ../../../fixtures/test.fixture

All Fixtures

Fixture Use For
workViewPage Task list, adding tasks
taskPage Task operations (get, edit, mark done)
projectPage Project CRUD, navigation
settingsPage Settings navigation, plugin management
dialogPage Modal/dialog interactions
plannerPage Planner view operations
syncPage WebDAV sync setup
tagPage Tag management
notePage Notes functionality
sideNavPage Side navigation
testPrefix Auto-applied to task/project names for isolation

Assertion Helpers

import {
  expectTaskCount,
  expectTaskVisible,
  expectTaskDone,
  expectDoneTaskCount,
  expectDialogVisible,
  expectNoGlobalError,
} from '../../utils/assertions';

// Usage:
await expectTaskCount(taskPage, 2);
await expectTaskVisible(taskPage, 'Task Name');
await expectTaskDone(taskPage, 'Task Name');
await expectDialogVisible(dialogPage);
await expectNoGlobalError(page);

Common Patterns

Create project with tasks

await projectPage.createAndGoToTestProject();
await workViewPage.addTask('Task 1');
await workViewPage.addTask('Task 2');
await expectTaskCount(taskPage, 2);

Mark task done and verify

await workViewPage.addTask('My Task');
const task = taskPage.getTaskByText('My Task');
await taskPage.markTaskAsDone(task);
await expectDoneTaskCount(taskPage, 1);

Dialog interaction

// Trigger dialog via some action, then:
await dialogPage.waitForDialog();
await dialogPage.fillDialogInput('input[name="title"]', 'Value');
await dialogPage.clickSaveButton();
await dialogPage.waitForDialogToClose();

Sync tests (serial execution required)

test.describe.configure({ mode: 'serial' });

test.describe('Sync Feature', () => {
  test('should sync data', async ({ syncPage, workViewPage }) => {
    // Sync tests require special setup - see sync-test-helpers.ts
  });
});

Key Methods

workViewPage

  • waitForTaskList() - Call first in every test
  • addTask(name) - Add task via global input

taskPage

  • getTaskByText(text) → Locator
  • getTask(index) → Locator (1-based)
  • getAllTasks() → Locator
  • markTaskAsDone(task)
  • getTaskCount() → number
  • getDoneTasks() / getUndoneTasks() → Locator
  • waitForTaskWithText(text) → Locator

projectPage

  • createProject(name)
  • navigateToProjectByName(name)
  • createAndGoToTestProject() - Quick setup

settingsPage

  • navigateToPluginSettings()
  • uploadPlugin(path), enablePlugin(name), pluginExists(name)

dialogPage

  • waitForDialog() → Locator
  • clickDialogButton(text), clickSaveButton()
  • fillDialogInput(selector, value)
  • waitForDialogToClose()

For full method list, read the page object file: e2e/pages/<name>.page.ts

Selectors

import { cssSelectors } from '../../constants/selectors';
// Available: TASK, FIRST_TASK, TASK_TITLE, TASK_DONE_BTN, ADD_TASK_INPUT, MAT_DIALOG, SIDENAV, etc.

Critical Rules

  1. Always start with await workViewPage.waitForTaskList()
  2. Use page objects - not raw page.locator() for common actions
  3. No waitForTimeout() - use expect().toBeVisible() instead
  4. Tests are isolated - each gets fresh browser context + IndexedDB
  5. Use assertion helpers - for consistent, readable tests