Skip to content
This repository has been archived by the owner on Jul 29, 2024. It is now read-only.

Commit

Permalink
feat(lib): add support for waiting for angular2
Browse files Browse the repository at this point in the history
Use Angular2's testability API, if present, when waiting
for stability or loading a page.

Closes #2396
  • Loading branch information
juliemr committed Oct 7, 2015
1 parent 04e5bfb commit f246880
Show file tree
Hide file tree
Showing 4 changed files with 136 additions and 30 deletions.
18 changes: 13 additions & 5 deletions lib/clientsidescripts.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,10 @@ functions.waitForAngular = function(rootSelector, callback) {
var el = document.querySelector(rootSelector);

try {
if (window.getAngularTestability) {
window.getAngularTestability(el).whenStable(callback);
return;
}
if (!window.angular) {
throw new Error('angular could not be found on the window');
}
Expand Down Expand Up @@ -555,6 +559,8 @@ functions.findByCssContainingText = function(cssSelector, searchText, using) {
*
* @param {number} attempts Number of times to retry.
* @param {function} asyncCallback callback
*
* @return {{version: ?number, message: ?string}}
*/
functions.testForAngular = function(attempts, asyncCallback) {
var callback = function(args) {
Expand All @@ -564,19 +570,21 @@ functions.testForAngular = function(attempts, asyncCallback) {
};
var check = function(n) {
try {
if (window.angular && window.angular.resumeBootstrap) {
callback([true, null]);
if (window.getAllAngularTestabilities) {
callback({ver: 2});
} else if (window.angular && window.angular.resumeBootstrap) {
callback({ver: 1});
} else if (n < 1) {
if (window.angular) {
callback([false, 'angular never provided resumeBootstrap']);
callback({message: 'angular never provided resumeBootstrap'});
} else {
callback([false, 'retries looking for angular exceeded']);
callback({message: 'retries looking for angular exceeded'});
}
} else {
window.setTimeout(function() {check(n - 1);}, 1000);
}
} catch (e) {
callback([false, e]);
callback({message: e});
}
};
check(attempts);
Expand Down
62 changes: 37 additions & 25 deletions lib/protractor.js
Original file line number Diff line number Diff line change
Expand Up @@ -621,40 +621,52 @@ Protractor.prototype.get = function(destination, opt_timeout) {
msg('test for angular'),
Math.floor(timeout / 1000)).
then(function(angularTestResult) {
var hasAngular = angularTestResult[0];
if (!hasAngular) {
var message = angularTestResult[1];
var angularVersion = angularTestResult.ver;
if (!angularVersion) {
var message = angularTestResult.message;
throw new Error('Angular could not be found on the page ' +
destination + ' : ' + message);
}
return angularVersion;
}, function(err) {
throw 'Error while running testForAngular: ' + err.message;
})
.then(null, deferred.reject);
.then(loadMocks, deferred.reject);

function loadMocks(angularVersion) {
if (angularVersion === 1) {
// At this point, Angular will pause for us until angular.resumeBootstrap
// is called.
var moduleNames = [];
for (var i = 0; i < self.mockModules_.length; ++i) {
var mockModule = self.mockModules_[i];
var name = mockModule.name;
moduleNames.push(name);
var executeScriptArgs = [mockModule.script, msg('add mock module ' + name)].
concat(mockModule.args);
self.executeScript_.apply(self, executeScriptArgs).
then(null, function(err) {
throw 'Error while running module script ' + name +
': ' + err.message;
})
.then(null, deferred.reject);
}

// At this point, Angular will pause for us until angular.resumeBootstrap
// is called.
var moduleNames = [];
for (var i = 0; i < this.mockModules_.length; ++i) {
var mockModule = this.mockModules_[i];
var name = mockModule.name;
moduleNames.push(name);
var executeScriptArgs = [mockModule.script, msg('add mock module ' + name)].
concat(mockModule.args);
this.executeScript_.apply(this, executeScriptArgs).
then(null, function(err) {
throw 'Error while running module script ' + name +
': ' + err.message;
})
.then(null, deferred.reject);
self.executeScript_(
'angular.resumeBootstrap(arguments[0]);',
msg('resume bootstrap'),
moduleNames)
.then(null, deferred.reject);
} else {
// TODO: support mock modules in Angular2. For now, error if someone
// has tried to use one.
if (self.mockModules_.length > 1) {
deferred.reject('Trying to load mock modules on an Angular2 app ' +
'is not yet supported.');
}
}
}

this.executeScript_(
'angular.resumeBootstrap(arguments[0]);',
msg('resume bootstrap'),
moduleNames)
.then(null, deferred.reject);

this.driver.controlFlow().execute(function() {
return self.plugins_.onPageStable().then(function() {
deferred.fulfill();
Expand Down
29 changes: 29 additions & 0 deletions spec/angular2Conf.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
var env = require('./environment.js');

// This is the configuration for a smoke test for an Angular2 application.
//
// *** NOTE ***
// As Angular2 is in rapid development, the test application that ships with
// the Protractor repository does not yet contain an Angular2 section. This
// configuration assumes that you are serving the examples from the
// angular/angular repository at localhost:8000.
// See https://github.com/angular/angular/blob/master/DEVELOPER.md for
// setup instructions.
//
// TODO: when Angular2 is beta, include a test application in the
// Protractor repository.
exports.config = {
seleniumAddress: env.seleniumAddress,

framework: 'jasmine2',

specs: [
'ng2/async_spec.js'
],

capabilities: env.capabilities,

baseUrl: 'http://localhost:8000',

rootElement: 'async-app'
};
57 changes: 57 additions & 0 deletions spec/ng2/async_spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
describe('async angular2 application', function() {
var URL = 'examples/src/async/index.html';

beforeEach(function() {
browser.get(URL);
});

it('should work with synchronous actions', function() {
var increment = $('#increment');
increment.$('.action').click();

expect(increment.$('.val').getText()).toEqual('1');
});

it('should wait for asynchronous actions', function() {
var timeout = $('#delayedIncrement');

// At this point, the async action is still pending, so the count should
// still be 0.
expect(timeout.$('.val').getText()).toEqual('0');

timeout.$('.action').click();

expect(timeout.$('.val').getText()).toEqual('1');
});

it('should turn off when ignoreSynchronization is true', function() {
var timeout = $('#delayedIncrement');

// At this point, the async action is still pending, so the count should
// still be 0.
expect(timeout.$('.val').getText()).toEqual('0');

browser.ignoreSynchronization = true;

timeout.$('.action').click();
timeout.$('.cancel').click();

browser.ignoreSynchronization = false;

// whenStable should be called since the async action is cancelled. The
// count should still be 0;
expect(timeout.$('.val').getText()).toEqual('0');
});

it('should wait for a series of asynchronous actions', function() {
var timeout = $('#multiDelayedIncrements');

// At this point, the async action is still pending, so the count should
// still be 0.
expect(timeout.$('.val').getText()).toEqual('0');

timeout.$('.action').click();

expect(timeout.$('.val').getText()).toEqual('10');
});
});

0 comments on commit f246880

Please sign in to comment.