Skip to main content

Cypress Component Testing

What we are testing​

Snappycart is a UI package. Cypress Component Testing gives us the fastest feedback loop for browser-rendered behaviour without needing to run full end-to-end flows.

This page covers component-level behaviour for Snappycart UI pieces and their mounted integration shape. It does not replace unit tests for reducers or hooks, and it does not replace end-to-end coverage in the demo app.

For the full test inventory and the recommended number of tests across all layers, read the Cart testing plan.

Scope of Cypress Component Testing​

Cypress CT is the right place for:

  • rendered UI behaviour
  • component interaction in a real browser DOM
  • focus management
  • keyboard and pointer interaction
  • state transitions visible through mounted components
  • provider-backed component integration when a component depends on cart context

Cypress CT is not the main place for:

  • reducer-only logic
  • hook guard behaviour in isolation
  • full app routing or full storefront journeys

Those belong in unit tests or end-to-end tests.

Where component tests live​

Component tests should live next to the component they cover, not in a central cypress/component folder.

Use co-located spec files such as:

packages/snappycart/src/cart/components/CartDrawer.cy.tsx
packages/snappycart/src/cart/components/CartIcon/CartIcon.cy.tsx
packages/snappycart/src/cart/components/CartIntegration.cy.tsx

Selector contract​

We use data-cy as a stable test contract. Do not rely on CSS classes for component tests.

Core selectors:

  • cart-icon
  • cart-badge
  • cart-drawer
  • cart-overlay
  • cart-drawer-title
  • cart-close
  • cart-empty
  • cart-subtotal
  • cart-clear

Per-item selectors:

  • cart-item-<id>
  • cart-inc-<id>
  • cart-dec-<id>
  • cart-qty-value-<id>
  • cart-remove-<id>

If a new visible cart interaction is introduced, add a stable data-cy selector as part of the same change.

Starter spec​

Start with a co-located file:

packages/snappycart/src/cart/components/CartDrawer.cy.tsx
import CartDrawer from './CartDrawer';
import { CartProvider } from '../context/CartProvider';

const sel = (id: string) => `[data-cy="${id}"]`;

function mountDrawer(params?: { open?: boolean; onClose?: () => void; title?: string }) {
const open = params?.open ?? true;
const onClose = params?.onClose ?? (() => {});
const title = params?.title ?? 'Your Cart';

cy.mount(
<CartProvider>
<CartDrawer open={open} onClose={onClose} title={title} />
</CartProvider>,
);
}

describe('CartDrawer (CT)', () => {
it('does not render when closed', () => {
mountDrawer({ open: false });
cy.get('body').find(sel('cart-drawer')).should('not.exist');
});

it('renders empty state when there are no items', () => {
mountDrawer({ open: true });

cy.get(sel('cart-drawer-title')).should('be.visible');
cy.get(sel('cart-drawer-title')).should('contain', '(0)');
cy.get(sel('cart-empty')).should('be.visible');
});

it('focuses the Close button on open', () => {
mountDrawer({ open: true });

cy.get(sel('cart-close')).should('be.focused');
});

it('calls onClose when pressing Escape', () => {
const onClose = cy.stub().as('onClose');
mountDrawer({ open: true, onClose });

cy.get(sel('cart-close')).should('be.focused');
cy.window().trigger('keydown', { key: 'Escape' });

cy.get('@onClose').should('have.been.calledOnce');
});

it('calls onClose when clicking the overlay', () => {
const onClose = cy.stub().as('onClose');
mountDrawer({ open: true, onClose });

cy.get(sel('cart-overlay')).click({ force: true });

cy.get('@onClose').should('have.been.calledOnce');
});
});

Some component transitions require cart state. In those cases, mount the UI inside a small harness that wraps CartProvider and exposes a controlled way to seed or mutate the cart through public actions.

Typical harness responsibilities:​

  • wrap in CartProvider
  • render CartIcon
  • render CartDrawer
  • expose one or two deterministic add-item buttons
  • optionally manage local open state for the drawer

This allows contributors to test real browser-visible transitions without reaching into internal implementation details.

Component state and transition matrix​

Use this table to decide what to cover in Cypress Component Testing. Each row describes a browser-visible state transition that should be validated through mounted components.

IDComponentCurrent stateTrigger / eventExpected next stateWhat to assert in CT
CT-01CartDrawerDrawer closed, cart emptyMount with open={false}Drawer remains absent from DOMcart-drawer does not exist
CT-02CartDrawerDrawer open, cart emptyMount with open={true}Empty state is showncart-empty visible, title shows (0)
CT-03CartDrawerDrawer open, cart emptyInitial renderClose button receives focuscart-close is focused
CT-04CartDrawerDrawer openPress EscapeDrawer requests closeonClose called
CT-05CartDrawerDrawer openClick overlayDrawer requests closeonClose called
CT-06CartIconEmpty cartMount inside providerBadge shows empty or zero state according to speccart-badge matches expected empty behaviour
CT-07CartIconCart has 1 itemSeed one item through harnessBadge updates to 1cart-badge contains 1
CT-08CartIconCart has multiple itemsSeed multiple quantities or itemsBadge reflects total item countcart-badge contains expected total
CT-09CartIcon + CartDrawerDrawer closed, cart emptyClick cart iconDrawer opens in empty statecart-drawer visible and cart-empty visible
CT-10CartIcon + CartDrawer + CartProviderEmpty cart, drawer closedAdd first item through harnessCart becomes non-emptyBadge updates and title count updates when opened
CT-11CartDrawer + CartProviderDrawer open, cart has 1 item with qty 1Open drawer after seeding first itemPopulated item row is showncart-item-<id> visible and quantity is 1
CT-12CartDrawer + CartProviderDrawer open, cart has 1 item with qty 1Click incrementQuantity becomes 2cart-qty-value-<id> shows 2, subtotal updates
CT-13CartDrawer + CartProviderDrawer open, cart has 1 item with qty 2Click decrementQuantity becomes 1cart-qty-value-<id> shows 1, subtotal updates
CT-14CartDrawer + CartProviderDrawer open, cart has 1 item with qty 1Click decrement if decrement removes at 1Cart returns to empty stateItem row disappears and empty state is shown
CT-15CartDrawer + CartProviderDrawer open, cart has 1 item with qty 1Click removeItem is removedItem row disappears and empty state is shown
CT-16CartDrawer + CartProviderDrawer open, cart has multiple distinct itemsSeed two different itemsMultiple rows are renderedBoth rows visible and subtotal matches sum
CT-17CartDrawer + CartProviderDrawer open, cart has multiple itemsRemove one itemRemaining item stays visibleRemoved row absent, remaining row visible, subtotal recalculated
CT-18CartDrawer + CartProviderDrawer open, cart has one remaining itemRemove last itemCart returns to empty stateEmpty state shown and subtotal cleared or hidden according to spec
CT-19CartDrawer + CartProviderDrawer open, cart has itemsClick clear buttonCart becomes emptycart-clear empties cart and empty state is shown
CT-20CartDrawer + CartProviderDrawer open, subtotal visibleIncrement or decrement quantitySubtotal recalculates immediatelycart-subtotal updates to expected value
CT-21CartDrawer + CartProviderDrawer open after state changesClose and reopen drawerState remains consistentSame items, same quantities, same subtotal
CT-22CartIcon + CartDrawer + CartProviderCart has items, drawer closedClick icon, then close via overlay or EscapeDrawer closes without losing cart stateDrawer hides and badge still shows current count
CT-23CartDrawer + CartProviderCart contains visible product metadataSeed representative productProduct content renders correctlyExpected product name or visible fields are shown
CT-24CartDrawer + CartProviderCart has enough items for repeated interactionsIncrement, decrement, remove in sequenceUI stays in sync after each transitionCount, rows, and subtotal remain correct after each step

How to use this matrix​

When adding or reviewing a Cypress component test:

  • identify the component under test
  • identify the current state
  • identify the visible transition
  • assert the next visible browser state
  • prefer one meaningful transition per test

Every new Cypress CT contribution should map to at least one row in the matrix above.

Coverage guidance for contributors​

Prioritise these rows first:​

  • empty CartDrawer behaviour
  • CartIcon badge behaviour
  • first item added
  • quantity increase and decrease
  • remove item
  • remove last item
  • subtotal recalculation
  • icon and drawer interaction through provider-backed mounting

Avoid duplicating the same transition in multiple slightly different tests. Prefer meaningful behavioural coverage over repetitive assertions.