cypress-to-playwright
Migrate Cypress end-to-end tests to Playwright. This codemod transforms test structure, selectors, actions, assertions, and navigation commands.
Migration Approach
This codemod uses a two-phase approach to ensure reliable transformations:
-
AST-based transformations (automatic, reliable): Handles standard Cypress patterns using traditional AST-based codemods. These transformations are deterministic and cover the majority of common patterns.
-
AI-assisted transformations (optional): Handles tricky cases that require context-aware conversion. When enabled via Codemod Campaign, AI will automatically migrate complex patterns that were marked with TODO comments by the AST-based step.
What Gets Transformed
AST-Based Transformations (Automatic)
The following transformations are handled reliably by AST-based codemods:
Test Structure
| Cypress | Playwright |
|---|---|
describe() | test.describe() |
it() | test() |
before() | test.beforeAll() |
beforeEach() | test.beforeEach() |
after() | test.afterAll() |
afterEach() | test.afterEach() |
Selectors and Actions
| Cypress | Playwright |
|---|---|
cy.get('.selector') | page.locator('.selector') |
cy.contains('text') | page.getByText('text') |
cy.get('.el').click() | await page.locator('.el').click() |
cy.get('.el').type('text') | await page.locator('.el').fill('text') |
cy.get('.el').check() | await page.locator('.el').check() |
cy.get('.el').select('opt') | await page.locator('.el').selectOption('opt') |
cy.get('.el').first() | page.locator('.el').first() |
cy.get('.el').last() | page.locator('.el').last() |
cy.get('.el').eq(n) | page.locator('.el').nth(n) |
cy.get('.el').find('.child') | page.locator('.el').locator('.child') |
Assertions
| Cypress | Playwright |
|---|---|
.should('be.visible') | await expect(...).toBeVisible() |
.should('exist') | await expect(...).toBeAttached() |
.should('have.text', 'x') | await expect(...).toHaveText('x') |
.should('contain', 'x') | await expect(...).toContainText('x') |
.should('have.value', 'x') | await expect(...).toHaveValue('x') |
.should('have.class', 'x') | await expect(...).toHaveClass(/x/) |
.should('have.attr', 'a', 'v') | await expect(...).toHaveAttribute('a', 'v') |
.should('be.disabled') | await expect(...).toBeDisabled() |
.should('have.length', n) | await expect(...).toHaveCount(n) |
.should('not.exist') | await expect(...).not.toBeAttached() |
Navigation
| Cypress | Playwright |
|---|---|
cy.visit('/path') | await page.goto('/path') |
cy.reload() | await page.reload() |
cy.go('back') | await page.goBack() |
cy.go('forward') | await page.goForward() |
cy.wait(1000) | await page.waitForTimeout(1000) |
cy.url().should('include', '/x') | await expect(page).toHaveURL(/\/x/) |
cy.title().should('eq', 'x') | await expect(page).toHaveTitle('x') |
Other Transformations
| Cypress | Playwright |
|---|---|
cy.clearCookies() | await page.context().clearCookies() |
cy.screenshot('name') | await page.screenshot({ path: 'name.png' }) |
cy.viewport(w, h) | await page.setViewportSize({ width: w, height: h }) |
Configuration Migration
The codemod also automatically migrates Cypress configuration files:
cypress.config.ts→playwright.config.ts- Converts Cypress-specific settings to equivalent Playwright configuration
Custom Command Detection
The codemod scans for custom Cypress commands in support files and reports them for review. These are typically handled by the optional AI-assisted step when enabled.
AI-Assisted Transformations (Optional)
When running via Codemod Campaign with autoAIReview enabled (default), the AI will automatically handle these tricky patterns that cannot be reliably transformed by AST-based codemods:
-
.then()patterns (includingcy.visit().then()):cy.visit().then((window) => {...})→ Usespage.evaluate()to access window objectlocator.then((el) => {...})→ Extracts logic and useslocator.evaluate()or locator methods- Generic
.then()callbacks → Converts to async/await patterns with proper Playwright APIs
-
Custom Cypress commands:
- Custom commands like
cy.nextStep(),cy.prevStep(),cy.compareSnapshot(), etc. - AI analyzes the command definition and converts to appropriate Playwright helpers or
page.evaluate()
- Custom commands like
-
Complex
.should()callbacks:- Assertions with callback functions that contain custom logic
- Converts Cypress assertions within callbacks to Playwright matchers
These patterns are initially marked with TODO comments by the AST-based codemod, then automatically resolved by AI when the optional AI step is enabled.
Requires Manual Migration
Some Cypress features may still require manual attention:
cy.intercept()→ Usepage.route()in Playwright (may be handled by AI if simple cases)cy.wait('@alias')→ Usepage.waitForResponse()or similar (may be handled by AI)cy.task()→ Use Playwright fixtures or global setup
Example
Before (Cypress)
typescript
After (Playwright)
typescript