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:
Locator | Usage 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();
});
}
}
});
}