Cypress Component Isolation Issues

cypress testing playwright e2e

Working on a personal project, I hit a frustrating limitation with Cypress: component state bleeding between tests.

The Problem

Cypress mounts components in the same browser context across tests. Even with beforeEach cleanup, some state persists:

Example that failed intermittently:

describe('User form', () => {
  beforeEach(() => {
    cy.mount(<UserForm />)
  })

  it('validates email', () => {
    cy.get('[data-testid="email"]').type('invalid')
    cy.get('[data-testid="error"]').should('contain', 'Invalid email')
  })

  it('submits successfully', () => {
    cy.get('[data-testid="email"]').type('valid@example.com')
    cy.get('[data-testid="submit"]').click()
    // Fails intermittently - previous test's error message still in DOM
  })
})

Why This Happens

Cypress reuses the browser instance. Components unmount, but:

The Workaround

Force full remount between tests:

afterEach(() => {
  cy.window().then((win) => {
    win.location.reload()
  })
})

This works but adds ~500ms per test. With 200+ component tests, that’s 100 seconds of wasted time.

Playwright Does This Better

Playwright’s component testing uses isolated browser contexts per test. Each test gets:

Same test in Playwright:

test('submits successfully', async ({ mount }) => {
  const component = await mount(<UserForm />)
  await component.getByTestId('email').fill('valid@example.com')
  await component.getByTestId('submit').click()
  // Always works - completely isolated from previous test
})

No manual cleanup. No intermittent failures. No performance workaround.

Migration Considerations

Switching to Playwright would mean:

The isolation model is compelling. Cypress is great for E2E, but for component testing, Playwright’s architecture is superior.

Might be time to migrate.

← Back to notes