This page documents the end-to-end (E2E) test infrastructure for n8n, which uses Playwright to drive a real browser against a running n8n instance. It covers the package layout, the page object model (POM) hierarchy, the fixture system, Currents-based parallel execution, and coverage collection.
For unit and integration testing (Vitest/Jest against the backend), see Unit and Integration Testing. For the overall testing strategy and tool selection, see Testing Strategy and Tools.
All Playwright tests live in the dedicated package at packages/testing/playwright/, published under the name n8n-playwright. The package is separate from all application packages and has no runtime dependency on them ā it communicates with a running n8n instance through HTTP and the browser.
packages/testing/playwright/
āāā config/ # Environment constants (e.g. test-users.ts)
āāā fixtures/ # Playwright fixtures and JSON mock data
āāā pages/ # Page Object Models (POM)
ā āāā BasePage.ts
ā āāā ExecutionsPage.ts
ā āāā components/
ā āāā LogsPanel.ts
āāā scripts/ # Coverage utilities
ā āāā generate-coverage-report.js
ā āāā coverage-workflow.md
āāā tests/
ā āāā e2e/ # Test suites organised by feature area
ā āāā workflows/
ā ā āāā executions/
ā ā āāā list.spec.ts
ā āāā settings/
ā āāā users/
ā āāā users.spec.ts
āāā currents.config.ts # Currents parallel-run configuration
āāā nyc.config.ts # Istanbul/NYC coverage configuration
Sources: packages/testing/playwright/currents.config.ts1-14 packages/testing/playwright/scripts/generate-coverage-report.js1-30 packages/testing/playwright/pages/ExecutionsPage.ts1-10 packages/testing/playwright/tests/e2e/workflows/executions/list.spec.ts1-10
The following commands are available from the repo root or from inside the packages/testing/playwright directory:
| Command | Description |
|---|---|
pnpm --filter=n8n-playwright test:local | Start a local n8n on port 5680 and run all E2E tests headlessly |
pnpm --filter=n8n-playwright test:local --ui | Interactive Playwright UI (useful for debugging) |
pnpm --filter=n8n-playwright test:local --grep="test-name" | Run a single test matching a pattern |
These commands are documented in `CONTRIBUTING.md472-481
Tests import from fixtures/base and receive an n8n composite fixture object. The fixture abstracts test-setup concerns (starting the server, navigating to the right URL, resetting state) behind a clean interface.
Fixture architecture ā code entity map
Sources: packages/testing/playwright/tests/e2e/workflows/executions/list.spec.ts1-5 packages/testing/playwright/tests/e2e/settings/users/users.spec.ts1-5
A typical test imports and uses the fixture as follows:
Tests also use JSON fixture files for mocking server responses (e.g. fixtures/execution-out-of-memory-server-response.json).
Sources: packages/testing/playwright/tests/e2e/workflows/executions/list.spec.ts1-55
Page Object Models (POMs) encapsulate Playwright locators and actions for each UI area. They prevent brittle test code by centralising selectors in one place.
POM class hierarchy
Sources: packages/testing/playwright/pages/ExecutionsPage.ts1-123
BasePageBasePage provides generic helpers such as clickButtonByName(). All feature-specific page classes extend it.
ExecutionsPageExecutionsPage models the workflow executions list view. It is instantiated via the n8n.executions fixture property.
| Method / Property | Description |
|---|---|
logsPanel | LogsPanel nested component wrapping data-testid="logs-panel" inside the preview iframe |
getExecutionItems() | Returns all div.execution-card locators |
getLastExecutionItem() / getFirstExecutionItem() | Shorthand for the newest/oldest card |
getAutoRefreshButton() | data-testid="auto-refresh-checkbox" |
getPreviewIframe() | Content frame of data-testid="workflow-preview-iframe" |
clickLastExecutionItem() | Clicks the first execution card |
handlePinnedNodesConfirmation(action) | Clicks "Unpin" or "Cancel" on the pinned-nodes dialog |
getExecutionsList() | data-testid="current-executions-list" |
getExecutionsSidebar() | data-testid="executions-sidebar" |
getExecutionsEmptyList() | data-testid="execution-list-empty" |
getSuccessfulExecutionItems() | [data-test-execution-status="success"] |
getFailedExecutionItems() | [data-test-execution-status="error"] |
scrollExecutionsListToBottom() | Triggers lazy-loading by scrolling |
getErrorNotificationsInPreview() | Error toasts inside the preview iframe |
deleteExecutionInPreview() | Clicks delete button and confirm dialog |
openFilter() / resetFilter() | Toggle the filter popover |
selectFilterStatus(status) | Opens the status combobox and picks an option |
getFilterBadge() | Badge indicating active filter count |
Sources: packages/testing/playwright/pages/ExecutionsPage.ts1-123
LogsPanelLogsPanel wraps the logs panel element inside the execution preview iframe. It is accessed as n8n.executions.logsPanel.
Tests are grouped by feature area under tests/e2e/. Each test.describe block may carry a metadata annotation identifying the owning team:
Tests that are known to be flaky can be marked test.fixme(...) with a descriptive comment explaining the root cause.
Sources: packages/testing/playwright/tests/e2e/workflows/executions/list.spec.ts4-59 packages/testing/playwright/tests/e2e/settings/users/users.spec.ts4-10
E2E tests run in CI as part of the pull-request workflow. The ci-filter action determines whether the e2e suite needs to run based on changed paths.
E2E CI trigger paths
| Changed path | Triggers E2E? |
|---|---|
.github/workflows/test-e2e-*.yml | Yes |
packages/testing/playwright/** | Yes |
packages/testing/containers/** | Yes |
.github/scripts/cleanup-ghcr-images.mjs | Yes |
| Any other application code | Via the ci filter (all paths) |
The E2E job only runs in the n8n-io/n8n repository ā not in forks ā because it requires secrets for Currents.
Sources: .github/workflows/ci-pull-requests.yml54-130
Currents distributes Playwright tests across multiple CI workers using shard-based parallelism and records results for flakiness tracking.
The configuration is in `packages/testing/playwright/currents.config.ts`:
| Field | Description |
|---|---|
recordKey | CI secret; uniquely identifies this run in the Currents dashboard |
projectId | Hard-coded project ID (LRxcNt); overridable via env var |
coverage.projects | Enabled when BUILD_WITH_COVERAGE=true; collects per-project NYC data |
Sources: packages/testing/playwright/currents.config.ts1-14
Coverage is collected by building the editor-ui with Istanbul instrumentation and running tests against the instrumented build. Data is stored in .nyc_output/ and processed into HTML by generate-coverage-report.js.
Coverage workflow
The script generate-coverage-report.js looks for coverage in the following project-named sub-directories of .nyc_output/:
| Project name | Test mode |
|---|---|
e2e | Local mode |
sqlite:e2e | Container mode (SQLite) |
postgres:e2e | Container mode (Postgres) |
queue:e2e | Container mode (queue) |
multi-main:e2e | Container mode (multi-main) |
Sources: packages/testing/playwright/scripts/generate-coverage-report.js1-70 packages/testing/playwright/scripts/coverage-workflow.md1-50 packages/testing/playwright/currents.config.ts6-10
Sources: packages/testing/playwright/tests/e2e/workflows/executions/list.spec.ts69-160
| Symbol | Location | Role |
|---|---|---|
BasePage | packages/testing/playwright/pages/BasePage.ts | Root POM; shared locator helpers |
ExecutionsPage | packages/testing/playwright/pages/ExecutionsPage.ts | POM for executions list |
LogsPanel | packages/testing/playwright/pages/components/LogsPanel.ts | POM sub-component for log output |
CurrentsConfig | packages/testing/playwright/currents.config.ts | Parallel shard runner config |
generate-coverage-report.js | packages/testing/playwright/scripts/ | Merges NYC shards into HTML |
fixtures/base | packages/testing/playwright/fixtures/base.ts | Playwright test + n8n composite fixture |
n8n-playwright | packages/testing/playwright/ | Package name for the E2E suite |
test-e2e-ci-reusable.yml | .github/workflows/ | Reusable CI workflow for E2E |
Refresh this wiki
This wiki was recently refreshed. Please wait 2 days to refresh again.