In the previous article, we discussed setting up a Project for Protractor Automation using visual studio code and Jasmine Test. In this article, we will discuss Promise in Protractor. All the Protractor API methods return promise objects. And while writing protractor automation tests it is important to ensure that promise chaining taken care as expected.

What is Promise in Protractor?

A promise is an object that represents the eventual completion (success or failure) of an asynchronous operation and its resulting value. That means the object that will be resolved in the future. In short, the promise will have a value that is not ready to use immediately but you can use it once the promise is resolved.

Let's understand with an example,

// spec.ts
describe('Promise Demo',()=> { 
	it('Sample', () => { 
		browser.get('https://material.angularjs.org/latest/demo/autocomplete') 
		element(by.name('basic-usage')).click();
		element(by.id('input-99')).sendKeys('Test');
		const name = element(by.id('input-99')).getAttribute("value");
		expect(name).toBe("Test"); 
	}); 
});

Note: I know that by now we have not yet done any of the Protractor commands, and the above code might look complicated to you. But it is very necessary for us to understand the Protractor Promise before we move to Protractor Commands. So I take the opportunity to explain to you the steps at a high level for now.

  1. browser.get('https://google.com/') => Opens up the webpage
  2. element(by.name('q')).click() => locate the element using it's name and perform click action on it
  3. element(by.id('input-99')).sendKeys('Test') => locate the element using it's id and type desired characters in it
  4. name = element(by.id(input-99')).getAttribute("value")) => locate the element, reads its value attribute text and then save it to name variable

The typical expectation with above example is code blocks will get executed one after the other but it's not like that. Protractor handles the control flow its own way. So there is no guarantee that it will be executed one after the other.

Two things to notice from the above example.

  • There is no guarantee that tests will be executed sequentially, so by the time it reaches the expect statement name variable might not have any value.
  • If the above statement got true, the name variable doesn't hold the value of attribute value, because protractor API returns promise object but not the actual value. Instead returns a promise of providing a string.

This is because Protractor may wait for several moments for a page to finish rendering. Once the page has been rendered, the promise resolves, executing the attached function and passing the element text into a variable. Any code that relies on this variable must be executed after the promise resolves.

Why Protractor function designed to return Promises?

The above example might work great if the web page is a static website. But modern web pages render the web page element in an asynchronous manner i.e loading web page doesn't guarantee that all the web page elements are rendered.

Protractor has an internal mechanism to wait for such objects automatically but to trigger that mechanism it requires you to use the keyword "then". Any statement started with then keyword, protractors guarantees that operation will perform based on the previous action only. This also reduces the explicit waits such as browser.sleep() and browser.wait(). It also helps automation tests to complete faster.

Almost all Protractor functions work in this manner - returning a promise that resolves after an indeterminate amount of time, and upon resolving, triggering a function to handle the result.

Promise chaining in Protractor

Let's try to understand it with the help of a quick analogy:

Imagine you have promised a friend that you are going to buy him a juice. This promise doesn't mean you actually have a juice for him at that exact moment. Your upcoming beverage run could succeed, and you return with juice in hand, or it could fail and the friend is left thirsty. So the outcome may be anything.

If your friend has to open the juice bottle there should be a juice bottle in his hand or he might have to wait until you bring him the juice bottle.

In protractor, it is handled by using then statement.

// spec.ts
import { browser, element, by } from "protractor";

describe('Promise Demo',()=> {
	browser.waitForAngularEnabled(false); 
	it('Protractor Promise Chaining', () => { 
			browser.get('https://google.com/') 
			.then(() => element(by.name('q')).click()) // then keyword is used to chain
			.then(() => element(by.name('q')).sendKeys('Test')) 
			.then(() => element(by.name('q')).getAttribute("title")) 
			.then((name) => {expect(name).toBe("Search") }); 
	}); 
});

then keyword is used for promise chaining, this also ensures that your code block will always be executed one after another.

  1. browser.get('https://google.com/') executes and then it waits for page to load
  2. once the page is loaded it executes element(by.name('q')).click() then it ensures action is performed
  3. Then it executes the line element(by.name('q')).sendKeys('Test') once this action is done next line and continues...
  4. .then(() => element(by.name('q')).getAttribute("title")) .then((name) => { expect(name).toBe("Search") }); watch this line we are getting attribute once this operation is successful it returns promise object that is resolved now the name will hold the actual attribute value.

Promise in Protractor

Note: Earlier version of protractor allowed users to write specs without promise chaining, but these were managed by WebDriverJS API with the recent changes protractor deprecated usage of control flow, so writing the protractor tests without promise chaining may lead to test failure or unexpected results.

Custom Assertions in Jasmine
Custom Assertions in Jasmine
Previous Article
Protractor Browser Commands
Protractor Browser Commands
Next Article

Similar Articles

Feedback