Unified Test Runner (run-e2e.sh)
Overlay Documentation
This page covers writing tests within rhdh-plugin-export-overlays. For using @red-hat-developer-hub/e2e-test-utils in external projects, see the Guide.
The run-e2e.sh script orchestrates E2E test execution across multiple workspaces. It handles workspace discovery, dependency installation, Playwright configuration generation, and parallel test execution.
Usage
# Run all workspace tests
./run-e2e.sh
# List discovered projects (dry run)
./run-e2e.sh --list
# Run specific workspaces
./run-e2e.sh -w tech-radar
./run-e2e.sh -w backstage -w quickstart
# Control parallelism
./run-e2e.sh --workers=4
# Run specific project
./run-e2e.sh --project=acr
# Filter tests by name
./run-e2e.sh --grep="Quick"
# Combine flags
./run-e2e.sh -w backstage --workers=2
# List projects in a workspace
./run-e2e.sh -w backstage --list
# Use a local build of e2e-test-utils
E2E_TEST_UTILS_PATH=/path/to/rhdh-e2e-test-utils ./run-e2e.sh -w tech-radar
# Pin a specific npm version of e2e-test-utils
E2E_TEST_UTILS_VERSION=1.1.30 ./run-e2e.sh -w tech-radar2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
Workspace Selection
Use the -w or --workspace flag to filter which workspaces to test. Without this flag, all workspaces with E2E tests are included.
A workspace is discovered when it has a workspaces/<name>/e2e-tests/ directory containing both package.json and playwright.config.ts.
# Single workspace
./run-e2e.sh -w tech-radar
# Multiple workspaces
./run-e2e.sh -w tech-radar -w keycloak -w github-actions2
3
4
5
Environment Variables
RHDH Deployment
| Variable | Description | Default |
|---|---|---|
RHDH_VERSION | RHDH version to deploy (e.g., 1.10, next) | 1.10 |
INSTALLATION_METHOD | Deployment method: helm or operator | helm |
SKIP_KEYCLOAK_DEPLOYMENT | Set true to skip Keycloak deployment | - |
CATALOG_INDEX_IMAGE | Override the default catalog index image baked into the RHDH chart | - |
Test Framework
| Variable | Description | Default |
|---|---|---|
CI | Enables CI mode (forbidOnly, namespace teardown) | true |
PLAYWRIGHT_VERSION | Pin @playwright/test version | 1.59.1 |
E2E_TEST_UTILS_PATH | Absolute path to a local e2e-test-utils build (for testing unpublished changes) | - |
E2E_TEST_UTILS_VERSION | Pin @red-hat-developer-hub/e2e-test-utils npm version | latest (nightly), empty otherwise |
Plugin Resolution
| Variable | Description | Default |
|---|---|---|
E2E_NIGHTLY_MODE | When true, uses released OCI images from metadata; defaults E2E_TEST_UTILS_VERSION to latest | false |
GIT_PR_NUMBER | PR number for OCI URL generation (uses PR-built images) | - |
JOB_NAME | CI job name; if contains periodic-, disables metadata injection. Also used to auto-derive skip tags. | - |
Skip Tags
When JOB_NAME is set (by OpenShift CI), the script auto-derives a Playwright tag and injects --grep-invert to exclude tests tagged with it. This lets test authors skip specific tests in specific CI jobs using standard Playwright tags.
How It Works
The job suffix is extracted from JOB_NAME by stripping everything up to and including -e2e-. A negative lookahead (?!-) is appended so each tag matches exactly — @skip-ocp-helm won't accidentally filter @skip-ocp-helm-nightly:
| JOB_NAME (suffix shown) | --grep-invert pattern |
|---|---|
...-e2e-ocp-helm | @skip-ocp-helm(?!-) |
...-e2e-ocp-helm-nightly | @skip-ocp-helm-nightly(?!-) |
...-e2e-ocp-operator | @skip-ocp-operator(?!-) |
...-e2e-ocp-operator-nightly | @skip-ocp-operator-nightly(?!-) |
If JOB_NAME doesn't contain -e2e-, no tag is derived and no filtering is applied.
Tagging Tests
Add Playwright tags to test.describe or individual test calls:
// Skip only in ocp-helm-nightly job
test.describe("My Plugin", { tag: "@skip-ocp-helm-nightly" }, () => { ... });
// Skip only in ocp-helm PR check job (won't affect nightly)
test("expensive test", { tag: "@skip-ocp-helm" }, async () => { ... });
// Multiple tags — skip in both helm and operator nightly
test.describe("Suite", {
tag: ["@skip-ocp-helm-nightly", "@skip-ocp-operator-nightly"],
}, () => { ... });2
3
4
5
6
7
8
9
10
From a Workspace Directory
Skip tags work the same way when running tests from a workspace — pass --grep-invert manually:
cd workspaces/tech-radar/e2e-tests
yarn test -- --grep-invert "@skip-ocp-helm"2
Precedence
The auto-derived --grep-invert is prepended to the Playwright arguments. If you also pass --grep-invert on the command line, Playwright uses the last value (last wins), so your manual flag takes precedence.
How It Works
The script performs these steps in order:
1. Validate Prerequisites
Checks for node, yarn, jq, and verifies cluster login (oc whoami). Cluster login is skipped for --list mode.
2. Discover Workspaces
Scans workspaces/*/e2e-tests/ for directories containing both package.json and playwright.config.ts. Applies -w filter if provided.
3. Generate Root package.json
Creates a root package.json with:
- Yarn workspaces pointing to selected
workspaces/*/e2e-testsdirectories - Resolutions to pin
@playwright/testand optionally@red-hat-developer-hub/e2e-test-utils
{
"workspaces": ["workspaces/tech-radar/e2e-tests", "workspaces/keycloak/e2e-tests"],
"resolutions": {
"@playwright/test": "1.59.1",
"@red-hat-developer-hub/e2e-test-utils": "1.1.30"
}
}2
3
4
5
6
7
4. Install Dependencies
Cleans all node_modules and yarn.lock to ensure fresh resolution, then runs yarn install.
5. Generate Root playwright.config.ts
Extracts projects: [...] blocks from each workspace's playwright.config.ts using sed text processing. Injects testDir into each project pointing to the workspace's tests/ directory.
Why sed Instead of Import?
Importing workspace configs would execute their top-level code (e.g., process.env mutations), which can pollute the environment for other workspaces. Text extraction avoids this.
The generated config imports baseConfig from the test utils package, which provides reporters, timeouts, video/screenshot/trace settings, and global setup.
6. Run Tests
Runs npx playwright test with any additional arguments passed through. All arguments not recognized as -w/--workspace are forwarded directly to Playwright.
7. Display Summary
Parses playwright-report/results.json and displays:
- Duration, passed/failed/flaky/skipped counts
- Overall status (PASSED/FAILED)
- Report file location
Version Pinning
There are two ways to control which version of @red-hat-developer-hub/e2e-test-utils is used:
Local Build (Development)
Use E2E_TEST_UTILS_PATH to point to a local checkout. The script builds it before installing:
E2E_TEST_UTILS_PATH=/home/user/rhdh-e2e-test-utils ./run-e2e.sh -w tech-radarSpecific npm Version
Use E2E_TEST_UTILS_VERSION to pin a published version:
E2E_TEST_UTILS_VERSION=1.1.30 ./run-e2e.sh -w tech-radarNightly Default
When E2E_NIGHTLY_MODE=true, E2E_TEST_UTILS_VERSION defaults to latest to pick up the most recent published version. This ensures nightly runs always use the latest test utilities.
Priority
E2E_TEST_UTILS_PATH takes precedence over E2E_TEST_UTILS_VERSION. If neither is set, the version declared in each workspace's package.json is used (after resolution).
List Mode
Use --list to preview discovered test projects without running them:
./run-e2e.sh --list
./run-e2e.sh -w backstage --list2
This generates a lightweight config that skips globalSetup and teardown reporters, so no cluster connection is needed.
Playwright Arguments
All arguments not recognized as -w/--workspace are forwarded directly to Playwright:
# Control workers
./run-e2e.sh --workers=1
# Run specific project
./run-e2e.sh --project=tech-radar
# Filter by test name
./run-e2e.sh --grep="catalog"
# Combine multiple flags
./run-e2e.sh -w backstage --workers=2 --retries=1 --project=backstage-kubernetes2
3
4
5
6
7
8
9
10
11
Generated Files
The script generates these temporary files in the repository root:
| File | Purpose |
|---|---|
package.json | Root workspace config with resolutions |
.yarnrc.yml | Yarn node-modules linker config |
playwright.config.ts | Combined Playwright config with all workspace projects |
playwright.list.config.ts | Lightweight config for --list mode (when used) |
These files are generated fresh on each run.
Path Resolution
Running tests from the repo root changes the working directory relative to each workspace. The package handles this transparently:
- WorkspacePaths resolves all config file paths (app-config, secrets, dynamic-plugins, metadata) from Playwright's
test.info().project.testDir— an absolute path — instead ofprocess.cwd(). This works correctly regardless of where the process was launched. - Worker fixture sets
process.chdir()to the workspace'se2e-tests/directory when the test worker starts, so relative paths in shell scripts andfscalls also resolve correctly.
No changes to test code are needed. The same spec files work both with yarn test from the workspace and ./run-e2e.sh from the repo root. See Path Resolution for details.
Design Decisions
Why Single Root Playwright (Not Per-Workspace Parallel)
Two strategies were evaluated for running all workspace tests in CI:
| Single root Playwright | Per-workspace shell parallel | |
|---|---|---|
| Parallelism | Worker-level — Playwright auto-balances across all projects | Workspace-level — a large workspace bottlenecks while small ones sit idle |
| Keycloak | globalSetup runs once, no races | Multiple processes deploy simultaneously, causing races |
| Reporting | Single report with traces/screenshots/videos | Blob merge step needed, adds a failure point |
| Dependency validation | Yarn resolutions validates upgrades across all workspaces in one run | No way to test a dependency upgrade across all workspaces at once |
| CLI | Standard Playwright flags work (--project, --grep, --shard) | Flags must be forwarded per-process |
The single root approach requires WorkspacePaths to resolve config paths correctly, but this change is backward-compatible and benefits all execution modes.
Why Yarn Workspaces Is Required
Playwright errors out if @playwright/test is loaded from more than one file path in a process. With separate node_modules per workspace, each workspace resolves the package from a different path. Yarn workspaces hoists all dependencies to a single root node_modules, ensuring one copy. This is the primary reason yarn workspaces is used — not just for convenience.
No Impact on Existing Workflows
Nothing is committed to the repo. package.json, playwright.config.ts, and .yarnrc.yml are generated at runtime. Workspace yarn.lock files are bypassed (fresh resolution at root), which is fine for nightly — PR checks still use --immutable per workspace. Fresh resolution in nightly actually catches dependency regressions early.
Related Pages
- Running Tests Locally - Individual workspace testing
- CI/CD Pipeline - How run-e2e.sh is used in OpenShift CI
- Environment Variables - All supported variables
- Package.json Scripts - Workspace-level test scripts