How to Migrate from Protractor to Cypress
Migrate Protractor tests to Cypress in stages: set up an Angular workspace, translate interactions and waits, run both suites, and update CI safely.
To migrate from Protractor to Cypress, inventory the coverage you have, add Cypress to your Angular workspace, port a small set of representative tests, and run the new specs alongside Protractor until they are reliable. You do not need to rewrite the whole suite at once.
Protractor reached end-of-life in August 2023. Its project website recommends that existing users migrate. Cypress notes that Protractor stopped being included in new Angular projects as of Angular 12; that change to Angular defaults is separate from Protractor’s end-of-life. See the Protractor project website and Cypress’s migration guide.
1. Inventory the existing suite
Before changing runner configuration, make a short map of what the suite protects. This helps choose a representative first migration and avoid dropping coverage during the transition.
- List important user journeys and the Protractor specs that cover them.
- Identify shared page objects, helper functions, custom locators, and test data setup.
- Find uses of
waitForAngular(), fixed delays, browser-specific setup, and custom assertions. - Record how CI starts the application, selects browsers, and reports failures.
- Note any tests that depend on Angular-specific behavior or third-party services.
Choose an early test that is stable, valuable, and representative of common interactions. Do not begin by porting every helper or by translating syntax without checking what each test is intended to prove.
2. Add Cypress to the Angular workspace
Recommended setup: Angular schematic
ng add @cypress/schematic
Cypress recommends this schematic for Angular projects. It installs Cypress, adds open and run scripts, and scaffolds Cypress files and directories. During setup, it can also prompt you to remove Protractor and point Angular CLI’s default ng e2e target at Cypress. Review those prompts against your staged migration plan before accepting changes that remove the existing runner.
The guide documents these Angular CLI commands after schematic setup:
ng e2e
ng run my-app:cypress-open
ng run my-app:cypress-run
Replace my-app with the project name in your workspace. The schematic also supports choosing a browser with --browser or configuring a default browser. Check the generated scripts and workspace targets, since names and project configuration vary by workspace.
Manual installation
Manual installation is an option when you do not want the schematic to configure the Angular workspace. Install Cypress using the package manager already used by the repository, then add Cypress configuration and scripts appropriate to the project. The application must be available at a known URL while Cypress runs. A helper such as concurrently can start the development server and Cypress together, but it is optional; CI can also start the server as a separate step.
Use the commands and configuration supported by the Cypress and Angular versions in the repository. The migration guide’s setup instructions are the primary reference; there is no single universal Angular workspace configuration.
3. Port test behavior, not just syntax
Cypress uses retryable queries and chained commands, so many Protractor interactions have straightforward counterparts. Treat these as starting points, not a guarantee that every custom locator or helper has a one-to-one replacement.
| Protractor pattern | Cypress pattern |
|---|---|
element(by.css('#email-field')) |
cy.get('#email-field') |
.sendKeys('text') |
.type('text') |
| Click a checkbox | .check() |
| Uncheck a checkbox | .uncheck() |
| Select an option | .select('value') |
| Scroll an element into view | .scrollIntoView() |
| Find matching text | A Cypress query such as .contains(...) |
For example, convert an interaction and assert the resulting application state:
// Protractor
element(by.css('input')).sendKeys('my text');
element.all(by.css('[type="checkbox"]')).first().click();
// Cypress
cy.get('input').type('my text');
cy.get('[type="checkbox"]').first().check();
cy.get('[type="checkbox"]').first().should('be.checked');
Review selectors as part of the port. Prefer selectors that identify the intended control reliably; Cypress also points to Testing Library commands as an option for element selection. For each translated action, preserve the original test’s intent and assert a meaningful result rather than stopping after the click or typing command.
4. Replace Angular waits with observable conditions
Protractor suites may use waitForAngular() or fixed delays to wait for the application. Cypress retries DOM queries and waits for elements to become actionable, subject to command timeout settings. Where possible, wait for the expected UI state with a query and assertion:
// Avoid using an arbitrary delay as a substitute for a condition
cy.wait(2000);
// Express the condition the test needs
cy.get('[data-testid="results"]').should('be.visible');
cy.contains('Saved').should('be.visible');
This retry behavior applies to Cypress’s supported queries and assertions; it does not mean every asynchronous process is automatically complete. If a test depends on a specific network response or external process, identify that dependency and wait for a meaningful condition. Use a fixed cy.wait(number) only when a real, deliberate timing requirement cannot be represented another way.
When an element exists but is covered, disabled, detached, or not yet actionable, inspect the application state and locator. Raising a timeout may help with a genuinely slow operation, but it can also hide a synchronization problem if used indiscriminately.
5. Run Protractor and Cypress side by side
Cypress documents keeping Protractor tests in the existing Angular CLI e2e directory and placing Cypress specs in a sibling cypress directory. The suites can coexist while the team migrates gradually.
- Port one stable user journey and run it locally.
- Add it to CI while retaining the corresponding Protractor coverage.
- Compare what each test asserts and investigate differences in failure behavior.
- Repeat for the next journey, keeping track of coverage that has moved.
- Remove a Protractor test only after its replacement is dependable in the team’s actual environment.
This staged rollout preserves existing coverage while Cypress specs are introduced. It also gives the team a chance to update test helpers and CI configuration incrementally.
6. Update local and CI commands
With schematic setup, the Cypress guide documents ng run {project}:cypress-open for the interactive runner and ng run {project}:cypress-run for a run. The schematic also supports browser selection and optional CI recording and parallelization configuration.
For manual setup, define scripts that run Cypress against the application URL. Decide whether the app server starts in a preceding CI step or alongside Cypress with a helper process. Ensure the Cypress command waits for the server to be ready; otherwise, startup races can appear as application load failures.
Cypress’s guide describes optional Cypress Cloud recording and parallelization, including the --record and --parallel run options. These require service configuration and a recording key. Consider them when CI scaling or debugging recorded runs is a need; they are not required for a local or headless Cypress migration. The guide also describes Test Replay for debugging recorded runs. Check Cypress’s current documentation for the configuration supported by the installed version.
7. Troubleshoot common migration problems
| Symptom | Likely cause | What to do |
|---|---|---|
ng e2e still runs Protractor or has no Cypress target |
The schematic target was not selected, or the workspace uses a different project target. | Inspect the Angular workspace configuration and use the generated Cypress target command, such as ng run my-app:cypress-run. |
| Cypress cannot load the application | The app server is not running, the base URL is wrong, or CI starts Cypress before the server is ready. | Start the app explicitly, verify the URL, and make the CI step wait for server readiness. |
| A query times out | The selector does not match, the expected state never occurs, or the page is slower than the configured command timeout. | Check the selector and assertion, inspect the rendered state, and adjust timeout only when the operation is legitimately slow. |
| A migrated test is flaky after replacing a fixed delay | The delay was masking an asynchronous condition that the new test does not yet assert. | Identify the intended condition and wait for a retryable query, visible state, or specific dependency. |
.check() or .select() fails |
The element is not the expected checkbox or select, is disabled, or is not actionable. | Confirm element type and state, then assert the resulting checked or selected value. |
| Browser works locally but not in CI | The CI browser, application startup, or workspace command differs from local setup. | Use the configured browser option, verify installed browser availability, and run the same project target in CI. |
| Recorded or parallel run does not start | Recording configuration, key, or service setup is missing. | Remove recording options for a basic run, or configure the required Cypress Cloud settings for recorded parallel runs. |
8. Performance, reliability, and cost considerations
Migration performance depends on the application, test design, browser, and CI setup; the cited migration guide does not provide a universal speed comparison. Start by making tests deterministic and selecting clear assertions. Then measure your own CI run before deciding whether to add parallelization or recorded runs.
Reliability improves when tests assert observable application outcomes instead of relying on arbitrary delays, and when the suite remains in staged coexistence until replacements have proved useful in the project’s environment. Keep a record of which journeys have migrated so a runner change does not silently reduce coverage.
The migration guide describes Cypress Cloud as an optional service for recording, parallelization, and replay workflows. Evaluate its configuration and cost against the team’s CI and debugging needs. The research sources do not establish a universal cost for Cypress Cloud, so check its current pricing before adopting it.
Or skip the browser setup
If your migration work also needs screenshots of pages for documentation or review, ScreenshotNeo is a website screenshot API and MCP server. One GET request returns a PNG, JPEG, WebP, or PDF. Cookie banners, popups, and chat widgets are removed before the shot; bot checks, blank pages, and failed loads are never billed. Its MCP server lets AI agents take screenshots. The free plan includes 1,000 screenshots a month with no card, and paid plans start at $5 for 3,000. See the API documentation.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
Sign up for 1,000 free screenshots a month with no card.
FAQ
Do I have to replace all of my tests with Cypress immediately?
No. Cypress’s migration guide describes progressive migration, with existing Protractor tests and new Cypress specs living in separate folders.
Can Protractor and Cypress coexist in the same app?
Yes. Keep the Protractor suite in its existing end-to-end directory and add Cypress specs in a sibling directory while the migration proceeds.
Does Cypress require Angular?
No. The Angular schematic is a recommended setup path for Angular workspaces; Cypress can also be installed and configured manually.
Do I need Cypress Cloud to use Cypress?
No. The guide presents recording, parallelization, and replay as optional workflows for teams that need them.


