Using Explicit Waits in Protractor When Angular Sync Is Disabled
Learn how to disable Protractor’s automatic Angular synchronization and use explicit waits with ExpectedConditions to reliably test third‑party widgets inside iframes.
23 Aug 2026, 03:36 UTC

Problem: Flaky tests when Protractor’s automatic Angular sync fails
Protractor’s waitForAngular feature keeps commands paused until Angular’s zone.js signals stability. This works well for pure Angular pages, but many applications embed third‑party widgets, legacy iframes, or non‑Angular micro‑frontends. When those parts load outside Angular’s digest cycle, waitForAngular either times out or returns too early, causing intermittent failures.
Thesis: Disable automatic sync and rely on explicit browser.wait with ExpectedConditions for deterministic, maintainable tests
By turning off Protractor’s built‑in Angular synchronization and writing targeted explicit waits, you regain control over exactly what the test waits for. This approach reduces flakiness caused by hidden stability checks while keeping the test code readable through the Page Object Model.
When to disable Angular sync
Disable sync only for the portion of the test that interacts with non‑Angular content. Keep it enabled for the rest of the suite so you still benefit from automatic waiting on Angular pages.
// protractor.conf.js
exports.config = {
framework: 'jasmine',
specs: ['specs/**/*.spec.js'],
capabilities: {
browserName: 'chrome'
},
// Turn off global Angular sync; we will re‑enable it locally when needed
getPageTimeout: 10000,
allScriptsTimeout: 15000,
onPrepare: function () {
browser.waitForAngularEnabled(false);
}
};
The onPrepare hook runs once before any spec, disabling sync globally. Remember that this affects every test unless you re‑enable it inside a spec or page object.
Worked example: Waiting for a third‑party chat widget inside an iframe
Imagine a support chat widget loaded from an external domain inside an <iframe>. The widget is not built with Angular, so Protractor’s automatic sync cannot detect when it is ready.
Page Object
// chat-widget.po.js
class ChatWidget {
constructor () {
this.iframe = $('iframe#support-chat');
this.sendButton = $('button.send-message'); // inside the iframe
}
async load () {
// Wait for the iframe element to be present in the DOM
await browser.wait(
protractor.ExpectedConditions.frameToBeAvailableAndSwitchToIt(this.iframe),
8000,
'Chat iframe never became available'
);
// Now we are inside the iframe; wait for the send button to be visible
await browser.wait(
protractor.ExpectedConditions.visibilityOf(this.sendButton),
8000,
'Send button never became visible'
);
}
async sendMessage (text) {
const input = $('textarea.message-input');
await input.sendKeys(text);
await this.sendButton.click();
}
async leaveFrame () {
await browser.switchTo().defaultContent();
}
}
module.exports = new ChatWidget();
Spec using the page object
// specs/chat-widget.spec.js
const ChatWidget = require('../po/chat-widget.po');
describe('Third‑party chat widget', () => {
beforeEach(async () => {
await browser.get('https://example-app.com/dashboard');
});
it('should allow sending a message', async () => {
await ChatWidget.load();
await ChatWidget.sendMessage('Hello from Protractor');
await ChatWidget.leaveFrame();
// Optional: assert something that appears after the message is sent
const status = $('.chat-status');
await browser.wait(
protractor.ExpectedConditions.textToBePresentInElement(status, 'Sent'),
5000
);
expect(await status.getText()).toContain('Sent');
});
});
Where to run: open a terminal in the project root, ensure you have Node ≥ 12 and the Protractor version installed (check package.json), then execute protractor protractor.conf.js. No special permissions are required beyond those needed to launch Chrome or Firefox.
Expected checks
- The test starts, navigates to the dashboard, and pauses at each
browser.waituntil the condition resolves or the timeout expires. - If the iframe appears within the timeout, Protractor switches context and proceeds to wait for the send button.
- After the button is visible, the test types the message, clicks, switches back to the default content, and validates a status update.
- If any wait times out, Protractor throws a timeout error, making the failure explicit and easier to debug.
Trade‑off: Explicit waits increase test duration and maintenance overhead
While explicit waits eliminate hidden flakiness, they add deterministic pauses that can lengthen test runs. Each wait must be carefully chosen; too‑short a timeout yields false negatives, too‑long a timeout wastes CI time. Moreover, you must remember to switch frames or contexts correctly; forgetting to call leaveFrame leaves the driver stuck inside the iframe, causing subsequent commands to fail.
Practical way to check the result: after a test run, examine the Protractor log for lines like "Waiting for condition..." and note the elapsed time. Compare runs with different timeout values to see if the test consistently passes without timeout errors. If the log shows repeated waits hitting the maximum, increase the timeout or investigate why the condition never becomes true.
Actionable closing
If your Angular application integrates non‑Angular parts, consider disabling Protractor’s global Angular sync and writing explicit browser.wait statements with the appropriate ExpectedConditions. Scope the disablement to the specific test or page object, re‑enable sync when returning to Angular pages, and always pair each wait with a clear error message. This gives you predictable test behavior while still leveraging the Page Object Model for maintainable specs.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.