Stopping Flaky Tests: Using Expected Conditions in Protractor
Stop using browser.sleep() to fix flaky Protractor tests. Learn how to implement Expected Conditions (EC) to synchronize tests with the DOM and reduce NoSuchElementErrors.
19 Jun 2026, 01:42 UTC

The Problem with 'Invisible' Elements
You have a test that passes locally but fails randomly in CI. The error is usually NoSuchElementError or ElementNotInteractableError. The element exists in the HTML, but the browser hasn't finished rendering it, or the Angular digest cycle hasn't completed the transition when the test tries to interact.
The common instinct is to add browser.sleep(2000). Hard-coded sleeps waste time when the app is fast or fail when the network is slow. The solution is explicit waits using Expected Conditions (EC).
How Expected Conditions Work
Protractor extends WebDriverJS with a specialized wrapper for Angular and synchronizes automatically with the Angular digest cycle. However, it cannot always predict when a non-Angular element, third-party component, or slow API response will become truly interactable.
The Expected Conditions module lets you define a specific state the browser must reach before proceeding. The test polls the DOM and continues the millisecond the condition is met.
Implementing Explicit Waits
Import EC and wrap the element locator inside browser.wait(). This prevents actions on elements that aren't ready.
const EC = require('protractor').Expected_conditions;
// Wait for a success banner after form submission
async function verifySubmission() {
const successMsg = element(by.id('success-banner'));
await browser.wait(EC.visibilityOf(successMsg), 5000);
expect(await successMsg.getText()).toBe('Form Submitted Successfully!');
}Execution details: run this within Protractor spec files (JavaScript or TypeScript). Replace 'success-banner' with your actual element locator and 5000 with a timeout appropriate for your slowest environment. Setting the timeout too high can mask performance regressions.
Choosing the Right Condition
Choosing the wrong EC method can cause false positives where a test passes but a user cannot actually interact with the UI.
| Method | Use Case | Behavior |
|---|---|---|
presenceOf | Hidden elements | Checks if element exists in the DOM, regardless of visibility. |
visibilityOf | UI interactions | Checks if element is in the DOM and has height/width > 0. |
elementClickable | Buttons/Links | Checks visibility and ensures the element is enabled. |
The Trade-off: Synchronization Overhead
EC reduces flakiness, but using it for every single interaction can slow the suite. Automatic Angular synchronization is efficient for standard components. Use browser.wait() only for elements triggered by setTimeout, animations, elements from non-Angular libraries, or API responses that bypass the Angular zone.
Verification
Simulate network delay with Chrome DevTools throttling and run the test. Consistent passes without browser.sleep indicate EC is working. A timeout error suggests the timeout is too low for the environment. Protractor is officially deprecated by the Angular team in favor of Cypress or Playwright.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.