--- name: e2e-test-review description: Review Cypress E2E spec files for Metabase conventions, common gotchas, and flakiness/performance issues. Use when reviewing pull requests or diffs containing Cypress spec files in e2e/test/scenarios/. allowed-tools: Read, Grep, Bash, Glob --- # E2E Test Review Skill @./../_shared/cypress-conventions.md ## Review mode detection Before starting, determine which mode to use: 1. **PR review mode** — if `mcp__github__create_pending_pull_request_review` is available, post issues as one cohesive pending review. 2. **Local review mode** — if not, output a numbered list in the conversation. ## Review process 1. Detect mode. 2. Read the changed spec end-to-end first to understand intent. Don't review line-by-line cold. 3. **(Conditional)** If after reading the spec a specific assertion or selector is **ambiguous** — you can't tell if it's the right anchor, or whether the test actually verifies what its title claims — briefly grep the related component for the test ID / role / text. **Don't read components by default.** Only do this when there's a real signal of confusion in the spec. 4. Scan against the checklist + pattern table below. 5. Number all issues sequentially. Skip nits — only flag what's worth fixing. ### When to hand off instead of review If the spec references an issue (`metabase#NNNNN`) and the user wants to **fix** flakiness or assess whether the test still reproduces the original bug, that's outside the review skill's scope. Point them at `/fix-flakey-test` (or the dedicated flake-fixing workflow), which knows to fetch the issue and the resolving PR's diff. The review skill stays focused on "is this test well-written and conformant" — it doesn't fetch external issue context by default. ## Review checklist The checklist mirrors the order of the conventions file. Items marked **(lint)** are also caught by ESLint — flag only when you see them slip through (helper-wrapped, lint-disabled, etc.). ### File and naming - [ ] Spec lives in `e2e/test/scenarios//` - [ ] Extension is `.cy.spec.ts` (preferred) or `.cy.spec.js` — don't flag `.js` on existing files - [ ] `describe` block names the area when relevant: `"area > sub-area > feature (#issue-number)"` - [ ] No leftover `.only` or `.skip` (pre-commit hook should catch, but flag it if it slips through) ### Helpers and constants - [ ] Helpers accessed via `const { H } = cy;` — no direct imports from `e2e/support/helpers` **(lint)** - [ ] Sample DB schema imported from `cypress_sample_database` - [ ] Instance data IDs imported from `cypress_sample_instance_data` - [ ] **No hardcoded numeric IDs anywhere** — including for entities the test creates itself. Capture from the create response or alias the intercept. - [ ] Existing navigation helpers used (`H.openOrdersTable`, `H.visitDashboard(id)`, etc.) instead of raw `cy.visit()` chains ### Selectors - [ ] Prefers a11y queries (`findByRole`, `findByLabelText`) over `findByText` - [ ] `findByText` only when an a11y query doesn't fit - [ ] `findByTestId` for `data-testid` attributes (never raw `cy.get("[data-testid='...']")`) - [ ] No CSS class names — especially generated ones from styled-components/Mantine (`.css-1abc2d`) - [ ] No ad-hoc CSS attribute selectors in specs or new helpers (the visualization helpers in `e2e/support/helpers/e2e-visual-tests-helpers.js` are the **only** intentional exception — see "What NOT to flag") - [ ] No XPath - [ ] **Positional selectors** (`.eq()`, `.first()`, `.last()`, `:nth-child`) only when the order is the assertion itself, or guarded by a length assertion immediately before. **(lint catches `.last()` and `.eq(