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

Commit 1670384

Browse files
committed
feat(runner): allow protractor to restart browser between tests
Enables adding `restartBrowserBetweenTests: true` to your configuration file. Note that this will slow down test suites considerably. Closes #1435
1 parent b28355d commit 1670384

File tree

9 files changed

+145
-56
lines changed

9 files changed

+145
-56
lines changed

docs/referenceConf.js

+4
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,10 @@ exports.config = {
216216
// The path is relative to the location of this config.
217217
resultJsonOutputFile: null,
218218

219+
// If true, protractor will restart the browser between each test.
220+
// CAUTION: This will cause your tests to slow down drastically.
221+
restartBrowserBetweenTests: false,
222+
219223
// ---------------------------------------------------------------------------
220224
// ----- The test framework --------------------------------------------------
221225
// ---------------------------------------------------------------------------

lib/driverProviders/README.md

+10
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,21 @@ Each file exports a function which takes in the configuration as a parameter and
1313
*/
1414
DriverProvider.prototype.setupEnv
1515

16+
/**
17+
* @return {Array.<webdriver.WebDriver>} Array of existing webdriver instances.
18+
*/
19+
DriverProvider.prototype.getExistingDrivers
20+
1621
/**
1722
* @return {webdriver.WebDriver} A new setup driver instance.
1823
*/
1924
DriverProvider.prototype.getNewDriver
2025

26+
/**
27+
* @param {webdriver.WebDriver} The driver instance to quit.
28+
*/
29+
DriverProvider.prototype.quitDriver
30+
2131
/**
2232
* @return {q.promise} A promise which will resolve when the environment
2333
* is down.

lib/driverProviders/driverProvider.js

+47-19
Original file line numberDiff line numberDiff line change
@@ -7,36 +7,24 @@
77
var webdriver = require('selenium-webdriver'),
88
q = require('q');
99

10+
1011
var DriverProvider = function(config) {
1112
this.config_ = config;
1213
this.drivers_ = [];
1314
};
1415

16+
1517
/**
16-
* Teardown and destroy the environment and do any associated cleanup.
17-
* Shuts down the drivers.
18+
* Get all existing drivers.
1819
*
1920
* @public
20-
* @return {q.promise} A promise which will resolve when the environment
21-
* is down.
21+
* @return array of webdriver instances
2222
*/
23-
DriverProvider.prototype.teardownEnv = function() {
24-
var deferredArray = this.drivers_.map(function(driver) {
25-
var deferred = q.defer();
26-
driver.getSession().then(function(session_) {
27-
if (session_) {
28-
driver.quit().then(function() {
29-
deferred.resolve();
30-
});
31-
} else {
32-
deferred.resolve();
33-
}
34-
});
35-
return deferred.promise;
36-
});
37-
return q.all(deferredArray);
23+
DriverProvider.prototype.getExistingDrivers = function() {
24+
return this.drivers_.slice(); // Create a shallow copy
3825
};
3926

27+
4028
/**
4129
* Create a new driver.
4230
*
@@ -52,4 +40,44 @@ DriverProvider.prototype.getNewDriver = function() {
5240
return newDriver;
5341
};
5442

43+
44+
/**
45+
* Quit a driver.
46+
*
47+
* @public
48+
* @param webdriver instance
49+
*/
50+
DriverProvider.prototype.quitDriver = function(driver) {
51+
var driverIndex = this.drivers_.indexOf(driver);
52+
if (driverIndex >= 0) {
53+
this.drivers_.splice(driverIndex, 1);
54+
}
55+
56+
var deferred = q.defer();
57+
driver.getSession().then(function(session_) {
58+
if (session_) {
59+
driver.quit().then(function() {
60+
deferred.resolve();
61+
});
62+
} else {
63+
deferred.resolve();
64+
}
65+
});
66+
return deferred.promise;
67+
};
68+
69+
70+
/**
71+
* Teardown and destroy the environment and do any associated cleanup.
72+
* Shuts down the drivers.
73+
*
74+
* @public
75+
* @return {q.promise} A promise which will resolve when the environment
76+
* is down.
77+
*/
78+
DriverProvider.prototype.teardownEnv = function() {
79+
return q.all(this.drivers_.map(this.quitDriver.bind(this)));
80+
};
81+
82+
5583
module.exports = DriverProvider;

lib/driverProviders/mock.js

-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ var webdriver = require('selenium-webdriver'),
1111
* @constructor
1212
*/
1313
var MockExecutor = function() {
14-
this.drivers_ = [];
1514
};
1615

1716
/**

lib/frameworks/jasmine.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,9 @@ exports.run = function(runner, specs) {
3030
this.startTime = new Date();
3131
};
3232
RunnerReporter.prototype.reportSpecResults = function(spec) {
33-
if (spec.results().passed()) {
33+
if (spec.results().passedCount) {
3434
this.emitter.emit('testPass');
35-
} else {
35+
} else if (spec.results().failedCount) {
3636
this.emitter.emit('testFail');
3737
}
3838

lib/runner.js

+35-33
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@ var Runner = function(config) {
2424
this.preparer_ = null;
2525
this.driverprovider_ = null;
2626
this.config_ = config;
27-
this.drivers_ = [];
2827

2928
if (config.v8Debug) {
3029
// Call this private function instead of sending SIGUSR1 because Windows.
@@ -147,13 +146,13 @@ Runner.prototype.controlFlow = function() {
147146
* Sets up convenience globals for test specs
148147
* @private
149148
*/
150-
Runner.prototype.setupGlobals_ = function(browser) {
149+
Runner.prototype.setupGlobals_ = function(browser_) {
151150
// Export protractor to the global namespace to be used in tests.
152151
global.protractor = protractor;
153-
global.browser = browser;
154-
global.$ = browser.$;
155-
global.$$ = browser.$$;
156-
global.element = browser.element;
152+
global.browser = browser_;
153+
global.$ = browser_.$;
154+
global.$$ = browser_.$$;
155+
global.element = browser_.element;
157156
global.by = global.By = protractor.By;
158157

159158
// Enable sourcemap support for stack traces.
@@ -176,13 +175,12 @@ Runner.prototype.setupGlobals_ = function(browser) {
176175
Runner.prototype.createBrowser = function() {
177176
var config = this.config_;
178177
var driver = this.driverprovider_.getNewDriver();
179-
this.drivers_.push(driver);
180178
driver.manage().timeouts().setScriptTimeout(config.allScriptsTimeout);
181-
var browser = protractor.wrapDriver(driver,
179+
var browser_ = protractor.wrapDriver(driver,
182180
config.baseUrl, config.rootElement);
183-
browser.params = config.params;
181+
browser_.params = config.params;
184182
if (config.getPageTimeout) {
185-
browser.getPageTimeout = config.getPageTimeout;
183+
browser_.getPageTimeout = config.getPageTimeout;
186184
}
187185
var self = this;
188186

@@ -193,45 +191,34 @@ Runner.prototype.createBrowser = function() {
193191
* @param {boolean} opt_copyMockModules Whether to apply same mock modules on creation
194192
* @return {Protractor} a protractor instance.
195193
*/
196-
browser.forkNewDriverInstance = function(opt_useSameUrl, opt_copyMockModules) {
194+
browser_.forkNewDriverInstance = function(opt_useSameUrl, opt_copyMockModules) {
197195
var newBrowser = self.createBrowser();
198196
if (opt_copyMockModules) {
199-
newBrowser.mockModules_ = browser.mockModules_;
197+
newBrowser.mockModules_ = browser_.mockModules_;
200198
}
201199
if (opt_useSameUrl) {
202-
browser.driver.getCurrentUrl().then(function(url) {
200+
browser_.driver.getCurrentUrl().then(function(url) {
203201
newBrowser.get(url);
204202
});
205203
}
206204
return newBrowser;
207205
};
208-
return browser;
206+
return browser_;
209207
};
210208

209+
211210
/**
212211
* Final cleanup on exiting the runner.
213212
*
214213
* @return {q.Promise} A promise which resolves on finish.
215214
* @private
216215
*/
217216
Runner.prototype.shutdown_ = function() {
218-
var deferredArr = this.drivers_.map(function(driver) {
219-
var deferred = q.defer();
220-
driver.getSession().then(function(session_) {
221-
if (session_) {
222-
driver.quit().then(function() {
223-
deferred.resolve();
224-
});
225-
} else {
226-
deferred.resolve();
227-
}
228-
});
229-
return deferred.promise;
230-
});
231-
return q.all(deferredArr);
217+
return q.all(
218+
this.driverprovider_.getExistingDrivers().
219+
map(this.driverprovider_.quitDriver.bind(this.driverprovider_)));
232220
};
233221

234-
235222
/**
236223
* The primary workhorse interface. Kicks off the test running process.
237224
*
@@ -241,7 +228,8 @@ Runner.prototype.shutdown_ = function() {
241228
Runner.prototype.run = function() {
242229
var self = this,
243230
testPassed,
244-
plugins;
231+
plugins,
232+
browser_;
245233

246234
if (!this.config_.specs.length) {
247235
throw new Error('Spec patterns did not match any files.');
@@ -256,9 +244,9 @@ Runner.prototype.run = function() {
256244
});
257245
// 2) Create a browser and setup globals
258246
}).then(function() {
259-
var browser = self.createBrowser();
260-
self.setupGlobals_(browser);
261-
return browser.getSession().then(function(session) {
247+
browser_ = self.createBrowser();
248+
self.setupGlobals_(browser_);
249+
return browser_.getSession().then(function(session) {
262250
log.debug('WebDriver session successfully started with capabilities ' +
263251
util.inspect(session.getCapabilities()));
264252
}, function(err) {
@@ -287,6 +275,20 @@ Runner.prototype.run = function() {
287275
') is not a valid framework.');
288276
}
289277

278+
if (self.config_.restartBrowserBetweenTests) {
279+
var restartDriver = function() {
280+
// Note: because tests are not paused at this point, any async
281+
// calls here are not guaranteed to complete before the tests resume.
282+
self.driverprovider_.quitDriver(browser_.driver);
283+
// Copy mock modules, but do not navigate to previous URL.
284+
browser_ = browser_.forkNewDriverInstance(false, true);
285+
self.setupGlobals_(browser_);
286+
};
287+
288+
self.on('testPass', restartDriver);
289+
self.on('testFail', restartDriver);
290+
}
291+
290292
return require(frameworkPath).run(self, self.config_.specs).
291293
then(function(testResults) {
292294
return helper.joinTestLogs(pluginSetupResults, testResults);

scripts/test.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,8 @@ var passingTests = [
2424
'node lib/cli.js spec/pluginsFullConf.js',
2525
'node lib/cli.js spec/ngHintSuccessConfig.js',
2626
'node lib/cli.js spec/interactionConf.js',
27-
'node lib/cli.js spec/directConnectConf.js'
27+
'node lib/cli.js spec/directConnectConf.js',
28+
'node lib/cli.js spec/restartBrowserBetweenTestsConf.js'
2829
];
2930

3031
passingTests.push(
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
var env = require('../environment.js');
2+
3+
describe('pages with login', function() {
4+
it('should set a cookie', function() {
5+
browser.get(env.baseUrl+'/index.html');
6+
7+
browser.manage().addCookie('testcookie', 'Jane-1234');
8+
9+
// Make sure the cookie is set.
10+
browser.manage().getCookie('testcookie').then(function(cookie) {
11+
expect(cookie.value).toEqual('Jane-1234');
12+
});
13+
});
14+
15+
it('should check the cookie is gone', function() {
16+
browser.get(env.baseUrl+'/index.html');
17+
18+
// Make sure the cookie is gone.
19+
browser.manage().getCookie('testcookie').then(function(cookie) {
20+
expect(cookie).toEqual(null);
21+
});
22+
});
23+
});
+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
var env = require('./environment.js');
2+
3+
// The main suite of Protractor tests.
4+
exports.config = {
5+
seleniumAddress: env.seleniumAddress,
6+
7+
// Spec patterns are relative to this directory.
8+
specs: [
9+
'restartBrowserBetweenTests/*_spec.js'
10+
],
11+
12+
capabilities: env.capabilities,
13+
14+
baseUrl: env.baseUrl,
15+
16+
jasmineNodeOpts: {
17+
isVerbose: true,
18+
realtimeFailure: true
19+
},
20+
21+
restartBrowserBetweenTests: true,
22+
};

0 commit comments

Comments
 (0)