feat(e2e): cluster-free local E2E harness (legacy app, Tier B)#5005
feat(e2e): cluster-free local E2E harness (legacy app, Tier B)#5005gustavolira wants to merge 18 commits into
Conversation
Run real Playwright E2E against RHDH without an OpenShift/Kubernetes cluster or container images — Playwright boots the backend and a frontend dev server in-process and drives the browser against them. - Legacy harness (Tier B, recommended): targets packages/app with dynamic plugins loaded via Scalprum, so the EXISTING specs run unmodified. Verified the production RHDH home page (Quick Access from the dynamic home-page plugin) renders off-cluster and the guest-signin home-page test passes. - app-next harness: targets the new frontend system; covers core/statically registered plugin UIs (dynamic frontend loading is blocked upstream — see doc). - Shared guest-auth + in-memory-SQLite overlay (app-config.local-e2e.yaml); webServer invokes backstage-cli/janus-cli from the repo-root .bin. - yarn scripts: e2e:legacy-local, e2e:app-next-local. Part of RHIDP-13501 (E2E Test Optimization), Layer 4a spike RHIDP-15075. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
PR Summary by QodoAdd cluster-free local Playwright E2E harnesses (legacy Tier B + app-next) Description
Diagram
High-Level Assessment
Files changed (6)
|
Code Review by Qodo
1. Wrong server reused
|
- Scope legacy testMatch to the spec verified to pass off-cluster (learning-path-page); document the others as pending the global-header mount fix / per-spec config so the default run is green. - Guard PATH interpolation against an undefined process.env.PATH via a shared pathWithRepoBin constant in both configs. - Default CATALOG_INDEX_IMAGE to quay.io/rhdh/plugin-catalog-index:latest for main (release branches use the matching :1.y tag). - Note that the guest-auth overlay is test-only and must never reach a production config; drop a duplicated comment. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
The container image build workflow finished with status: |
The chosen direction is the legacy cluster-free harness (packages/app), which runs the existing specs unmodified. The app-next harness can't load dynamic plugins yet (blocked upstream), so it's removed from this PR and tracked as a follow-up; a short note in the doc records why legacy is the target. - Remove playwright.app-next-local.config.ts and its guest-identity.spec.ts. - Remove the e2e:app-next-local script. - Make the overlay and docs legacy-only (keep a "why not app-next yet" note). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Scope the default run to the one verified-green test (guest-signin home-page) via testMatch + grep, instead of an unvalidated spec whose sidebar navigation may not match the harness config. - Add a globalSetup that fails fast with the populate command when dynamic-plugins-root is empty (clear error instead of a locator timeout). - Build the shared --config args via array join; document workers=1 and that the backend command mirrors packages/backend's start script. - Doc: describe the grep-scoped default run and the fail-fast guard. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
The container image build workflow finished with status: |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #5005 +/- ##
==========================================
- Coverage 55.39% 54.77% -0.62%
==========================================
Files 122 110 -12
Lines 2365 2147 -218
Branches 563 538 -25
==========================================
- Hits 1310 1176 -134
+ Misses 1049 969 -80
+ Partials 6 2 -4
Continue to review full report in Codecov by Harness.
🚀 New features to boost your workflow:
|
…h 80) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
/test e2e-ocp-helm-nightly |
Add .github/workflows/e2e-cluster-free.yaml: a no-cluster job that installs deps + skopeo, populates dynamic-plugins-root from the public catalog index via the install-dynamic-plugins CLI (same mechanism as the nightly sanity check), boots the backend + legacy app dev servers in-process, and runs yarn e2e:legacy-local. Triggers on e2e-tests/** and app-config*.yaml. Follows the project workflow-security rules: pull_request (no secrets, public image), pinned action SHAs, minimal permissions, concurrency control. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The 'Install Playwright browser' step hung on 'playwright install --with-deps' (its apt phase); drop --with-deps since ubuntu-latest already has the libs headless chromium needs. Add setup-node yarn caching for both lockfiles to cut the slow root install on subsequent runs. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Use mcr.microsoft.com/playwright:v1.59.1-noble (browsers + OS deps preinstalled, matching @playwright/test 1.59.1) to eliminate the playwright-install step that hung on plain ubuntu runners. Enable corepack for the vendored yarn 4 and cache the yarn global cache explicitly (the container lacks the yarn binary setup-node's cache relies on). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ree-e2e-harness # Conflicts: # e2e-tests/package.json
…ation main migrated e2e-tests from ESLint/Prettier/tsc to oxlint + oxfmt. Reformat the harness files with oxfmt and satisfy oxlint: a shared isCI constant (process.env.CI !== undefined && !== ''), nullish coalescing, the regexp u flag, and no inline comments. Matches the pattern in playwright.config.ts. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The merge with main bumped @playwright/test to 1.61.1, but the container was pinned at v1.59.1-noble, so the browser launch failed with 'Executable doesn't exist at /ms-playwright/chromium_headless_shell-1228/...'. Match the container tag to the package version. (Populate + backend + frontend already came up fine in CI — only the browser version was off.) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The previous populate (plugins: []) installed 0 plugins, so the home page 404'd and the test failed waiting for 'Welcome back!'. The catalog index's default.yaml references core plugins via local ./dynamic-plugins/dist paths that need a source build, which CI doesn't do. Instead, install the dynamic-home-page frontend plugin from the public OCI registry (ghcr, via skopeo) — no build needed. app-config.dynamic-plugins.yaml already configures it (route / -> DynamicHomePage + QuickAccessCard), so the guest-signin home-page test renders off-cluster. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…l-header OCI Install the global-header plugin from OCI (top bar + profile dropdown) so the Settings and Sign-out specs render, and drop the grep so all three guest-signin-happy-path tests run off-cluster (home + settings + sign-out). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
The container image build workflow finished with status: |
The global-header plugin loads off-cluster but does not mount in the layout with the default config, so the Settings/Sign-out specs can't run yet. Revert to the verified-green home-page test; global-header rendering + those specs are a follow-up (noted in local-harness/dynamic-plugins.yaml). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
The container image build workflow finished with status: |
…iew fixes Apply the code-review findings on the cluster-free harness: - Extract the dynamic-plugins-root populate step into e2e-tests/local-harness/populate.sh — the single source of truth used by CI, the docs, and the global-setup error message (they had already diverged: CI installed the harness OCI set pinned to CLI 0.2.0 while the docs/error message pointed at the catalog index, unversioned). - Drop the unused CATALOG_INDEX_IMAGE env from the workflow: with the harness dynamic-plugins.yaml (no includes), the CLI only used it to extract a catalog index that nothing consumed — a wasted :latest quay pull on every run. Fix the workflow header comment accordingly (plugins come from ghcr OCI, not the catalog index). - Scope the harness run by a @cluster-free test tag instead of a title regex; widening coverage is now tagging the validated test and allowlisting its spec file in testMatch. - Count only directories in the global-setup guard: the installer writes its generated global-config file into dynamic-plugins-root even when zero plugins install, which previously satisfied the guard and produced the confusing locator timeout it exists to prevent. - Rewrite the docs populate section: the OCI/populate.sh path (what CI uses, works from a fresh clone) is primary; the catalog-index path is demoted to after-source-build use since its default.yaml references ./dynamic-plugins/dist paths that don't exist otherwise. - Gitignore the root dynamic-plugins.yaml copy the populate script leaves behind (the CLI hardcodes that path). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Expand the harness from 1 to 4 tests by fixing the global-header mounting and enabling the learning-paths spec: - global-header: the repo's static app-config.dynamic-plugins.yaml only mounts the bare GlobalHeader container with no children, which is why the plugin loaded but rendered nothing off-cluster. In-cluster the full mount points (ProfileDropdown, Settings/Sign-out menu items, search, etc.) come from the plugin's pluginConfig in the catalog index. Install the plugin from OCI with that canonical pluginConfig (copied from rhdh-plugins workspaces/global-header app-config.dynamic.yaml) and load the generated dynamic-plugins-root/app-config.dynamic-plugins.yaml last in the webServer config args — the same file and merge order the production container uses. This unblocks the guest-signin Settings and Sign-out tests, which navigate via the header's profile dropdown. - learning-paths: the page renders off-cluster from the static fallback data bundled with packages/app. The spec navigates through the "References" sidebar group, which is a CI config-map menu customization — mirror that menuItems nesting in app-config.local-e2e.yaml (objects deep-merge across config files, so only the nesting keys are needed). - Tag the three newly validated tests @cluster-free and allowlist learning-path-page.spec.ts in testMatch. - settings.spec.ts and home-page-customization.spec.ts stay out for now: they assert CI test data (catalog ownership entities, customized home cards) that the harness does not provide yet — documented in Known issues. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
The container image build workflow finished with status: |
|
@gustavolira: The following test failed, say
Full PR test history. Your PR dashboard. DetailsInstructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository. I understand the commands that are listed here. |
…k name The PR check now reads "E2E Cluster-free / e2e (pull_request)" instead of "E2E Cluster-free Harness / Cluster-free E2E (legacy app) (pull_request)", matching the naming style of the sibling E2E workflows. The "(pull_request)" suffix is appended by GitHub (trigger event) and cannot be removed. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…tion, path prefixes - Mark harnesses that are still in review and instruct the assistant to verify paths exist on main before recommending them (the skill referenced redhat-developer#5005/redhat-developer#5044/redhat-developer#2714/redhat-developer#4967 deliverables in present tense). - Describe the overlays native smoke's real interface: yarn smoke --dynamic-plugins <file> [--out] (no --workspace flag exists; the harness does not read workspaces/*/metadata — a workspace run is a dp.yaml listing its oci:// refs). - Replace the fork-only RHIDP-13235-layer3-component-tests branch pointer with the closed PR rhdh#4864. - Prefix every path with its repo (rhdh:/overlays:/plugins:) and use full URLs for cross-repo docs, so the skill resolves from any of its three homes. - Fix skopeo claim: it installs on macOS via brew; CI has it preinstalled. - Name the Docker smoke location (overlays smoke-tests/ + run-workspace-smoke-tests.yaml), replace the vague "any" repo cell, use "n/a (cluster)" for the cluster row's Docker column, move PR numbers to References with status, add trigger phrases to the frontmatter description, and deduplicate the guiding-rule sentence. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The populate script and harness doc claimed skopeo is "not available on macOS"; it is (brew install skopeo). The real point is that CI has it preinstalled. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
The container image build workflow finished with status: |
|



Summary
Adds a cluster-free E2E harness for RHDH: run the existing Playwright specs against a real RHDH instance without an OpenShift/Kubernetes cluster or container image build. Playwright boots the backend and the legacy app (
packages/app) dev servers in-process, installs dynamic plugins from the public OCI registry (ghcr, via skopeo — no source build), and drives the browser against them.Part of RHIDP-13501 (E2E Test Optimization), Layer 4a spike RHIDP-15075.
Status: 4 tests green in CI ✅
The GitHub Actions job (
.github/workflows/e2e-cluster-free.yaml) runs cluster-free and passes in ~3.5 min:Install (cached): ~90s · Populate plugins from OCI: ~11s · Boot + run: ~2m
The full
guest-signin-happy-pathspec passes unmodified — including Settings/Sign-out, which navigate through the global-header dynamic plugin's profile dropdown.How it works
v1.61.1-noble— browsers preinstalled).dynamic-plugins-rootvia./e2e-tests/local-harness/populate.sh— the single source of truth for the populate step (CI, docs, and the fail-fast globalSetup message all point to it). It installs the plugin set frome2e-tests/local-harness/dynamic-plugins.yaml(dynamic-home-page + global-header, from ghcr) with the CLI pinned to the version CI uses.yarn e2e:legacy-local— boots backend + legacy app with the config overlay, guest auth, and runs the specs.Test scoping uses a
@cluster-freetag + atestMatchfile allowlist: enabling a validated spec is tagging the test and adding its file totestMatch.The global-header fix (why Settings/Sign-out work now)
The repo's static
app-config.dynamic-plugins.yamlonly mounts the bareGlobalHeadercontainer with no children, so off-cluster the header rendered empty. In-cluster, the full mount points (ProfileDropdown, Settings/Sign-out items, search) come from the plugin'spluginConfigin the catalog index. The harness now carries that canonicalpluginConfig(copied from rhdh-pluginsworkspaces/global-header/.../app-config.dynamic.yaml) on the OCI entry, and the webServer loads the generateddynamic-plugins-root/app-config.dynamic-plugins.yamllast — the same file and merge order the production container uses. This pattern generalizes to any plugin whose config is not in the repo's static file.Why OCI instead of the catalog index
The catalog index's
dynamic-plugins.default.yamlreferences core plugins via local./dynamic-plugins/dist/…paths that only exist after a source build (which CI doesn't do). The overlays repo publishes those plugins as OCI artifacts, whichinstall-dynamic-pluginscan pull with skopeo directly — no build.Scope / follow-ups
docs/e2e-tests/local-e2e-harness.md(Known issues):settings.spec.tsneeds the CI catalog test entities,home-page-customization.spec.tsneeds the CI home-card customization — follow-up PRs (spec inventory tracked as RHIDP-15076).1.49.4(RHDH is on1.52.0); they load fine, but bump when the overlays repo publishes matching tags (noted in the config file).🤖 Generated with Claude Code