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

Commit eedf50b

Browse files
committed
feat(launcher): add beforeTest and afterTest
1 parent d21e464 commit eedf50b

File tree

8 files changed

+133
-103
lines changed

8 files changed

+133
-103
lines changed

docs/referenceConf.js

+17-1
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,16 @@ exports.config = {
146146
// How long to wait for a page to load.
147147
getPageTimeout: 10000,
148148

149+
// A callback function called once configs are read but before any environment
150+
// setup. This will only run once, and before onPrepare.
151+
// You can specify a file containing code to run by setting beforeLaunch to
152+
// the filename string.
153+
beforeLaunch: function() {
154+
// At this point, global variable 'protractor' object will NOT be set up,
155+
// and globals from the test framework will NOT be available. The main
156+
// purpose of this function should be to bring up test dependencies.
157+
},
158+
149159
// A callback function called once protractor is ready and available, and
150160
// before the specs are executed.
151161
// If multiple capabilities are being run, this will run once per
@@ -168,9 +178,15 @@ exports.config = {
168178

169179
// A callback function called once the tests have finished running and
170180
// the WebDriver instance has been shut down. It is passed the exit code
171-
// (0 if the tests passed or 1 if not). This is called once per capability.
181+
// (0 if the tests passed). This is called once per capability.
172182
onCleanUp: function(exitCode) {},
173183

184+
// A callback function called once all tests have finished running and
185+
// the WebDriver instance has been shut down. It is passed the exit code
186+
// (0 if the tests passed). This is called only once before the program
187+
// exits (after onCleanUp).
188+
afterLaunch: function() {},
189+
174190
// The params object will be passed directly to the Protractor instance,
175191
// and can be accessed from your test as browser.params. It is an arbitrary
176192
// object and can contain anything you may need in your test.

lib/frameworks/README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ Requirements
1919

2020
- `runner.emit` must be called with `testPass` and `testFail` messages.
2121

22-
- `runner.runTestPreparers` must be called before any tests are run.
22+
- `runner.runTestPreparer` must be called before any tests are run.
2323

2424
- `runner.getConfig().onComplete` must be called when tests are finished.
2525

lib/frameworks/cucumber.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ exports.run = function(runner, specs) {
5858
}
5959
global.cucumber = Cucumber.Cli(execOptions);
6060

61-
return runner.runTestPreparers().then(function () {
61+
return runner.runTestPreparer().then(function () {
6262
return q.promise(function (resolve, reject) {
6363
global.cucumber.run(function (succeeded) {
6464
try {

lib/frameworks/jasmine.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ exports.run = function(runner, specs) {
3737
// get to complete first.
3838
jasmine.getEnv().addReporter(new RunnerReporter(runner));
3939

40-
return runner.runTestPreparers().then(function() {
40+
return runner.runTestPreparer().then(function() {
4141
return q.promise(function (resolve, reject) {
4242
var jasmineNodeOpts = runner.getConfig().jasmineNodeOpts;
4343
var originalOnComplete = runner.getConfig().onComplete;

lib/frameworks/mocha.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ exports.run = function(runner, specs) {
5050

5151
mocha.loadFiles();
5252

53-
runner.runTestPreparers().then(function() {
53+
runner.runTestPreparer().then(function() {
5454

5555
specs.forEach(function(file) {
5656
mocha.addFile(file);

lib/launcher.js

+69-52
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@
66

77
var child = require('child_process'),
88
ConfigParser = require('./configParser'),
9-
TaskScheduler = require('./taskScheduler');
9+
TaskScheduler = require('./taskScheduler'),
10+
helper = require('./util');
1011

1112
var launcherPrefix = '[launcher]';
1213
var RUNNERS_FAILED_EXIT_CODE = 100;
@@ -131,60 +132,76 @@ var init = function(configFile, additionalConfig) {
131132
this.reporter.reportHeader_();
132133
};
133134

134-
// Don't start new process if there is only 1 task.
135-
var totalTasks = scheduler.numTasksRemaining();
136-
if (totalTasks === 1) {
137-
var Runner = require('./runner');
138-
var task = scheduler.nextTask();
139-
config.capabilities = task.capability;
140-
config.specs = task.specs;
135+
var cleanUpAndExit = function(exitCode) {
136+
helper.runFilenameOrFn_(config.configDir, config.afterLaunch, [exitCode]).
137+
then(function(returned) {
138+
if (typeof returned === 'number') {
139+
process.exit(returned);
140+
} else {
141+
process.exit(exitCode);
142+
}
143+
}, function(err) {
144+
log_('Error:', err);
145+
process.exit(1);
146+
});
147+
};
141148

142-
var runner = new Runner(config);
143-
runner.run().then(function(exitCode) {
144-
process.exit(exitCode);
145-
}).catch(function(err) {
146-
log_('Error:', err.stack || err.message || err);
147-
process.exit(1);
148-
});
149-
} else {
150-
if (config.debug) {
151-
throw new Error('Cannot run in debug mode with ' +
152-
'multiCapabilities, count > 1, or sharding');
153-
}
154-
for (var i = 0; i < scheduler.maxConcurrentTasks(); ++i) {
155-
var createNextRunnerFork = function() {
156-
var task = scheduler.nextTask();
157-
if (task) {
158-
var done = function() {
159-
task.done();
160-
createNextRunnerFork();
161-
};
162-
var runnerFork = new RunnerFork(task);
163-
runnerFork.addEventHandlers(done);
164-
runnerFork.run();
165-
}
166-
};
167-
createNextRunnerFork();
168-
}
169-
log_('Running ' + scheduler.countActiveTasks() + ' instances of WebDriver');
149+
helper.runFilenameOrFn_(config.configDir, config.beforeLaunch).then(function() {
150+
// Don't start new process if there is only 1 task.
151+
var totalTasks = scheduler.numTasksRemaining();
152+
if (totalTasks === 1) {
153+
var Runner = require('./runner');
154+
var task = scheduler.nextTask();
155+
config.capabilities = task.capability;
156+
config.specs = task.specs;
170157

171-
process.on('exit', function(code) {
172-
if (code) {
173-
log_('Process exited with error code ' + code);
174-
process.exit(code);
175-
} else if (runnerErrorCount > 0) {
176-
reporter.reportSummary();
177-
process.exit(RUNNERS_FAILED_EXIT_CODE);
178-
} else {
179-
if (scheduler.numTasksRemaining() > 0) {
180-
throw new Error('BUG: launcher exited with ' +
181-
scheduler.numTasksRemaining() + ' tasks remaining');
182-
}
183-
reporter.reportSummary();
184-
process.exit(0);
158+
var runner = new Runner(config);
159+
runner.run().then(function(exitCode) {
160+
cleanUpAndExit(exitCode);
161+
}).catch(function(err) {
162+
log_('Error:', err.stack || err.message || err);
163+
cleanUpAndExit(1);
164+
});
165+
} else {
166+
if (config.debug) {
167+
throw new Error('Cannot run in debug mode with ' +
168+
'multiCapabilities, count > 1, or sharding');
185169
}
186-
});
187-
}
170+
for (var i = 0; i < scheduler.maxConcurrentTasks(); ++i) {
171+
var createNextRunnerFork = function() {
172+
var task = scheduler.nextTask();
173+
if (task) {
174+
var done = function() {
175+
task.done();
176+
createNextRunnerFork();
177+
};
178+
var runnerFork = new RunnerFork(task);
179+
runnerFork.addEventHandlers(done);
180+
runnerFork.run();
181+
}
182+
};
183+
createNextRunnerFork();
184+
}
185+
log_('Running ' + scheduler.countActiveTasks() + ' instances of WebDriver');
186+
187+
process.on('exit', function(code) {
188+
if (code) {
189+
log_('Process exited with error code ' + code);
190+
cleanUpAndExit(code);
191+
} else if (runnerErrorCount > 0) {
192+
reporter.reportSummary();
193+
cleanUpAndExit(RUNNERS_FAILED_EXIT_CODE);
194+
} else {
195+
if (scheduler.numTasksRemaining() > 0) {
196+
throw new Error('BUG: launcher exited with ' +
197+
scheduler.numTasksRemaining() + ' tasks remaining');
198+
}
199+
reporter.reportSummary();
200+
cleanUpAndExit(0);
201+
}
202+
});
203+
}
204+
});
188205
};
189206

190207
//###### REPORTER #######//

lib/runner.js

+18-46
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
var protractor = require('./protractor'),
22
webdriver = require('selenium-webdriver'),
3-
path = require('path'),
43
util = require('util'),
54
q = require('q'),
6-
EventEmitter = require('events').EventEmitter;
5+
EventEmitter = require('events').EventEmitter,
6+
helper = require('./util');
77

88
/*
99
* Runner is responsible for starting the execution of a test run and triggering
@@ -18,7 +18,7 @@ var protractor = require('./protractor'),
1818
* @constructor
1919
*/
2020
var Runner = function(config) {
21-
this.preparers_ = [];
21+
this.preparer_ = null;
2222
this.driverprovider_ = null;
2323
this.config_ = config;
2424

@@ -49,54 +49,24 @@ var Runner = function(config) {
4949
util.inherits(Runner, EventEmitter);
5050

5151

52-
/**
53-
* Internal helper for abstraction of polymorphic filenameOrFn properties.
54-
* @private
55-
* @param {object} filenameOrFn The filename or function that we will execute.
56-
* @return {object} A number or a promise that will resolve when the test
57-
* preparers are finished.
58-
*/
59-
Runner.prototype.runFilenameOrFn_ = function(filenameOrFn, args) {
60-
if (filenameOrFn) {
61-
if (typeof filenameOrFn === 'function') {
62-
return filenameOrFn.apply(null, args);
63-
} else if (typeof filenameOrFn === 'string') {
64-
return require(path.resolve(this.config_.configDir, filenameOrFn));
65-
} else {
66-
// TODO - this is not generic.
67-
throw 'config.onPrepare and config.onCleanUp must be a string or function';
68-
}
69-
}
70-
};
71-
7252
/**
7353
* Registrar for testPreparers - executed right before tests run.
7454
* @public
7555
* @param {string/Fn} filenameOrFn
7656
*/
77-
Runner.prototype.registerTestPreparer = function(filenameOrFn) {
78-
this.preparers_.push(filenameOrFn);
57+
Runner.prototype.setTestPreparer = function(filenameOrFn) {
58+
this.preparer_ = filenameOrFn;
7959
};
8060

8161

8262
/**
83-
* Executor of testPreparers
63+
* Executor of testPreparer
8464
* @public
8565
* @return {q.Promise} A promise that will resolve when the test preparers
8666
* are finished.
8767
*/
88-
Runner.prototype.runTestPreparers = function() {
89-
var filenameOrFn;
90-
var promises = [];
91-
var returned;
92-
for (var i = 0; i < this.preparers_.length; i++) {
93-
filenameOrFn = this.preparers_[i];
94-
returned = this.runFilenameOrFn_(filenameOrFn);
95-
if (q.isPromiseAlike(returned)) {
96-
promises.push(returned);
97-
}
98-
}
99-
return q.all(promises);
68+
Runner.prototype.runTestPreparer = function() {
69+
return helper.runFilenameOrFn_(this.config_.configDir, this.preparer_);
10070
};
10171

10272

@@ -137,12 +107,14 @@ Runner.prototype.loadDriverProvider_ = function() {
137107
* @param {int} Standard unix exit code
138108
*/
139109
Runner.prototype.exit_ = function(exitCode) {
140-
var returned = this.runFilenameOrFn_(this.config_.onCleanUp, [exitCode]);
141-
if (typeof returned === 'number' || q.isPromiseAlike(returned)) {
142-
return returned;
143-
} else {
144-
return exitCode;
145-
}
110+
helper.runFilenameOrFn_(this.config_.configDir, this.config_.onCleanUp, [exitCode]).
111+
then(function(returned) {
112+
if (typeof returned === 'number') {
113+
return returned;
114+
} else {
115+
return exitCode;
116+
}
117+
});
146118
};
147119

148120

@@ -225,7 +197,7 @@ Runner.prototype.run = function() {
225197
throw new Error('Spec patterns did not match any files.');
226198
}
227199

228-
this.registerTestPreparer(this.config_.onPrepare);
200+
this.setTestPreparer(this.config_.onPrepare);
229201

230202
// 1) Setup environment
231203
//noinspection JSValidateTypes
@@ -280,7 +252,7 @@ Runner.prototype.run = function() {
280252
}).then(function() {
281253
var passed = testResult.failedCount === 0;
282254
var exitCode = passed ? 0 : 1;
283-
return q.when(self.exit_(exitCode));
255+
return self.exit_(exitCode);
284256
});
285257
};
286258

lib/util.js

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
var q = require('q'),
2+
path = require('path');
3+
4+
/**
5+
* Internal helper for abstraction of polymorphic filenameOrFn properties.
6+
* @param {object} filenameOrFn The filename or function that we will execute.
7+
* @param {Array.<object>}} args The args to pass into filenameOrFn.
8+
* @return {q.Promise} A promise that will resolve when filenameOrFn completes.
9+
*/
10+
exports.runFilenameOrFn_ = function(configDir, filenameOrFn, args) {
11+
var returned;
12+
if (filenameOrFn) {
13+
if (typeof filenameOrFn === 'function') {
14+
returned = filenameOrFn.apply(null, args);
15+
} else if (typeof filenameOrFn === 'string') {
16+
filenameOrFn = require(path.resolve(configDir, filenameOrFn));
17+
if (typeof filenameOrFn === 'function') {
18+
returned = filenameOrFn.apply(null, args);
19+
}
20+
} else {
21+
throw 'filenameOrFn must be a string or function';
22+
}
23+
}
24+
return q.when(returned);
25+
};

0 commit comments

Comments
 (0)