ワンクリックで
cypress-e2e
// Write or refactor Cypress E2E tests following Page Object Model, action/assertion separation, and project conventions. Use when creating new tests, refactoring existing ones, or adding page object functions.
// Write or refactor Cypress E2E tests following Page Object Model, action/assertion separation, and project conventions. Use when creating new tests, refactoring existing ones, or adding page object functions.
Implement a Figma design using shadcn/ui components. Use when converting Figma URLs to React code.
Creates a Storybook UI prototype from a PRD-style input using existing shadcn/ui components, subtle visual tone, and a clear hierarchy. Use when the user invokes /design.prototype or asks for a prototype Storybook story.
Sync a UI component from Figma to code using the component sync workflow. Use when updating components to match Figma designs.
Sync CSS variables from Figma plugin export to globals.css. Use when updating design tokens/colors from Figma.
Verify Figma implementation is pixel-perfect. Use after implementing Figma designs to catch and fix discrepancies.
| name | cypress-e2e |
| description | Write or refactor Cypress E2E tests following Page Object Model, action/assertion separation, and project conventions. Use when creating new tests, refactoring existing ones, or adding page object functions. |
| argument-hint | [test-description-or-file-path] |
| allowed-tools | ["Read","Edit","Write","Bash","Grep","Glob","Agent"] |
Write or refactor Cypress E2E tests for $ARGUMENTS.
Read apps/web/cypress/CLAUDE.md and apps/web/cypress/AGENTS.md for project-specific conventions before proceeding.
apps/web/cypress/e2e/pages/apps/web/cypress/CLAUDE.md for conventions*.page*.js files and main.page.js before creating anything newdata-testid attributes exist and which component renders themOrganize page object files in clear sections with this order:
// 1. Imports
// 2. Selectors — grouped by feature area, with comments
// 3. Labels & regex patterns
// 4. Internal helpers (selector builders, lookups)
// 5. Action functions (exported)
// 6. Verify functions (exported)
// 7. Composite flows (exported — multi-step sequences like onboarding)
data-testid (preferred), aria-label, or semantic HTML — never class namesdata-testid, add one to the React componentconst (no export)export constdata-testid, check if one already exists on the elementdata-testid must be unique — never reuse the same value across different componentssingle-account-name (AccountWidgetItem — single-chain)
multichain-account-name (AccountItemContent — expandable multichain)
data-testid added to source must be referenced in at least one Cypress page object selector. After adding test-ids, run a cross-reference check./\$[\s]*[1-9][\d,]*/ not /\$875/) since live data changesAction functions (click*, open*, expand*, type*, visit*): perform one user action AND wait for the result to be ready. An action that opens a popover must wait for the popover to appear. An action that navigates must wait for the target page to load. This prevents flaky tests where the next step runs before the UI has settled:
// ✅ Good: action waits for result
export function clickOnExpandWalletBtn() {
cy.get(expandWalletBtn).should('be.visible').click()
cy.get(sentinelStart).next().should('exist') // wait for popover
}
// ❌ Bad: action with no wait — next step may fail
export function clickOnExpandWalletBtn() {
cy.get(expandWalletBtn).click()
}
Verify functions (verify*): assert state only, no user actions
Functions used only within the page object: no export
Functions used by test files: export
General functions (3+ page files): put in main.page.js
Page-specific functions: put in that page's .pages.js
Wallet/navigation functions belong in navigation.page.js — not in feature page objects. Feature page objects import and call them. When the same action has different UI across contexts (e.g. legacy vs spaces wallet button), create separate functions in navigation.page.js rather than duplicating selectors in feature page objects:
// navigation.page.js — both wallet expand variants
export function clickOnWalletExpandMoreIcon() { ... } // legacy
export function clickOnExpandWalletBtn() { ... } // spaces
// spaces.page.js — uses navigation, no local wallet selector
export function disconnectFromSpaceLevel() {
navigation.clickOnExpandWalletBtn()
navigation.clickOnDisconnectBtn()
}
Prefer one parameterized function over multiple similar functions — use a type/variant parameter with a selector lookup table when the same verification applies to different component variants:
const selectors = {
single: { name: singleName, address: singleAddress },
multichain: { name: multichainName, address: multichainAddress },
}
export function verifyAccountRowDetails(type, rowIndex, details) {
const sel = selectors[type]
// ... use sel.name, sel.address etc.
}
Check main.page.js first for general utilities like verifyElementsCount, verifyMinimumElementsCount, verifyValuesExist, verifyElementsIsVisible — use them instead of writing custom versions
Multi-step flows (onboarding, account creation, member invite) should be split into small private helper functions and composed into one exported function:
// Private step functions — not exported
function navigateToCreateSpacePage() { ... }
function submitSpaceName(name) { ... }
function skipSelectSafesStep() { ... }
function skipInviteMembersStep() { ... }
function verifySpaceDashboardLoaded() { ... }
// Exported composite flow — reads like a clear sequence
export function createSpaceViaOnboardingWithSkip(name) {
navigateToCreateSpacePage()
submitSpaceName(name)
skipSelectSafesStep()
skipInviteMembersStep()
verifySpaceDashboardLoaded()
}
Rules for composite flows:
// Wait for EITHER the list page OR the form page to appear
cy.get(`${listPageBtn}, ${formPageInput}`, { timeout: 30000 })
.filter(':visible')
.first()
.then(($el) => {
if (!$el.is(formPageInput)) {
cy.wrap($el).click()
}
})
| Prefix | Purpose | Example |
|---|---|---|
click* | Click an element | clickAccountItemByIndex(index) |
open* | Open a dropdown/modal/panel | openSpaceSelector() |
expand* | Expand a collapsible section | expandAccountRow(index) |
type* | Type into an input | typeSpaceName(name) |
visit* | Navigate to a URL | visitSpaceDashboard(id) |
verify* | Assert state (visibility, URL) | verifySpaceSidebarItemsVisible() |
it('Verify that [expected behavior]', () => {
// 1. Preconditions — verify page is ready
space.verifySpaceDashboardWidgetVisible('Accounts')
// 2. Actions — user interactions
space.clickAccountItemByIndex(0)
// 3. Assertions — verify outcomes (grouped at the end)
space.verifyAccountRowDetails('single', 0, { name: 'My Safe', address: '0x...' })
})
'Verify that [expected behavior]'cy.get(), cy.url().should(), cy.contains().click() must be in a page object functioncy.wait(N) hard waits: use assertion-based waits (cy.get(sel, { timeout: 30000 }).should('be.visible')) or waitFor* functions.only: never commit it.only or describe.onlynonZeroBalanceRegex not $875Regex)cypress/fixtures/, not inline in tests. Use staticSpaces, getSafes(CATEGORIES.static), etc.main.verifyElementsCount for counting elements instead of inline .should('have.length', N)const at the top of the test (e.g. const safeData = staticSpaces.dashboardWithSafes.pendingTxAccount)getSafes(CATEGORIES.static) — never hardcodecypress/fixtures/spaces/staticSpaces.jssupport/localstorage_data.js, keys in support/constants.jscy.intercept() + cy.fixture() from fixtures/When a component needs a data-testid for E2E tests:
AccountWidgetItem for single-chain vs ExpandableAccountItem for multichain) — they need distinct test-idsdata-testid to the correct element in the correct componentdata-testid in source is referenced in at least one page object selectorKeep test data and UI selectors in separate places — never mix them.
cypress/fixtures/) — test data onlye2e/pages/) — UI selectors and labels onlydata-testid selectorsaria-label selectorsconst in the page objectimport staticSpaces from '../../fixtures/spaces/staticSpaces.js')After writing or refactoring tests, verify:
export keywords — only export what test files importconst/function (not export)cy.wait(N) hard waits (except in legacy flows pending refactor).only left in testsdata-testid added to source components are referenced in Cypress page objectsdata-testid values across different components