mirror of
https://github.com/johannesjo/super-productivity.git
synced 2026-07-17 16:37:43 +00:00
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>
5.5 KiB
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 testaddTask(name)- Add task via global input
taskPage
getTaskByText(text)→ LocatorgetTask(index)→ Locator (1-based)getAllTasks()→ LocatormarkTaskAsDone(task)getTaskCount()→ numbergetDoneTasks()/getUndoneTasks()→ LocatorwaitForTaskWithText(text)→ Locator
projectPage
createProject(name)navigateToProjectByName(name)createAndGoToTestProject()- Quick setup
settingsPage
navigateToPluginSettings()uploadPlugin(path),enablePlugin(name),pluginExists(name)
dialogPage
waitForDialog()→ LocatorclickDialogButton(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
- Always start with
await workViewPage.waitForTaskList() - Use page objects - not raw
page.locator()for common actions - No
waitForTimeout()- useexpect().toBeVisible()instead - Tests are isolated - each gets fresh browser context + IndexedDB
- Use assertion helpers - for consistent, readable tests