/e2e-test-create
Analyze React component source code to understand UI structure, then generate idiomatic Cypress E2E tests following Metabase conventions. Falls back to Playwright MCP browser exploration only when code reading and screenshot debugging are insufficient.
Claude Code
$ npx -y skills add metabase/metabase --skill e2e-test-create --agent claude-code
Codex
$ npx skills add metabase/metabase --skill e2e-test-create -a codex -y
Cursor
$ npx skills add metabase/metabase --skill e2e-test-create -a cursor -y
Gemini CLI
$ npx skills add metabase/metabase --skill e2e-test-create -a gemini-cli -y
GitHub Copilot
$ npx skills add metabase/metabase --skill e2e-test-create -a github-copilot -y
OpenCode
$ npx skills add metabase/metabase --skill e2e-test-create -a opencode -y
Windsurf
$ npx skills add metabase/metabase --skill e2e-test-create -a windsurf -y
SKILL.md
e2e-test-create.SKILL.mdname: e2e-test-create
description: >
Analyze React component source code to understand UI structure,
then generate idiomatic Cypress E2E tests following Metabase conventions.
Falls back to Playwright MCP browser exploration only when code reading
and screenshot debugging are insufficient.
disable-model-invocation: true
allowed-tools:
- Bash
- Read
- Write
- Grep
- Glob
- Skill
- "mcp__playwright__*"
Code-Reading-First → Generate Cypress Tests (Metabase)
You are writing Cypress E2E tests for the **Metabase** codebase. Before generating ANY test code, you MUST analyze React component source code to understand DOM structure, selectors, and user flows.
Phase 0 — Research
1. Read existing helpers before writing anything:
- `e2e/support/helpers/` — all shared helpers (restore, signInAs, openOrdersTable, etc.)
- `e2e/support/cypress_sample_database.ts` — table/field schema constants (ORDERS, PRODUCTS, etc.)
- `e2e/support/cypress_sample_instance_data.ts` — instance-specific IDs (ORDERS_DASHBOARD_ID, NORMAL_USER_ID, etc.)
2. Glob `e2e/test/scenarios/` to find the closest existing spec to the area under test. Study its patterns — match them exactly. 3. Glob `frontend/src/metabase/` to find React components for the feature area.
Phase 1 — Code Analysis
Read React component source to understand DOM structure. No browser needed — source code has everything.
1. **Find relevant components**: Glob and grep `frontend/src/metabase/` for the feature area. 2. **Extract selectors**: Grep for `data-testid` in relevant components. 3. **Note visible text**: Read component JSX for button labels, headings, placeholders. 4. **Note aria attributes**: Grep for `aria-label` in relevant components. 5. **Understand user flows**: Read event handlers (onClick, onSubmit, onChange) to understand interactions. 6. **Find API calls**: Grep for `Api.use`, `fetch`, `useQuery`, endpoint definitions to identify API calls to intercept. 7. **Cross-reference with existing specs**: Find specs in the same area and reuse their proven selectors and `cy.intercept` patterns.
Phase 2 — Start Backend
Use `MB_EDITION=oss` by default. Only use `MB_EDITION=ee` when the user explicitly asks to write an enterprise test.
Start the backend using `run_in_background: true` (NOT `&`). `bin/e2e-backend` automatically detects if a backend is already running and reuses it.
MB_EDITION=oss bin/e2e-backend
Do NOT manually generate snapshots by running unrelated test specs. The `bun test-cypress` runner has `GENERATE_SNAPSHOTS: true` by default and automatically generates snapshots before running any spec. When running tests in Phase 4 via the `/e2e-test` skill, snapshots will be generated on the first run if they don't already exist.
Restore clean test data:
curl -sf -X POST http://localhost:4000/api/testing/restore/default
Phase 3 — Generate Cypress Spec
Follow the Metabase Cypress conventions:
@./../_shared/cypress-conventions.md
When you identified API calls during code analysis, stub or wait on them using the intercept pattern shown above.
Phase 4 — Validate
After generating specs: 1. Check that all imported helpers exist (Grep `e2e/support/helpers/`). 2. **You MUST use the `/e2e-test` skill** to run tests — do NOT run `bun test-cypress` directly. The `/e2e-test` skill handles edition selection, snapshot management, and correct env vars. `/e2e-test GREP="should do the thing" --spec e2e/test/scenarios/<path>` If you created multiple `it()` blocks, run each one individually to isolate failures.
Phase 5 — Fix Failures (up to 2 attempts)
When a test fails, **try to fix it from Cypress output first**: 1. Read the failure screenshot (path printed under `(Screenshots)`). 2. Read the error message and code frame from the console output. 3. Fix the test and re-run (back to Phase 4, step 2).
If you cannot diagnose the issue after 2 attempts, proceed to Phase 6.
Phase 6 — Playwright Fallback
Only reach this phase after 2 failed fix attempts from Phase 5. The backend is already running.
Restore clean test data:
curl -sf -X POST http://localhost:4000/api/testing/restore/default
**Bypass CSP** headers before navigating (Metabase serves strict CSP that blocks dev server scripts). Use `browser_run_code` to set this up:
async (page) => {
// Strip CSP headers so the page loads (mirrors Cypress chromeWebSecurity: false)
await page.context().route('**/*', async (route) => {
const response = await route.fetch();
const headers = { ...response.headers() };
delete headers['content-security-policy'];
delete headers['content-security-policy-report-only'];
await route.fulfill({ response, headers });
});
// Sign in via API
const response = await page.request.post('http://localhost:4000/api/session', {
data: { username: 'admin@metabase.test', password: '12341234' }
});
const session = await response.json();
await page.context().addCookies([{
name: 'metabase.DEVICE',
value: session.id,
domain: 'localhost',
path: '/'
}]);
await page.goto('http://localhost:4000');
await page.waitForLoadState('networkidle');
return 'signed in';
}**Maintain an observation log incrementally.** After EVERY significant Playwright interaction, IMMEDIATELY append what you observed to the scratch file BEFORE performing the next interaction:
cat >> /tmp/e2e-observations.md << 'OBSERVATION'
## [Page/Flow name]
- URL: /question/notebook#...
- Clicked: "Box plot" button → visible text "Box plot", role: radio
- Selectors: data-testid="viz-type-button", findByText("Box plot")
- API call: POST /api/dataset (triggered on viz change)
- Key state: after selecting viz type, summary sidebar shows metric picker
OBSERVATIONFor each page/flow:
- Take an accessibility snapshot (`browser_snapshot`).
- Click through interactive elements, fill forms, trigger modals.
- **Append to the observation log i
Read more
name: e2e-test-create description: > Analyze React component source code to understand UI structure, then generate idiomatic Cypress E2E tests following Metabase conventions. Falls back to Playwright MCP browser exploration only when code reading and screenshot debugging are insufficient. disable-model-invocation: true allowed-tools: - Bash - Read - Write - Grep - Glob - Skill - "mcp__playwright__*"
Code-Reading-First → Generate Cypress Tests (Metabase)
You are writing Cypress E2E tests for the **Metabase** codebase. Before generating ANY test code, you MUST analyze React component source code to understand DOM structure, selectors, and user flows.
Phase 0 — Research
1. Read existing helpers before writing anything:
- `e2e/support/helpers/` — all shared helpers (restore, signInAs, openOrdersTable, etc.)
- `e2e/support/cypress_sample_database.ts` — table/field schema constants (ORDERS, PRODUCTS, etc.)
- `e2e/support/cypress_sample_instance_data.ts` — instance-specific IDs (ORDERS_DASHBOARD_ID, NORMAL_USER_ID, etc.)
2. Glob `e2e/test/scenarios/` to find the closest existing spec to the area under test. Study its patterns — match them exactly. 3. Glob `frontend/src/metabase/` to find React components for the feature area.
Phase 1 — Code Analysis
Read React component source to understand DOM structure. No browser needed — source code has everything.
1. **Find relevant components**: Glob and grep `frontend/src/metabase/` for the feature area. 2. **Extract selectors**: Grep for `data-testid` in relevant components. 3. **Note visible text**: Read component JSX for button labels, headings, placeholders. 4. **Note aria attributes**: Grep for `aria-label` in relevant components. 5. **Understand user flows**: Read event handlers (onClick, onSubmit, onChange) to understand interactions. 6. **Find API calls**: Grep for `Api.use`, `fetch`, `useQuery`, endpoint definitions to identify API calls to intercept. 7. **Cross-reference with existing specs**: Find specs in the same area and reuse their proven selectors and `cy.intercept` patterns.
Phase 2 — Start Backend
Use `MB_EDITION=oss` by default. Only use `MB_EDITION=ee` when the user explicitly asks to write an enterprise test.
Start the backend using `run_in_background: true` (NOT `&`). `bin/e2e-backend` automatically detects if a backend is already running and reuses it.
MB_EDITION=oss bin/e2e-backend
Do NOT manually generate snapshots by running unrelated test specs. The `bun test-cypress` runner has `GENERATE_SNAPSHOTS: true` by default and automatically generates snapshots before running any spec. When running tests in Phase 4 via the `/e2e-test` skill, snapshots will be generated on the first run if they don't already exist.
Restore clean test data:
curl -sf -X POST http://localhost:4000/api/testing/restore/default
Phase 3 — Generate Cypress Spec
Follow the Metabase Cypress conventions:
@./../_shared/cypress-conventions.md
When you identified API calls during code analysis, stub or wait on them using the intercept pattern shown above.
Phase 4 — Validate
After generating specs: 1. Check that all imported helpers exist (Grep `e2e/support/helpers/`). 2. **You MUST use the `/e2e-test` skill** to run tests — do NOT run `bun test-cypress` directly. The `/e2e-test` skill handles edition selection, snapshot management, and correct env vars. `/e2e-test GREP="should do the thing" --spec e2e/test/scenarios/<path>` If you created multiple `it()` blocks, run each one individually to isolate failures.
Phase 5 — Fix Failures (up to 2 attempts)
When a test fails, **try to fix it from Cypress output first**: 1. Read the failure screenshot (path printed under `(Screenshots)`). 2. Read the error message and code frame from the console output. 3. Fix the test and re-run (back to Phase 4, step 2).
If you cannot diagnose the issue after 2 attempts, proceed to Phase 6.
Phase 6 — Playwright Fallback
Only reach this phase after 2 failed fix attempts from Phase 5. The backend is already running.
Restore clean test data:
curl -sf -X POST http://localhost:4000/api/testing/restore/default
**Bypass CSP** headers before navigating (Metabase serves strict CSP that blocks dev server scripts). Use `browser_run_code` to set this up:
async (page) => {
// Strip CSP headers so the page loads (mirrors Cypress chromeWebSecurity: false)
await page.context().route('**/*', async (route) => {
const response = await route.fetch();
const headers = { ...response.headers() };
delete headers['content-security-policy'];
delete headers['content-security-policy-report-only'];
await route.fulfill({ response, headers });
});
// Sign in via API
const response = await page.request.post('http://localhost:4000/api/session', {
data: { username: 'admin@metabase.test', password: '12341234' }
});
const session = await response.json();
await page.context().addCookies([{
name: 'metabase.DEVICE',
value: session.id,
domain: 'localhost',
path: '/'
}]);
await page.goto('http://localhost:4000');
await page.waitForLoadState('networkidle');
return 'signed in';
}**Maintain an observation log incrementally.** After EVERY significant Playwright interaction, IMMEDIATELY append what you observed to the scratch file BEFORE performing the next interaction:
cat >> /tmp/e2e-observations.md << 'OBSERVATION'
## [Page/Flow name]
- URL: /question/notebook#...
- Clicked: "Box plot" button → visible text "Box plot", role: radio
- Selectors: data-testid="viz-type-button", findByText("Box plot")
- API call: POST /api/dataset (triggered on viz change)
- Key state: after selecting viz type, summary sidebar shows metric picker
OBSERVATIONFor each page/flow:
- Take an accessibility snapshot (`browser_snapshot`).
- Click through interactive elements, fill forms, trigger modals.
- **Append to the observation log i
Metabase is the easy, open-source way for everyone in your company to ask questions and learn from data.
Other skills on metabase.
More skills from other plugins.
brainstorming
You MUST use this before any creative work - creating features, building components, adding functionality, modifying behavior, or…
ask-matt
Ask which skill or flow fits your situation. A router over the skills in this repo.
Questions about e2e-test-create.
- Which coding agents can use e2e-test-create?
- e2e-test-create installs in Claude Code, Codex, Cursor, Gemini CLI, GitHub Copilot, OpenCode and Windsurf with the skills CLI, using each agent's flag shown on this page. Flowy's auto-invocation, which fires a skill when your prompt matches, runs in Claude Code only.

