Which migration strategy best preserves Protractor-specific locators?
0 reputation · 09 Jan 2022, 07:59 UTC
0 reputation · 09 Jan 2022, 07:59 UTC
Protractor provides specialized locators such as by.model(), by.binding(), and by.repeater() that hook directly into Angular's internal state. These finders simplify element selection by targeting the application's data-binding logic rather than relying solely on DOM attributes or CSS selectors.
As Protractor has reached end-of-life and the Angular team has deprecated the tool, teams must migrate to alternatives like Playwright, Cypress, or WebdriverIO. However, these successors do not natively support Angular-specific locators, necessitating a complete rewrite of the selection logic across the test suite.
When moving away from a legacy suite that relies heavily on these internal hooks, there is uncertainty regarding the most maintainable replacement strategy to avoid fragile CSS selectors.
by.model or by.binding into stable data-attributes?34775 reputation · 09 Jan 2022, 15:40 UTC
The short answer: no successor framework — Playwright, Cypress, or WebdriverIO — supports by.model(), by.binding(), or by.repeater() natively, and there is no standardized translation table. The strategy that best preserves the intent of those locators is not translation at all: it is instrumenting the Angular application with stable data-test (or data-testid) attributes before or during the migration, then rewriting selectors against those attributes.
Protractor locators query Angular's internal scope and digest state, not the DOM. Two of them have partial DOM footprints you can exploit as a stopgap:
by.binding('user.name') often corresponds to elements carrying ng-bind="user.name" or the ng-binding class, so [ng-bind="user.name"] is a workable CSS approximation in AngularJS-era templates.by.repeater('item in items') can sometimes be approximated with [ng-repeat="item in items"].But by.model() maps to ng-model only in AngularJS (1.x); in Angular 2+ there is no reliable DOM trace of the bound property at all. Interpolation bindings ({{ user.name }}) also leave no queryable attribute. Any strategy built on scraping these artifacts is fragile and version-dependent — treat it as a bridge, not a target.
data-test="..." attributes to the templates your suite touches. Name them after the component role, not the binding expression.grep -r "data-test" src/ | wc -l against your list of Protractor locator usages tells you how much instrumentation remains.page.getByTestId(), or [data-test="..."] selectors in Cypress/WebdriverIO.ng-binding/ng-repeat approximations above, and flag those tests for later hardening.This costs template changes, which is the real objection — but it is a one-time investment that decouples tests from both the framework and Angular internals, which is exactly what Protractor locators failed to do.
Protractor's automatic wait-for-Angular has no true equivalent. The honest comparison:
cypress-angular-selectors mimics by.model/by.binding semantics. Caveat: it is unofficial, targets specific Angular versions, and should be treated as transitional.Because community plugins and Angular internals change across versions, validate on your stack: run a pilot of 10–20 representative specs in staging, and include at least one flow with asynchronous HTTP-driven binding updates — that is where synchronization differences surface. If timing flakes appear only under load, the issue is synchronization strategy, not selector choice.
Version note: plugin compatibility and framework waiting behavior described here should be re-verified against the current releases of your chosen framework before rollout.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.