Protractor Is Deprecated: What That Actually Means for Your Angular Test Suite
Protractor is deprecated and unmaintained — here's what that actually breaks first, what a real migration looks like, and the one case where staying put is defensible.
01 Mar 2026, 00:01 UTC

If your Angular app's end-to-end tests still run on Protractor, you're maintaining a test framework that its own authors have stopped maintaining. Protractor was officially deprecated by the Angular team around Angular 12, with support ending after 2022 — no new features, no security fixes, no ChromeDriver compatibility work. The practical question isn't whether Protractor is good or bad; it's whether staying on it is a deliberate, bounded decision or just inertia.
This post lays out what Protractor actually gave you, what breaks first if you stay, and how to size up a migration before committing to one.
What Protractor gave you that nothing else did
Protractor's signature feature was automatic synchronization with Angular. Because it hooked into zone.js (the library Angular uses to track async work), a Protractor test would implicitly wait for pending HTTP requests and timers to settle before running the next command. No sleep(2000) calls, no manual wait loops — you clicked a button and the next assertion ran after the app was actually ready.
It also shipped Angular-aware locators: by.model('user.name'), by.binding('total'), and by.repeater('row in rows') let you target elements by their Angular bindings instead of CSS classes that designers rename every sprint.
Both features came with a hard boundary: they only work on Angular pages. Hybrid apps that mix Angular and non-Angular routes required toggling browser.waitForAngularEnabled(false), and getting that wrong produced flaky tests that were miserable to debug.
What breaks first if you stay
The failure mode of an unmaintained E2E framework isn't a dramatic shutdown — it's slow compatibility drift. The usual order:
- ChromeDriver drift. Chrome auto-updates; the WebDriver manager that downloads matching drivers does not keep pace in an unmaintained toolchain. Suites start failing on CI with session-creation errors, not test failures.
- Node and dependency rot. Old transitive dependencies accumulate unpatched vulnerabilities, and newer Node versions can break the toolchain.
- Promise-manager friction. Protractor's control flow managed WebDriver promises for you, which conflicts with modern
async/await. TheSELENIUM_PROMISE_MANAGER=0opt-out was a common halfway step, but it's a patch, not a future.
None of this means your suite stops working tomorrow. It means the maintenance cost rises every quarter while the value stays flat.
Worked example: translating one spec
The bulk of migration effort is usually locator rewrites, not framework mechanics. Here's a representative Protractor spec:
// Protractor
await element(by.model('search.query')).sendKeys('invoice');
await element(by.buttonText('Search')).click();
const first = element.all(by.repeater('r in results')).first();
expect(await first.getText()).toContain('INV-1042');The same flow in Playwright, using stable data-testid attributes instead of Angular bindings:
// Playwright (TypeScript)
await page.getByTestId('search-input').fill('invoice');
await page.getByRole('button', { name: 'Search' }).click();
await expect(page.getByTestId('result-row').first())
.toContainText('INV-1042');Playwright's locators auto-wait for elements to be actionable, which covers most of what Protractor's Angular synchronization did — as long as your app renders elements only when data is ready. If your app renders a shell and mutates text later, you'll need explicit assertions like the toContainText above (which retries) rather than one-shot reads.
The cost driver is adding data-testid attributes to templates. That's real work in a large app, but it's also the durable part of the migration: those attributes survive any future framework change.
The honest trade-off: when staying is defensible
There is one case where keeping Protractor is a rational decision: a legacy AngularJS (1.x) application in maintenance mode, where Protractor's by.model/by.repeater locators map directly onto the app's bindings and a full rewrite would cost more than the app's remaining lifespan justifies. In that scenario, pin your Chrome/ChromeDriver versions in CI (run tests in a fixed container image), freeze dependencies, and treat the suite as frozen infrastructure.
For anything actively developed — especially anything on a supported Angular version — that calculus doesn't hold. You're paying rising maintenance costs to avoid a one-time migration.
Before you commit: three checks
- Verify the current status. Deprecation timelines and community forks may have shifted since the original announcement. Check the official Protractor GitHub repository and Angular docs before citing dates in a migration proposal.
- Inventory your locators. Count usages of
by.model,by.binding, andby.repeateracross the suite. A quickgrep -r "by\.model\|by\.binding\|by\.repeater" e2e/from your repo root gives you a rough effort estimate — that count correlates with migration cost better than spec count does. - Run a proof of concept. Pick one representative flow — ideally one with async HTTP and route changes — and port it to Playwright or Cypress. Confirm the auto-waiting actually covers your app's behavior before committing the whole suite.
The decision isn't "Protractor vs. the new hotness." It's whether your E2E suite is an asset that keeps pace with your app, or a liability you're quietly paying interest on. A one-afternoon locator inventory and a single ported spec will tell you which situation you're in — and that's a much better basis for the conversation than another quarter of hoping ChromeDriver cooperates.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.