Skip to content

fix(e2e): consolidate frontend E2E to Cypress and restore CI (#967)#984

Open
DevHusnainAi wants to merge 18 commits into
OWASP:mainfrom
DevHusnainAi:feature/consolidate-cypress-e2e
Open

fix(e2e): consolidate frontend E2E to Cypress and restore CI (#967)#984
DevHusnainAi wants to merge 18 commits into
OWASP:mainfrom
DevHusnainAi:feature/consolidate-cypress-e2e

Conversation

@DevHusnainAi

Copy link
Copy Markdown

Closes #967

Consolidates the frontend E2E migration to Cypress and restores the
Test-e2e CI job, which has been disabled on main. Builds on #774 and
supersedes #729 / #732 (both now closed). Bundles the Playwright-install
guardrail.

Python Playwright is untouched

This PR does not touch Python Playwright (embeddings/scraping in
application/prompt_client/). The diff changes zero Playwright or
prompt_client files, and playwright install in make install-python
is unchanged. The Cypress e2e job simply doesn't invoke that target
(Cypress ships its own browser), so nothing Playwright is removed or
modified. The Python Test workflow (which exercises the Playwright/
embedding tests) stays green.

Why CI was red, and the fix

Re-enabling e2e.yml as-is failed because the e2e job built the SQLite
cache the wrong way:

  • flask db upgrade (migrations) is the Postgres path and omits
    columns the models added without a migration (e.g. cre.document_metadata),
    so upstream_sync failed with no such column.
  • The branch was 2 commits behind main, which left two Alembic heads
    flask db upgrade aborted with "Multiple head revisions".

Fix:

  • Merge upstream/main → single Alembic head (upstream's merge migration).
  • New make e2e-db target seeds the schema from the ORM models via
    create_all (the documented SQLite/CI path) then loads data with
    upstream_sync.
  • e2e.yml now runs install-deps + make e2e-db + make e2e, dropping
    migrate-upgrade and playwright install from the frontend job.

Test coverage (ported from the legacy Jest/Puppeteer suite)

Spec Flow
smoke.cy.js homepage search form, search → /search/{term}, browse /root_cres
search.cy.js no-results (asdf), free-text results (crypto)
standard.cy.js /node/standard/ASVS heading, sections, pagination
cre.cy.js CRE by id (558-807), case-insensitive filter
smartlink.cy.js internal redirect + external Mitre CWE fallback

Each spec asserts on real seeded data, so it fails on an empty DB
rather than passing against a broken backend.

Two notes on approach

  • Guardrail: the chore/playwright-install-guardrail change (69049e36)
    is already present in this branch as a commit with a byte-identical
    diff
    , so the Makefile + AGENTS.md guardrail docs land here. Cherry-picking
    the original SHA would just duplicate it.
  • Smartlink fixtures: local upstream_sync seeds only the CRE-linked
    subset, which doesn't include CWE/1002. I ported the same flow using
    seeded fixtures — ASVS/V13.2.5 (internal redirect) and an unmapped CWE id
    (external Mitre fallback) — so the test is deterministic.

Cleanup

Verification

All green on my fork (run):

  • Test-e2e: ✅ All specs passed! 11/11 (~10 min incl. full upstream_sync)
  • Test (Python): ✅ — Playwright/embedding tests unaffected
  • Lint Code Base: ✅
  • Local: make lint, yarn build, make e2e (3× no flakes).

PRAteek-singHWY and others added 17 commits March 3, 2026 12:01
Playwright here backs Python embeddings (prompt_client), not frontend
e2e. The Cypress migration replaced frontend e2e but this line was
removed by mistake, breaking `make install-python`. Restore it per
reviewer feedback on OWASP#774.
The readiness loop broke on success but fell through to yarn test:e2e
even when Flask never came up, causing confusing Cypress failures. Add a
post-loop probe that exits with a clear error if the server is still
unreachable. Addresses CodeRabbit review on OWASP#774.
…ings

Clarify Makefile and AGENTS.md so Cypress e2e migration PRs do not drop
playwright install from install-python (embeddings/scraping in prompt_client).

Co-authored-by: Cursor <cursoragent@cursor.com>
Build the e2e SQLite schema from the ORM models (create_all) instead of
migrate-upgrade: migrations are the Postgres path and omit columns the
models added without a migration (e.g. cre.document_metadata), so a
migrate-built cache is incomplete. Add a make e2e-db target and switch
the workflow to install-deps + e2e-db, dropping migrate-upgrade and the
unused playwright install (Cypress ships its own browser). Raise the job
timeout to cover the full upstream_sync graph download.

Refs OWASP#967
Add Cypress specs covering the historical Jest/Puppeteer coverage:
no-results and free-text search, ASVS standard browse, CRE lookup by id
and case-insensitive filter, and smartlink internal/external redirects.
Each spec asserts on real seeded data so it fails on an empty database.

Refs OWASP#967
Frontend e2e now runs on Cypress; drop the unused puppeteer and
jest-puppeteer devDependencies and their transitive lockfile entries.

Refs OWASP#967
Note how to seed the local database (make e2e-db) and run the Cypress
suite (make e2e, yarn cypress:open) in README and AGENTS.md.

Refs OWASP#967
Copilot AI review requested due to automatic review settings July 18, 2026 12:00

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@coderabbitai

coderabbitai Bot commented Jul 18, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro

Run ID: 59489d41-3df9-4149-ba5e-304b2bdc7903

📥 Commits

Reviewing files that changed from the base of the PR and between 0348a19 and 14bdb00.

📒 Files selected for processing (2)
  • .github/workflows/e2e.yml
  • README.md
🚧 Files skipped from review as they are similar to previous changes (2)
  • .github/workflows/e2e.yml
  • README.md

Summary by CodeRabbit

  • New Features
    • Added Cypress end-to-end coverage for key homepage, search results, CRE pages, filtering behavior, standard browsing, and smartlink redirects.
    • Added Cypress test commands for interactive and headless runs.
  • Documentation
    • Documented the Cypress workflow, including the one-time local E2E database seeding step.
  • Refactor
    • Switched the end-to-end test setup from the previous Puppeteer/Jest approach to Cypress.
    • Improved local and CI E2E orchestration with more reliable server startup/readiness checks and cleanup.

Walkthrough

The PR migrates frontend E2E testing from Puppeteer/Jest to Cypress, adds coverage for core routes and redirects, stabilizes Flask and database orchestration, updates CI execution, and documents local workflows.

Changes

Cypress E2E migration

Layer / File(s) Summary
Cypress test foundation
cypress.config.js, package.json, cypress/e2e/*
Cypress replaces the prior E2E scripts and dependencies, with tests covering smoke navigation, search, CRE pages, standard browsing, filtering, and smartlink redirects.
E2E environment orchestration
Makefile, .github/workflows/e2e.yml, AGENTS.md
Database seeding, Flask readiness polling, cleanup, dependency installation, and CI execution are coordinated through updated make targets and workflow steps.
Developer E2E instructions
README.md
Local database setup, headless Cypress execution, and interactive testing commands are documented.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Possibly related issues

Possibly related PRs

  • OWASP/OpenCRE#946 — Adds overlapping regression coverage for smartlink redirect edge cases.

Suggested reviewers: robvanderveer, northdpole, paoga87, pa04rth

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: moving frontend E2E to Cypress and restoring the CI job.
Description check ✅ Passed The description is directly about the Cypress E2E migration and CI restore, matching the changeset.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/e2e.yml:
- Around line 14-15: Update the checkout step using actions/checkout@v4 to set
persist-credentials to false, preventing the GitHub token from being stored in
local Git configuration while leaving the rest of the workflow unchanged.

In `@Makefile`:
- Around line 89-101: Update the Makefile recipe containing the Flask startup
and e2e test commands so all setup, environment exports, virtualenv activation,
background PID capture, trap registration, readiness polling, and test execution
run in one shell session. Add recipe line continuations and appropriate command
separators while preserving the existing startup, failure, and cleanup behavior.

In `@README.md`:
- Around line 362-363: Update the README comment for the make e2e-db command to
state that it rebuilds the local SQLite cache on every invocation and discards
the existing cache; remove the misleading “one-time” wording while leaving the
make e2e instruction unchanged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro

Run ID: f9880549-6dce-4a44-b898-62a462cdd4cc

📥 Commits

Reviewing files that changed from the base of the PR and between 2d1aa47 and 0348a19.

⛔ Files ignored due to path filters (1)
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (13)
  • .github/workflows/e2e.yml
  • AGENTS.md
  • Makefile
  • README.md
  • application/frontend/src/test/basic-e2e.test.ts
  • cypress.config.js
  • cypress.json
  • cypress/e2e/cre.cy.js
  • cypress/e2e/search.cy.js
  • cypress/e2e/smartlink.cy.js
  • cypress/e2e/smoke.cy.js
  • cypress/e2e/standard.cy.js
  • package.json
💤 Files with no reviewable changes (2)
  • application/frontend/src/test/basic-e2e.test.ts
  • cypress.json

Comment thread .github/workflows/e2e.yml
Comment thread Makefile
Comment thread README.md Outdated
Set persist-credentials: false on the e2e checkout (the job only reads,
never pushes) and correct the README: make e2e-db rebuilds and replaces
the local SQLite cache on every run, it is not a one-time step.

Refs OWASP#967
@DevHusnainAi

Copy link
Copy Markdown
Author

Hey team! @northdpole @robvanderveer @Pa04rth @paoga87

Just a quick heads-up that this migration is ready for your review. All 5 CI/CD pipelines are passing completely green, the legacy test suite has been successfully ported over to Cypress, and I've addressed the initial CodeRabbit feedback regarding workflow credentials and documentation updates.Let me know if you need any adjustments or if this is good to land!

Thanks.

@northdpole northdpole left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for consolidating the Cypress migration and for the careful create_all vs migrate-upgrade diagnosis — that SQLite/CI schema fix is the right call.

Accepting your offer on a lighter seed (see inline on e2e-db / upstream_sync). Once that lands and CI is green on this PR, this looks mergeable. Please also close #774 / #523 as superseded when we merge.

Comment thread Makefile
killall flask
rm -f "$(CURDIR)/standards_cache.sqlite" &&\
NO_LOAD_GRAPH_DB=1 FLASK_CONFIG=development python -c "from application import create_app, sqla; app=create_app(mode='development'); app.app_context().push(); sqla.create_all()" &&\
python cre.py --upstream_sync

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Request: please replace live upstream_sync here with a minimal checked-in seed (tiny sqlite fixture, or a small Python seeder using create_all + add_cre/add_node like application/tests/web_main_test.py).

Taking you up on the offer from the #967 thread (cached / fixture-based seed). Why it matters for us:

  • CI shouldn’t hit opencre.org every PR (~5 min, flaky, noisy on the public site).
  • Specs hardcode 558-807, crypto, ASVS/V13.2.5 — those should be explicit fixture contracts, not accidents of prod data.
  • The 45m job timeout can drop to something sane once sync is gone.

Minimum graph the current specs need: CRE 558-807 (“Mutually authenticate”) with ASVS links; enough ASVS sections for accordion + pagination; a crypto-matching CRE; one mapped section for smartlink; unmapped CWE for the Mitre fallback.

Comment thread .github/workflows/e2e.yml
runs-on: ubuntu-latest
# upstream_sync downloads the full CRE graph over the network (per-CRE
# fetches); allow headroom on top of install + frontend build + Cypress.
timeout-minutes: 45

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This 45m timeout exists for full-graph upstream_sync. Once e2e-db uses a fixture seed (see Makefile comment), please drop this (e.g. ~15m) so a hung Cypress run fails fast instead of burning a runner.

Comment thread .github/workflows/e2e.yml
yarn install
make install-deps-typescript
- name: Seed e2e database (schema from models via create_all, data from upstream)
run: make e2e-db

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We still need a DB here — this step is that DB. The important bit is not “skip migrations forever,” it’s “don’t use the Postgres migration path to build the SQLite e2e cache.”

OpenCRE has two schema stories:

  • Prod/staging Postgres → flask db upgrade / make migrate-upgrade
  • Local/CI SQLite → sqla.create_all() from the ORM (matches models; migrations omit some SQLite-relevant columns)

So: keep create_all + data load for e2e. Please don’t “fix” CI later by swapping e2e-db back to migrate-upgrade — that recreates the red job you already fixed. (Data load itself should move to a fixture per the Makefile note; schema creation stays create_all.)

Comment thread cypress/e2e/cre.cy.js
cy.contains('h5.cre-page__sub-heading', `ID: ${creId}`).should('be.visible');
});

it('applies a standard filter case-insensitively', () => {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Coverage gap vs the legacy Jest/Puppeteer suite: this only asserts the filter chrome (Filtering on / asvs / ASVS). The old test asserted the links container — contains ASVS + CRE, does not contain NIST.

Please strengthen to assert filtered link content once the fixture owns the linked standards. Otherwise we can regress real applyFilters behavior and stay green.

Comment thread cypress/e2e/search.cy.js
cy.url().should('include', `/search/${term}`);
// Data-bearing: a populated backend renders a "Matching CREs" section.
// This fails (red) if the DB is empty or the search API returns 500.
cy.contains('h1', 'Matching CREs').should('be.visible');

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Assertion strength: SearchName.tsx always renders the Matching CREs <h1> when the request didn’t 404 — even if the CRE list is empty. So this is weaker than it looks.

Prefer something data-bearing (e.g. at least one result row / .standard-page__links-container), which is what the old Jest crypto test did. Same idea as your comment above — one step further.

Comment thread cypress/e2e/smoke.cy.js

it('browse route is reachable', () => {
cy.visit('/root_cres');
cy.contains('h1', 'Root CREs').should('be.visible');

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Heading-only checks pass even on an empty/nearly empty DB if the route renders. Fine for smoke chrome; data-bearing coverage should live in search / cre / standard (as you’ve structured). Optional once the fixture seeds roots: assert at least one root CRE row.

Comment thread Makefile
sleep 1; \
done
curl -fsS http://127.0.0.1:5000 >/dev/null || { echo "ERROR: Flask did not become ready on http://127.0.0.1:5000 after 30s; see /tmp/opencre-e2e-flask.log"; exit 1; }
env -u ELECTRON_RUN_AS_NODE yarn test:e2e

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice readiness loop + fail-fast + ELECTRON_RUN_AS_NODE unset — keep that.

Ask: should e2e require a seeded DB and fail with a clear “run make e2e-db first” if standards_cache.sqlite is missing? Right now a contributor can make e2e against an empty cache and get confusing Cypress failures.

Comment thread Makefile
make install-deps-python &&\
playwright install
playwright install # Python embeddings/scraping (prompt_client); NOT frontend e2e — keep when migrating to Cypress

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for keeping playwright install and documenting it. That footgun (#774 removing it) is a big reason #967 exists — please leave this comment.

Comment thread package.json
"test:e2e": "jest",
"test:e2e": "cypress run",
"cypress:open": "cypress open",
"cypress:run": "cypress run",

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

test:e2e and cypress:run are identical. Either drop one or make test:e2e the Makefile entrypoint and cypress:run a documented alias — avoids three ways to run the same thing (make e2e / yarn test:e2e / yarn cypress:run).

Comment thread cypress.config.js
const { defineConfig } = require('cypress');

module.exports = defineConfig({
video: false,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fine to keep video off. Consider leaving screenshots-on-failure enabled (Cypress default) and uploading cypress/screenshots as a CI artifact on failure — otherwise the restored Test-e2e job will be painful to debug when it flakes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

E2E: migrate frontend to Cypress (consolidate #729 + #774); keep Python Playwright

4 participants