Migrating from TestCafe to Playwright: a real case โ
I took part in the full migration of a TestCafe E2E suite (3+ years of history, ~383 tests) to Playwright with TypeScript. Here's what I learned โ the numbers, the method, and the traps.
Why we migrated โ
- Execution time: TestCafe runs were essentially sequential; with the suite growing, CI became a bottleneck.
- Cookies and API calls: preparing data via API inside the tests (needing the session token) was a constant fight with TestCafe's native methods.
- Flakiness, especially in tests acting on an iframe (an embedded visual editor โ our worst case).
The PoC: validate the framework against your worst tests โ
Instead of migrating the easy tests to show off a pretty PoC, we deliberately picked the flakiest tests and the iframe ones (~21 tests across all domains). If Playwright survived that, it would survive anything.
Measured results (same suite, Firefox):
| Suite | TestCafe (sequential) | Playwright (console, parallel) |
|---|---|---|
| Domain A โ 6 tests | 2 min 56 s (with 2/6 failing) | 30.5 s (6/6 green) |
| Suite B โ 2 tests | 1 min 20 s | 24.2 s |
~5-6ร faster and more stable: tests that failed in TestCafe passed in Playwright. The PoC also validated the critical bits: iframes (FrameLocator), getting the session token from the context cookies (trivial in Playwright), tags, HTML report, parallelization, and running in Docker with the official image.
A method detail: the PoC was done in JavaScript (our language at the time โ that way we were evaluating the framework, not the language) and the final project in TypeScript.
Estimate the migration with data, not faith โ
The part I've seen done wrong most often. Our method:
- Inventory: every test by domain and type (sanity/regression/key workflow) โ 383 in total, 362 remaining after the PoC.
- Real velocity measured in the PoC: a senior QA migrated 6 tests/day (including the JSโTS learning curve).
- Plan: 4 QAs ร ~6 tests/day โ 16 days per person (~3 weeks) โ explicitly declared as an optimistic scenario, with the risks listed: learning curve, hard-to-locate elements, squad priorities, releases, and reviews.
Two hygiene rules that worked very well:
- Start with the sanity suite: coverage of the core stuff as early as possible; the long regression suite afterwards.
- Delete each TestCafe file as soon as its content is migrated. In a mixed repo, zombie code confuses and duplicates maintenance; plus, progress stays visible.
The mechanics of the migration โ
Tests: the main change is instantiating the page objects with page:
// TestCafe
await mainMenuTasks.goTo('AUDIT-LOG');
await auditLogQuestions.validateTitleExists();// Playwright
const mainMenu = new MainMenu(page);
await mainMenu.goTo('AUDIT-LOG');
const auditLog = new AuditLog(page);
await auditLog.validateTitleExists();POM: from three folders per page (UI/Tasks/Questions) to one class per page; TestCafe's global t object disappears in favor of methods over typed locators.
Assertions โ the subtle trap that explains the "residual flakiness" after migrating:
// โ literally migrated pattern: does NOT retry
await expect(this.success.innerText()).resolves.toBe(msg);
expect(await locator.isVisible()).toBe(true);
// โ
web-first assertions: retry until the timeout
await expect(this.success).toHaveText(msg);
await expect(locator).toBeVisible();API services and data: same classes in .ts, session token from the context cookies (getAccessToken(page)), and the mock data converted into typed factories with random generation.
Least privilege by default: each entity is tested with a user that has only the permission for that action (not the admin). Trick: the backend's OpenAPI spec tells you which permission each endpoint requires.
Lessons โ
- PoC against your worst tests, not your prettiest ones.
- Measure your migration velocity before committing to a plan โ and list the risks.
- PoC in the language you know, product in the right language.
- Aligning with the frontend team's stack (TypeScript) brought standards and support for free.
- Short "how to migrate X" guides (tests, POM, API, data) let 4 people parallelize without stepping on each other.
- Web-first assertions are half the stability you gain with Playwright โ use them from the very first migrated test.