Writing end-to-end tests with Protractor

Loading

End-to-end (E2E) testing ensures that an AngularJS application behaves as expected from the user’s perspective. Protractor, built on WebDriverJS, is designed specifically for Angular applications, enabling automation of browser interactions.

This guide will cover:

  • Setting up Protractor
  • Writing E2E tests
  • Using locators effectively
  • Running tests in different browsers
  • Best practices for Protractor tests

1. Setting Up Protractor

Before writing tests, install Protractor globally:

npm install -g protractor

Check the installation:

protractor --version

1.1 Installing WebDriver Manager

WebDriver Manager is required to run Protractor:

webdriver-manager update
webdriver-manager start

This starts the WebDriver server.


2. Configuring Protractor

Create a protractor.conf.js file in your project root:

exports.config = {
directConnect: true,
capabilities: {
browserName: 'chrome'
},
framework: 'jasmine',
specs: ['tests/e2e/*.spec.js'],
jasmineNodeOpts: {
defaultTimeoutInterval: 30000
}
};

Key Configurations

  • directConnect: true → Directly connects to Chrome without requiring a separate WebDriver.
  • capabilities.browserName → Runs tests in Chrome (can be changed to ‘firefox’ or ‘edge’).
  • specs → Points to the test files.
  • framework: 'jasmine' → Uses Jasmine for assertions.

3. Writing an E2E Test

Let’s test a login page.

3.1 Example Login Page (index.html)

<!DOCTYPE html>
<html lang="en" ng-app="myApp">
<head>
<title>Login</title>
</head>
<body>
<div ng-controller="LoginController">
<input type="text" id="username" ng-model="user.username" placeholder="Username">
<input type="password" id="password" ng-model="user.password" placeholder="Password">
<button id="loginBtn" ng-click="login()">Login</button>
<p id="message">{{ message }}</p>
</div>
<script src="angular.min.js"></script>
<script src="app.js"></script>
</body>
</html>

3.2 AngularJS Controller (app.js)

var app = angular.module('myApp', []);

app.controller('LoginController', function($scope) {
$scope.user = { username: '', password: '' };
$scope.message = '';

$scope.login = function() {
if ($scope.user.username === 'admin' && $scope.user.password === 'password') {
$scope.message = 'Login successful!';
} else {
$scope.message = 'Invalid credentials!';
}
};
});

4. Writing Protractor Test for Login Page

Test File (tests/e2e/login.spec.js)

describe('Login Page', function() {
beforeEach(function() {
browser.get('http://localhost:8000/index.html');
});

it('should display an error for incorrect credentials', function() {
element(by.id('username')).sendKeys('wrongUser');
element(by.id('password')).sendKeys('wrongPass');
element(by.id('loginBtn')).click();

var message = element(by.id('message')).getText();
expect(message).toEqual('Invalid credentials!');
});

it('should login successfully with correct credentials', function() {
element(by.id('username')).sendKeys('admin');
element(by.id('password')).sendKeys('password');
element(by.id('loginBtn')).click();

var message = element(by.id('message')).getText();
expect(message).toEqual('Login successful!');
});
});

5. Running the E2E Test

Start a simple HTTP server:

npx http-server -p 8000

Then, run the test:

protractor protractor.conf.js

6. Using Different Element Locators

Protractor provides multiple ways to locate elements:

LocatorUsage Example
by.id('id')element(by.id('loginBtn'))
by.model('ngModel')element(by.model('user.username'))
by.binding('binding')element(by.binding('message'))
by.css('css')element(by.css('.button-class'))
by.repeater('item in items')element.all(by.repeater('user in users'))

Example:

var loginBtn = element(by.css('#loginBtn'));
expect(loginBtn.isDisplayed()).toBe(true);

7. Running Tests in Multiple Browsers

Modify protractor.conf.js:

exports.config = {
multiCapabilities: [
{ browserName: 'chrome' },
{ browserName: 'firefox' }
],
framework: 'jasmine',
specs: ['tests/e2e/*.spec.js']
};

Run:

protractor protractor.conf.js

8. Handling Asynchronous Operations

Protractor uses Promises for asynchronous interactions.

Example: Using then() for Waiting

element(by.id('username')).sendKeys('admin').then(function() {
console.log('Username entered');
});

Example: Using browser.wait() for Element Visibility

var EC = protractor.ExpectedConditions;
var loginMessage = element(by.id('message'));

browser.wait(EC.textToBePresentInElement(loginMessage, 'Login successful!'), 5000);

9. Taking Screenshots on Test Failure

Modify protractor.conf.js:

onPrepare: function() {
var fs = require('fs');

jasmine.getEnv().addReporter({
specDone: function(result) {
if (result.status === 'failed') {
browser.takeScreenshot().then(function(png) {
var stream = fs.createWriteStream('screenshots/' + result.fullName + '.png');
stream.write(new Buffer.from(png, 'base64'));
stream.end();
});
}
}
});
}

Leave a Reply

Your email address will not be published. Required fields are marked *