|
| 1 | +# Cypress E2E Test Plan |
| 2 | + |
| 3 | +## Goal |
| 4 | +Cover all functionality and UI elements on each page to minimize UI-related bugs and regression. |
| 5 | + |
| 6 | +## Existing Coverage |
| 7 | + |
| 8 | +| Page | Test File | Coverage | |
| 9 | +|------|-----------|----------| |
| 10 | +| Login | `login.cy.js` | Logo, inputs, valid/invalid login, redirect | |
| 11 | +| Repo List | `repo.cy.js` | Add repo, duplicate error, anonymous/regular/admin permissions, clone tooltip | |
| 12 | +| Push Actions | `docker/pushActions.cy.js` | Approve, Reject, Cancel, unauthorized attempts, dialog cancel | |
| 13 | +| Auto-Approved Push | `autoApproved.cy.js` | Auto-approved message, tooltip timestamp | |
| 14 | +| Push Details | `push-details.cy.js` | Pending/Approved/Rejected/Canceled states, tabs, card body, steps, error state, navigation | |
| 15 | + |
| 16 | +--- |
| 17 | + |
| 18 | +## Prerequisites: Bug Fixes & Infrastructure |
| 19 | + |
| 20 | +### Bug Fixes Required |
| 21 | + |
| 22 | +| Bug | Location | Fix | |
| 23 | +|-----|----------|-----| |
| 24 | +| Error tab sends wrong query param | `src/ui/views/PushRequests/components/PushesTable.tsx:64` | Change `errored` → `error` to match DB field | |
| 25 | +| Broken CardBody selector in test | `cypress/e2e/push-details.cy.js:84` | Add `data-testid="push-details-card-body"` to `PushDetails.tsx` `<CardBody>` | |
| 26 | + |
| 27 | +### Test Data Cleanup Infrastructure |
| 28 | + |
| 29 | +**Problem:** `cy.createPush()` creates permanent push records via real git operations, but there is no API to delete a push. Tests accumulate data forever. |
| 30 | + |
| 31 | +**Solution:** |
| 32 | +1. Add `src/service/routes/test.ts` with test-only endpoints (gated by `NODE_ENV === 'test'`): |
| 33 | + - `DELETE /api/v1/test/push/:id` — calls `db.deletePush(id)` (admin auth) |
| 34 | + - `DELETE /api/v1/test/user/:username` — calls `db.deleteUser(username)` (admin auth) |
| 35 | +2. Conditionally mount in `src/service/routes/index.ts` when `NODE_ENV === 'test'` |
| 36 | +3. Add custom commands to `cypress/support/commands.js`: |
| 37 | + - `cy.deleteTestPush(pushId)` |
| 38 | + - `cy.deleteTestUser(username)` |
| 39 | +4. Backfill cleanup into existing leaky test files: |
| 40 | + - `push-details.cy.js` — add `afterEach` push cleanup |
| 41 | + - `docker/pushActions.cy.js` — add `afterEach` push cleanup + `after` user cleanup |
| 42 | + |
| 43 | +### UI Instrumentation Needed |
| 44 | + |
| 45 | +Systematically add `data-testid` and accessibility attributes to untested pages for robust, maintainable selectors: |
| 46 | + |
| 47 | +| File | Attributes to Add | |
| 48 | +|------|-------------------| |
| 49 | +| `src/ui/views/PushDetails/PushDetails.tsx` | `data-testid="push-details-card-body"` on `<CardBody>` | |
| 50 | +| `src/ui/views/PushRequests/PushRequests.tsx` | `data-testid="push-requests-tabs"` on tabs container | |
| 51 | +| `src/ui/views/PushRequests/components/PushesTable.tsx` | `data-testid="push-row-<id>"` on each `<TableRow>`, `data-testid="pushes-table"` on table | |
| 52 | +| `src/ui/views/RepoDetails/RepoDetails.tsx` | `data-testid="repo-info-card"`, `data-testid="reviewers-table"`, `data-testid="contributors-table"`, `data-testid="add-reviewer-btn"`, `data-testid="add-contributor-btn"`, `data-testid="code-clone-btn"` | |
| 53 | +| `src/ui/views/RepoDetails/Components/AddUser.tsx` | `data-testid="add-user-dialog"`, `data-testid="add-user-select"`, `data-testid="add-user-confirm-btn"` | |
| 54 | +| `src/ui/views/RepoDetails/Components/DeleteRepoDialog.tsx` | `data-testid="delete-repo-dialog"`, `data-testid="delete-repo-confirm-input"`, `data-testid="delete-repo-confirm-btn"` | |
| 55 | +| `src/ui/views/User/UserProfile.tsx` | `data-testid="profile-name"`, `data-testid="profile-role"`, `data-testid="profile-email"`, `data-testid="profile-gitAccount"`, `data-testid="profile-admin-status"`, `data-testid="gitAccount-input"`, `data-testid="update-profile-btn"` | |
| 56 | +| `src/ui/views/UserList/Components/UserList.tsx` | `data-testid="user-list-table"`, `data-testid="user-row-<username>"` on each row | |
| 57 | +| `src/ui/views/Settings/Settings.tsx` | `data-testid="jwt-token-input"`, `data-testid="jwt-token-toggle"`, `data-testid="jwt-save-btn"`, `data-testid="jwt-clear-btn"`, `data-testid="settings-snackbar"` | |
| 58 | +| `src/ui/components/Sidebar/Sidebar.tsx` | `aria-current="page"` on active `<NavLink>` | |
| 59 | +| `src/ui/components/Navbars/Navbar.tsx` | `data-testid="navbar"` | |
| 60 | +| `src/ui/components/Footer/Footer.tsx` | `data-testid="footer"` | |
| 61 | +| `src/ui/components/Search/Search.tsx` | `data-testid="search-input"` | |
| 62 | +| `src/ui/components/Pagination/Pagination.tsx` | `data-testid="pagination-previous"`, `data-testid="pagination-next"`, `data-testid="pagination-info"` | |
| 63 | +| `src/ui/components/Filtering/Filtering.tsx` | `data-testid="filter-dropdown"`, `data-testid="filter-option-<name>"`, `data-testid="filter-sort-toggle"` | |
| 64 | +| `src/ui/views/Extras/NotFound.tsx` | `data-testid="not-found-page"` | |
| 65 | +| `src/ui/views/Extras/NotAuthorized.tsx` | `data-testid="not-authorized-page"` | |
| 66 | + |
| 67 | +--- |
| 68 | + |
| 69 | +## Phase 1: High-Complexity Pages |
| 70 | + |
| 71 | +### 1. Push Details — Tabs & Content Rendering ✅ DONE |
| 72 | +**Route:** `/dashboard/push/:id` |
| 73 | +**File:** `cypress/e2e/push-details.cy.js` |
| 74 | +**Strategy:** Real API for 10/11 tests, intercept only for error state. Cleanup added via `afterEach`. |
| 75 | + |
| 76 | +- [x] 1.1 — Pending push shows Pending status with action buttons *(real API)* |
| 77 | +- [x] 1.2 — Card body renders: Timestamp, Remote Head link, Commit SHA link, Repository link, Branch link *(real API)* |
| 78 | +- [x] 1.3 — Commits tab renders commit data table with correct columns *(real API)* |
| 79 | +- [x] 1.4 — Changes tab renders diff content via diff2html *(real API)* |
| 80 | +- [x] 1.5 — Steps tab renders steps timeline with summary chips *(real API)* |
| 81 | +- [x] 1.6 — Steps accordions expand and show content/logs *(real API)* |
| 82 | +- [x] 1.7 — Rejected push shows rejection info with reason *(real API)* |
| 83 | +- [x] 1.8 — Approved push shows attestation info *(real API)* |
| 84 | +- [x] 1.9 — Error state renders error message when API fails *(intercept — can't trigger real 500)* |
| 85 | +- [x] 1.10 — Canceled push shows Canceled status *(real API)* |
| 86 | +- [x] 1.11 — Action buttons navigate back to push list after completing action *(real API)* |
| 87 | + |
| 88 | +### 2. Repo Details — User Management |
| 89 | +**Route:** `/dashboard/repo/:id` |
| 90 | +**File:** `cypress/e2e/repo-details.cy.js` |
| 91 | +**Strategy:** Real API for all tests. Create a test repo via `cy.request POST /api/v1/repo` in `before`, clean up in `after`. |
| 92 | + |
| 93 | +- [ ] 2.1 — Repo info renders: project, name, URL links |
| 94 | +- [ ] 2.2 — Reviewers table renders user list with links |
| 95 | +- [ ] 2.3 — Contributors table renders user list with links |
| 96 | +- [ ] 2.4 — Admin can add reviewer via "Add Reviewer" button |
| 97 | +- [ ] 2.5 — Admin can remove reviewer |
| 98 | +- [ ] 2.6 — Admin can add contributor via "Add Contributor" button |
| 99 | +- [ ] 2.7 — Admin can remove contributor |
| 100 | +- [ ] 2.8 — Delete repo dialog opens, confirms, navigates to repo list |
| 101 | +- [ ] 2.9 — Non-admin cannot see add/remove/delete buttons |
| 102 | +- [ ] 2.10 — Code clone button renders with correct URL |
| 103 | + |
| 104 | +### 3. Push Requests — Tab Filtering |
| 105 | +**Route:** `/dashboard/push` |
| 106 | +**File:** `cypress/e2e/push-requests.cy.js` |
| 107 | +**Strategy:** Shared dataset created once in `before()`, cleaned up in `after()`. Uses real pushes for Pending/Approved/Rejected/Canceled, intercept for Error tab. *Comment in code explaining shared dataset for PR reviewers.* |
| 108 | + |
| 109 | +- [ ] 3.1 — All 6 tabs render (All, Pending, Approved, Canceled, Rejected, Error) |
| 110 | +- [ ] 3.2 — Pending tab filters to show only pending pushes *(real API)* |
| 111 | +- [ ] 3.3 — Approved tab filters to show only approved pushes *(real API)* |
| 112 | +- [ ] 3.4 — Canceled tab filters to show only canceled pushes *(real API)* |
| 113 | +- [ ] 3.5 — Rejected tab filters to show only rejected pushes *(real API)* |
| 114 | +- [ ] 3.6 — Error tab filters to show only errored pushes *(intercept — requires UI bugfix + synthetic error push)* |
| 115 | +- [ ] 3.7 — Push table rows are clickable and navigate to Push Details *(real API)* |
| 116 | + |
| 117 | +### 4. Repo List — Search, Filter, Pagination |
| 118 | +**Route:** `/dashboard/repo` |
| 119 | +**File:** `cypress/e2e/repo-list.cy.js` |
| 120 | +**Strategy:** Create 6+ test repos via fast API (`cy.request POST /api/v1/repo`) in `before()`, clean up in `after()`. Pagination is tested here only (shared `Pagination` component). Search/filter use client-side logic. |
| 121 | + |
| 122 | +- [ ] 4.1 — Search filters repos by name |
| 123 | +- [ ] 4.2 — Search filters repos by project |
| 124 | +- [ ] 4.3 — Clear search resets to all repos |
| 125 | +- [ ] 4.4 — Filter dropdown sorts by Date Modified, Date Created, Alphabetical |
| 126 | +- [ ] 4.5 — Pagination renders and navigates between pages |
| 127 | +- [ ] 4.6 — Repo rows are clickable and navigate to Repo Details |
| 128 | + |
| 129 | +### 5. Profile Page |
| 130 | +**Route:** `/dashboard/profile` |
| 131 | +**File:** `cypress/e2e/profile.cy.js` |
| 132 | +**Strategy:** Real API for all tests. |
| 133 | + |
| 134 | +- [ ] 5.1 — Displays user info: name, role, email, GitHub username, admin status |
| 135 | +- [ ] 5.2 — User can edit their own GitHub username |
| 136 | +- [ ] 5.3 — Admin can edit another user's GitHub username (via `/dashboard/user/:id`) |
| 137 | +- [ ] 5.4 — Non-admin viewing another user's profile cannot edit |
| 138 | + |
| 139 | +### 6. User List (Admin) |
| 140 | +**Route:** `/dashboard/admin/user` |
| 141 | +**File:** `cypress/e2e/user-list.cy.js` |
| 142 | +**Strategy:** Real API. *Note: Create/delete user UI does not exist; tests cover only read access.* |
| 143 | + |
| 144 | +- [ ] 6.1 — Renders list of all users |
| 145 | +- ~~6.2 — Admin can create a new user~~ *(UI not implemented — removed from scope)* |
| 146 | +- ~~6.3 — Admin can delete a user~~ *(UI not implemented — removed from scope)* |
| 147 | +- [ ] 6.4 — Non-admin cannot access user list |
| 148 | + |
| 149 | +### 7. Settings Page |
| 150 | +**Route:** `/dashboard/admin/settings` |
| 151 | +**File:** `cypress/e2e/settings.cy.js` |
| 152 | +**Strategy:** Uses `localStorage` for JWT persistence. No backend API calls for save/clear. |
| 153 | + |
| 154 | +- [ ] 7.1 — JWT token field renders with show/hide toggle |
| 155 | +- [ ] 7.2 — Save button persists token and shows snackbar |
| 156 | +- [ ] 7.3 — Clear button removes token and shows snackbar |
| 157 | +- [ ] 7.4 — Token persists across page reload |
| 158 | + |
| 159 | +### 8. Navigation & Shell |
| 160 | +**File:** `cypress/e2e/navigation.cy.js` |
| 161 | +**Strategy:** Mix of real navigation and intercepts. |
| 162 | + |
| 163 | +- [ ] 8.1 — Sidebar renders all visible links (Repositories, Dashboard, My Account, Users, Settings) |
| 164 | +- [ ] 8.2 — Sidebar links navigate correctly |
| 165 | +- [ ] 8.3 — Active sidebar item highlights *(uses `aria-current="page"`)* |
| 166 | +- [ ] 8.4 — Navbar renders correctly |
| 167 | +- [ ] 8.5 — Footer renders |
| 168 | +- [ ] 8.6 — Unauthenticated user is redirected to `/login` |
| 169 | +- [ ] 8.7 — `/` redirects to `/dashboard/repo` |
| 170 | + |
| 171 | +### 9. Error Pages |
| 172 | +**File:** `cypress/e2e/error-pages.cy.js` |
| 173 | +**Strategy:** Direct navigation. No API needed. |
| 174 | + |
| 175 | +- [ ] 9.1 — Unknown route shows 404 page |
| 176 | +- [ ] 9.2 — Unauthorized route shows NotAuthorized page |
| 177 | + |
| 178 | +--- |
| 179 | + |
| 180 | +## Implementation Notes |
| 181 | + |
| 182 | +### Hybrid Approach: Real API First, Intercepts as Fallback |
| 183 | + |
| 184 | +- **Prefer real API calls** — leverage existing custom commands (`cy.createPush()`, `cy.createUser()`, `cy.addUserPushPermission()`, etc.) to create real data and test real UI rendering |
| 185 | +- **Use `cy.intercept()` only when real API is impractical** — e.g., mocking 500 errors, testing edge-case data shapes (empty commits, specific step errors/blocks), or simulating OIDC flows |
| 186 | +- **Shared datasets for read-only filtering tests** — acceptable when tests only assert on rendering, not mutations. Document with inline comments. |
| 187 | +- Use `cy.session()` for login (already available in custom commands) |
| 188 | +- Follow existing file naming convention: `cypress/e2e/<name>.cy.js` |
| 189 | +- Include Apache 2.0 license header in all new files |
| 190 | +- Each test file should document which tests use real API vs intercepts (see `push-details.cy.js` as reference) |
| 191 | + |
| 192 | +### Cleanup Discipline |
| 193 | + |
| 194 | +- Every test that creates a push via `cy.createPush()` must clean it up via `cy.deleteTestPush()` in `afterEach` or `after` |
| 195 | +- Every test that creates a user via `cy.createUser()` should clean it up via `cy.deleteTestUser()` in `after` |
| 196 | +- `repo-list.cy.js` creates repos via `cy.request POST /api/v1/repo`; clean up via `cy.deleteRepo()` in `after` |
| 197 | +- Do not rely on database wipes between CI runs; keep local repeated runs safe |
0 commit comments