Protractor hangs on non-Angular pages? Disable synchronization and wait explicitly
How to turn off Protractor's automatic Angular synchronization for non-Angular content, and why explicit waits become mandatory once you do.
12 Oct 2025, 04:57 UTC

A page that never finishes loading Angular
Your Protractor spec opens a login screen, clicks a button, and then... nothing. The browser sits idle until the test times out. The page is not an Angular application, or at least the part you are testing is not. Protractor's default behavior is to wait for Angular to stabilize before every command. When Angular never initializes, that wait never ends.
Protractor is built on WebDriverJS and adds automatic synchronization on top of it. Before each action, it checks that Angular has finished rendering, that $http requests are complete, and that $timeout callbacks have run. This is helpful for AngularJS and Angular applications, but it is the wrong default for a legacy HTML form, a third-party widget, or a hybrid page where only part of the DOM is Angular-managed.
Turn synchronization off for the right scope
Call browser.waitForAngularEnabled(false) to stop Protractor from waiting for Angular. Place this call before you interact with non-Angular content, and call browser.waitForAngularEnabled(true) afterward if you return to Angular pages in the same session.
Disabling synchronization removes the safety net. Without it, Protractor will not pause for network requests or DOM updates. You must replace it with explicit waits, or the test may click an element before it exists and fail intermittently.
Configuration check
Open protractor.conf.js in your project root. Confirm the framework, spec files, and base URL match the application under test. There is no setting in this file that disables synchronization globally; you control it inside the spec.
// protractor.conf.js
exports.config = {
framework: 'jasmine',
specs: ['specs/*.js'],
baseUrl: 'https://.example.com',
capabilities: {
browserName: 'chrome'
}
};
Run specs from the project root with:
npx protractor protractor.conf.js
This command requires no special permissions, but it does require a compatible Node.js installation, the Protractor package, and a browser driver that matches your installed browser version. Version mismatches can prevent the browser session from starting.
Worked example: non-Angular login widget
Imagine a page where the navigation shell is Angular, but the login form is a plain HTML widget. The following spec disables synchronization before the form, waits explicitly for the username field, then re-enables synchronization for the Angular dashboard.
const { browser, element, by, ExpectedConditions } = require('protractor');
describe('hybrid login page', () => {
it('logs in through a non-Angular widget', async () => {
// Stop waiting for Angular before touching the legacy form.
await browser.waitForAngularEnabled(false);
await browser.get('/legacy-login');
const username = element(by.id('username'));
const password = element(by.id('password'));
const submit = element(by.id('submit'));
// Replace automatic synchronization with an explicit wait.
await browser.wait(
ExpectedConditions.presenceOf(username),
5000,
'Username field did not appear'
);
await username.sendKeys('');
await password.sendKeys('');
await submit.click();
// Re-enable synchronization once the Angular dashboard loads.
await browser.waitForAngularEnabled(true);
});
});
Placeholders such as , , and represent values you supply from your environment. Do not commit real credentials to the spec file.
Expected behavior: the browser opens the legacy login page, waits up to five seconds for the username field, fills the form, submits it, and then resumes waiting for Angular on the next page. If the field never appears, the explicit wait throws a timeout error instead of hanging indefinitely on Angular stabilization.
Trade-off: explicit waits are more brittle
The main limitation is maintenance. Automatic synchronization removes the need to guess when an Angular page is ready. Once you disable it, every interaction with dynamic content needs its own browser.wait call. If the application changes its markup or timing, the explicit wait can fail even when the feature works.
There is a larger decision behind this tactic. The Angular team deprecated Protractor and ended active maintenance, pointing teams toward Cypress, Playwright, or WebdriverIO. Disabling synchronization keeps an existing Protractor suite running, but it does not make Protractor a good choice for new long-term projects. Treat this as a bridge while you plan migration, not as a reason to expand Protractor coverage.
Verify before you rely on it
To confirm the change works, run the same spec twice: once with synchronization enabled against the non-Angular page, and once with browser.waitForAngularEnabled(false) plus the explicit wait above. The first run should hang or time out; the second should proceed or fail fast on the missing element.
Also inspect protractor.conf.js and check that installed Protractor, Selenium WebDriver, and browser driver versions are compatible with your Node.js version. Because Protractor is no longer actively maintained, verify behavior after any Node or browser upgrade.
If you are starting a new end-to-end suite today, choose a maintained framework instead. If you must keep Protractor alive for an existing hybrid application, disable synchronization only where Angular is absent, and always replace it with an explicit wait.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.