| name | e2e-test |
| description | Write and run L4 UI E2E journey tests. Use when implementing UI features (TDD red-green), adding tests for existing features, or when E2E tests need updating. Handles the workflow: write test → run → green. |
E2E Journey Tests
Full strategy: docs/E2E-STRATEGY.md
When to Use
- Implementing a new UI feature (write test FIRST — TDD red-green)
- Adding test coverage for an existing feature
- Fixing a bug (write test that reproduces it, then fix)
- Updating tests after intentional UI changes
Principles (must follow)
- One test per feature — complete user flow, not element visibility checks
- Navigate like a user —
page.goto() only as entry point, then click links/buttons
- Complete the action — click approve AND verify confirmation. Don't stop at "button visible"
- Isolate mutating tests — tests that change state (cancel, approve, delete) must use dedicated seed data. Never mutate data other tests read
- Tests are protected — never modify a test just to make CI green
Writing a New Test
1. Create the file
packages/platform-ui/e2e/ui/<feature-name>.journey.ts
2. Template
import { test, expect } from '@playwright/test';
import { TEST_ORG_HANDLE } from '../helpers/constants';
import { trackPageErrors } from '../helpers/page-errors';
test.describe('<Feature> Journey', () => {
test('<what the user does>', async ({ page }) => {
trackPageErrors(page);
await page.goto(`/${TEST_ORG_HANDLE}/<path>`);
await expect(page.getByRole('heading', { name: '<title>' })).toBeVisible({ timeout: 10_000 });
await page.getByText('<link text>').click();
await page.getByRole('button', { name: /submit/i }).click();
await expect(page.getByText(/success/i)).toBeVisible({ timeout: 15_000 });
});
});
3. Key helpers
trackPageErrors(page) — first line; collects page/console errors so you can assert none occurred via getPageErrors(page)
allowPageErrors(page, [...]) — filter out test-environment-only errors that cannot occur in production
{ timeout: 10_000 } — on first assertion after page load
4. If test mutates state
Add dedicated seed data in e2e/helpers/seed-data.ts:
'proc-my-feature-target': {
id: 'proc-my-feature-target',
},
Use this dedicated instance in the test. Never mutate proc-running-1 or other shared instances.
Running
pnpm test:e2e -- --grep "<test name>"
pnpm test:e2e
After Test Passes
Commit the test file with the feature code in the same PR.
Debugging Failures
- Use
agent-browser on localhost:9007 (emulator mode) to see what the UI shows
- Check emulators running:
curl -s http://127.0.0.1:9099 and :8080
- Start emulators:
pnpm emulators
- Strict mode error → use
.first() or more specific locator
- Auth issue → re-run (flaky emulator state, CI retries handle this)