Skip to content

Feat/e2e testing framework#268

Open
ollieb89 wants to merge 15 commits intoabhi1693:masterfrom
ollieb89:feat/e2e-testing-framework
Open

Feat/e2e testing framework#268
ollieb89 wants to merge 15 commits intoabhi1693:masterfrom
ollieb89:feat/e2e-testing-framework

Conversation

@ollieb89
Copy link

Task / context

  • Mission Control task:
  • Why:

Scope

  • <bullet 1>
  • <bullet 2>

Out of scope

Evidence / validation

  • make check (or explain what you ran instead)
  • E2E (if applicable): <cypress run / screenshots>
  • Logs/links:

Screenshots (UI changes)

Desktop Mobile

Docs impact

  • No user/operator docs changes required
  • Docs updated: <paths/links>

Risk / rollout notes

  • Risk level: low / medium / high
  • Rollback plan (if needed):

Checklist

  • Branch created from origin/master (no unrelated commits)
  • PR is focused (one theme)
  • No secrets in code/logs/docs
  • If API/behavior changes: docs updated (OpenAPI + docs/reference/api.md)

ollieb89 and others added 15 commits March 12, 2026 06:52
Two-tier architecture (stubbed + integration) with shared factories,
Docker-based E2E environment, and parallel CI pipeline for the OCMC
dashboard.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
21 tasks across 5 chunks: factory layer, stubbed test refactoring,
Docker E2E environment, integration tests, and CI pipeline.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…shboard

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ction

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…t helpers

Rewrites boards_list, activity_feed, global_approvals, mobile_sidebar,
organizations, skill_packs_sync, local_auth_login, and board_tasks to
use the new factory layer and composable intercept helpers.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…rapper

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…seed runner

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…targets

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Adds auth-protected-access, board-lifecycle, and task-lifecycle
integration tests that run against the real backend.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings March 12, 2026 07:08
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Introduces a two-tier Cypress E2E framework (stubbed + real-backend integration) with shared factories/intercept helpers, plus Docker/CI orchestration to run the integration tier against an ephemeral stack.

Changes:

  • Added shared Cypress factories and composable intercept helpers to de-duplicate stubbed test setup.
  • Added an integration E2E tier (Cypress config + integration specs) backed by a Docker Compose e2e profile and a DB seed script.
  • Updated CI to split E2E into parallel e2e-stubbed and e2e-integration jobs.

Reviewed changes

Copilot reviewed 35 out of 35 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
scripts/e2e_seed.py Adds DB seeding for integration E2E prerequisites (user/org/membership).
frontend/cypress/support/testHooks.ts Refactors legacy hook helper to delegate to new stubbing utilities.
frontend/cypress/support/intercepts/sse.ts Adds reusable stubs for SSE endpoints.
frontend/cypress/support/intercepts/index.ts Barrel export for intercept helpers.
frontend/cypress/support/intercepts/dashboard.ts Adds reusable dashboard/metrics stubs.
frontend/cypress/support/intercepts/boards.ts Adds reusable boards + board-groups stubs.
frontend/cypress/support/intercepts/auth.ts Adds reusable auth bootstrap stubs (health + me + orgs + membership).
frontend/cypress/support/integration/api.ts Adds thin real-API helper for integration test setup/verification.
frontend/cypress/support/factories/user.ts Adds user entity factory for stubs.
frontend/cypress/support/factories/task.ts Adds task + task comment factories and request input builders.
frontend/cypress/support/factories/reset.ts Adds deterministic ID counter + reset hook for factories.
frontend/cypress/support/factories/org.ts Adds org + org member factories.
frontend/cypress/support/factories/index.ts Barrel export for factories.
frontend/cypress/support/factories/board.ts Adds board + create-input + snapshot factory helpers.
frontend/cypress/support/factories/approval.ts Adds approval factory helper.
frontend/cypress/support/e2e.ts Adds global factory reset + “unstubbed API” logging intercept.
frontend/cypress/support/commands.ts Makes local-auth login token env-aware for integration tier.
frontend/cypress/e2e/skill_packs_sync.cy.ts Migrates stubbed spec to new intercept/factory approach.
frontend/cypress/e2e/organizations.cy.ts Migrates org spec to stubAuth and trims inline stubs.
frontend/cypress/e2e/mobile_sidebar.cy.ts Migrates dashboard sidebar spec to intercept helpers.
frontend/cypress/e2e/local_auth_login.cy.ts Migrates local auth login spec to factories/intercepts.
frontend/cypress/e2e/global_approvals.cy.ts Migrates approvals spec to factories/intercepts.
frontend/cypress/e2e/boards_list.cy.ts Migrates boards list spec to factories/intercepts.
frontend/cypress/e2e/board_tasks.cy.ts Migrates board task spec to factories + SSE helper.
frontend/cypress/e2e/activity_feed.cy.ts Migrates activity feed spec to factories + SSE helper.
frontend/cypress/e2e-integration/task-lifecycle.cy.ts Adds real-backend task lifecycle integration spec.
frontend/cypress/e2e-integration/board-lifecycle.cy.ts Adds real-backend board lifecycle integration spec.
frontend/cypress/e2e-integration/auth-protected-access.cy.ts Adds real-backend auth gate integration spec.
frontend/cypress.integration.config.ts Adds Cypress config for integration tier (real API base + token).
docs/superpowers/specs/2026-03-12-e2e-testing-framework-design.md Design spec for the new E2E architecture and rollout.
docs/superpowers/plans/2026-03-12-e2e-testing-framework.md Detailed implementation plan for the E2E framework.
compose.yml Adds --profile e2e services (db-test, backend-e2e, e2e-seed).
backend/app/main.py Improves /readyz readiness probe to verify DB connectivity.
Makefile Adds e2e-up, e2e-seed, e2e-integration, e2e-full orchestration targets.
.github/workflows/ci.yml Splits E2E into parallel stubbed + integration jobs with artifacts/logs.

You can also share your feedback on Copilot code review. Take the survey.

getTasks(boardId: string) {
return cy.request({
method: "GET",
url: `${apiBase()}/api/v1/boards/${boardId}/snapshot`,
Copy link

Copilot AI Mar 12, 2026

Choose a reason for hiding this comment

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

api.getTasks() currently calls the same /boards/{id}/snapshot endpoint as api.getBoard(). The name suggests it fetches only tasks (or hits a /tasks endpoint), so this is easy to misuse later. Consider either renaming to reflect that it returns a snapshot, or changing the implementation to call an endpoint that actually returns tasks only (if available).

Suggested change
url: `${apiBase()}/api/v1/boards/${boardId}/snapshot`,
url: `${apiBase()}/api/v1/boards/${boardId}/tasks`,

Copilot uses AI. Check for mistakes.
Comment on lines +39 to +48
async def seed(database_url: str) -> None:
engine: AsyncEngine = create_async_engine(database_url, echo=False)
session_maker = async_sessionmaker(engine, class_=AsyncSession, expire_on_commit=False)

async with session_maker() as session:
# Idempotent: check if user already exists.
existing = await session.get(User, USER_ID)
if existing:
print(f"Seed data already exists (user {USER_ID}). Skipping.")
return
Copy link

Copilot AI Mar 12, 2026

Choose a reason for hiding this comment

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

seed() returns early when the user already exists, which (a) can leave the org/membership missing if the DB is in a partially-seeded state, and (b) skips engine.dispose() because the early return occurs before the dispose call. Consider making the seed idempotent per-entity (check/create org and membership too) and ensuring engine.dispose() runs via try/finally even on early exit.

Copilot uses AI. Check for mistakes.
Comment on lines +23 to +25
from sqlalchemy.ext.asyncio import AsyncEngine, async_sessionmaker, create_async_engine
from sqlmodel import SQLModel
from sqlmodel.ext.asyncio.session import AsyncSession
Copy link

Copilot AI Mar 12, 2026

Choose a reason for hiding this comment

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

SQLModel is imported but never used in this script. Since model registration is already handled via from app import models as _models, this import should be removed to avoid dead code / confusion.

Copilot uses AI. Check for mistakes.
it("positive: signed-in user can view /organization and sees correct invite permissions", () => {
// Semantically important: role is "member" (not default "owner")
// because invite button should be disabled for non-admins.
stubAuth(apiBase, {
Copy link

Copilot AI Mar 12, 2026

Choose a reason for hiding this comment

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

stubAuth() is being called with a membership user_id of "u1", but no matching user: { id: "u1" } override is provided. Because stubAuth() will still return a default user (e.g. user-1) from /users/me, the stubs become internally inconsistent (current user id != membership.user_id), which can cause subtle permission/UI logic issues. Make the stubs consistent by either overriding the user id to "u1" or by not overriding member.user_id.

Suggested change
stubAuth(apiBase, {
stubAuth(apiBase, {
user: { id: "u1" },

Copilot uses AI. Check for mistakes.
Comment on lines +27 to +41
// Fail stubbed tests if an API request escapes interception.
// Integration tests (which set Cypress.env("API_BASE")) skip this check.
if (!Cypress.env("API_BASE")) {
beforeEach(() => {
cy.intercept({ url: /\/api\/v1\//, middleware: true }, (req) => {
// If this middleware intercept is the ONLY handler, the request
// was not stubbed by a test-specific intercept.
// Log it so flaky leaks are visible in CI output.
Cypress.log({
name: "UNSTUBBED API",
message: `${req.method} ${req.url}`,
consoleProps: () => ({ method: req.method, url: req.url }),
});
});
});
Copy link

Copilot AI Mar 12, 2026

Choose a reason for hiding this comment

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

This block is documented as "Fail stubbed tests if an API request escapes interception", but the current intercept only logs and then allows the request to proceed. That means a missing stub could still yield a passing test if the app handles the response/error gracefully (or if a backend happens to be reachable locally). Consider explicitly failing the test for matched requests here (e.g., respond with an error status / throw) so unstubbed API calls are always surfaced as test failures.

Copilot uses AI. Check for mistakes.
Comment on lines +157 to +168
e2e-seed:
profiles: ["e2e"]
build:
context: .
dockerfile: backend/Dockerfile
depends_on:
db-test:
condition: service_healthy
environment:
DATABASE_URL: "postgresql+psycopg://ocmc:test@db-test:5432/ocmc_test"
command: ["python", "-m", "scripts.e2e_seed"]
restart: "no"
Copy link

Copilot AI Mar 12, 2026

Choose a reason for hiding this comment

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

e2e-seed only depends on db-test being healthy, but the seed script assumes the schema/migrations are already applied (handled by backend-e2e via DB_AUTO_MIGRATE). If someone runs make e2e-seed (or docker compose run e2e-seed) without having started/awaited backend-e2e, this can fail with missing tables. Consider adding a depends_on: backend-e2e: { condition: service_healthy } (or running migrations in the seed container) to make seeding robust when invoked independently.

Copilot uses AI. Check for mistakes.
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.

2 participants