![]()
Protractor tests failing due to timing issues is a common challenge when testing AngularJS applications end-to-end (E2E). These failures typically arise when Protractor attempts to interact with elements before they are fully rendered, bound, or visible in the DOM. This happens especially when asynchronous operations like HTTP requests, animations, or timeouts delay the readiness of elements.
Here’s a step-by-step explanation with 1000+ words, exploring the root causes, best practices, and solid techniques to resolve timing-related test failures in Protractor.
Understanding Timing Issues in Protractor
Protractor is built on top of WebDriverJS, which communicates with the browser in an asynchronous fashion. Protractor tries to manage synchronization automatically for Angular apps via Angular’s internal testability API (angular.getTestability() or $injector.get('$browser')). However, when there are non-Angular timeouts, third-party scripts, or race conditions, Protractor may get ahead of the application, leading to flaky or failing tests.
Common Symptoms
ElementNotVisibleErrorNoSuchElementErrorTimeoutErrorFailed: stale element reference- Tests that pass locally but fail in CI
- Tests passing/failing randomly
Root Causes of Timing Issues
1. Non-Angular App or Mixed Asynchronous Logic
Protractor waits for Angular, not arbitrary setTimeout or third-party AJAX like jQuery or vanilla JS fetch.
2. Slow Network or Animations
If the app has animations or slow HTTP responses, the DOM may not update in time for the test.
3. Manual async operations not awaited
Protractor doesn’t wait for setTimeout, setInterval, or custom JS events unless explicitly told.
4. Stale Elements
When the element is located before the DOM is updated, any further actions on it may fail.
Solutions and Techniques to Fix Timing Issues
1. Use ExpectedConditions (EC) Properly
Protractor offers ExpectedConditions to wait until certain states (visibility, clickability) are true.
const EC = protractor.ExpectedConditions;
const myButton = element(by.css('.submit-button'));
browser.wait(EC.elementToBeClickable(myButton), 5000, 'Button not clickable');
myButton.click();
2. Avoid Fixed Sleep Times (browser.sleep())
Using browser.sleep() may work temporarily but introduces brittleness and longer test runs.
// Not recommended
browser.sleep(3000);
Instead, always wait for a condition.
browser.wait(EC.presenceOf(element(by.css('.loader-done'))), 10000);
3. Use browser.waitForAngular() After Non-Angular Calls
If you interact with third-party libraries or non-Angular pages, tell Protractor when to wait.
browser.ignoreSynchronization = true; // For non-Angular apps
// Do your operation
browser.ignoreSynchronization = false;
browser.waitForAngular();
4. Check for Stability Before Interacting
Use stable element states: isPresent(), isDisplayed(), isEnabled(), etc.
const saveBtn = element(by.id('save'));
expect(saveBtn.isPresent()).toBe(true);
expect(saveBtn.isDisplayed()).toBe(true);
🔹 5. Wait for Animations to Complete
If your app uses Angular animations or jQuery fades, consider waiting for final state or disabling animations in test mode:
Option 1: Wait for state
browser.wait(EC.invisibilityOf(element(by.css('.ng-animating'))), 5000);
Option 2: Disable animations in test builds
angular.module('app').config(function($animateProvider) {
$animateProvider.classNameFilter(/^(?:(?!ng-animate-disabled).)*$/);
});
Example Scenario with Fix
Problem Code (Flaky):
it('should submit the form', function() {
element(by.model('user.name')).sendKeys('John');
element(by.css('.submit')).click();
expect(element(by.css('.success')).isDisplayed()).toBe(true);
});
Fixed Code:
it('should submit the form', function() {
const nameField = element(by.model('user.name'));
const submitBtn = element(by.css('.submit'));
const successMsg = element(by.css('.success'));
nameField.sendKeys('John');
browser.wait(EC.elementToBeClickable(submitBtn), 5000);
submitBtn.click();
browser.wait(EC.visibilityOf(successMsg), 5000);
expect(successMsg.isDisplayed()).toBe(true);
});
Tips to Prevent Future Timing Issues
Always Use ExpectedConditions Instead of browser.sleep()
They’re more reliable, readable, and make tests deterministic.
Chain Promises When Necessary
Protractor supports both promise-style and async/await. Mixing the two can cause flaky tests.
// Recommended: Use async/await
it('should wait properly', async () => {
const button = element(by.id('submit'));
await browser.wait(EC.elementToBeClickable(button), 5000);
await button.click();
});
Avoid Overusing element.all(...)
Filtering through multiple elements may delay the locator logic.
// Inefficient
element.all(by.css('.item')).get(3).click();
Instead, try:
element(by.css('.item:nth-child(4)')).click();
Advanced Issues and Their Fixes
Dealing with AngularJS $timeout
Protractor may not always wait for $timeout unless it’s wrapped in $apply.
Fix: Wrap your timeout code in Angular-aware functions or use browser.wait() explicitly for effects to finish.
Disabling Synchronization for Non-Angular Pages
browser.ignoreSynchronization = true;
// Do non-Angular steps
browser.ignoreSynchronization = false;
browser.waitForAngular(); // resume normal flow
Force Protractor to Wait on Element States
function waitUntilVisible(el, timeout = 5000) {
return browser.wait(EC.visibilityOf(el), timeout, 'Element not visible');
}
Cleanup and Retry Patterns
In CI environments, if your test fails intermittently:
- Add logging to know what step failed.
- Use retry logic inside a custom utility function (not the test itself).
- Use Protractor’s
onPrepare()to ensure pre-test setup (login, data reset, etc.).
Summary of Best Practices
| Pattern | Benefit |
|---|---|
Use ExpectedConditions | Reliable waits for clickability/visibility |
Avoid browser.sleep() | Reduces flakiness |
Use async/await | Better flow control |
| Add elements to DOM before triggering | Ensures events work |
| Handle non-Angular apps explicitly | Prevents silent sync failures |
| Debug failed steps | Helps reproduce locally |
Use .first() instead of hard-coded .get(n) | Cleaner list filtering |
| Check state before interaction | Prevents stale element error |
