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

Commit

Permalink
feat(launcher): add beforeTest and afterTest
Browse files Browse the repository at this point in the history
  • Loading branch information
hankduan committed Oct 7, 2014
1 parent d21e464 commit eedf50b
Show file tree
Hide file tree
Showing 8 changed files with 133 additions and 103 deletions.
18 changes: 17 additions & 1 deletion docs/referenceConf.js
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,16 @@ exports.config = {
// How long to wait for a page to load.
getPageTimeout: 10000,

// A callback function called once configs are read but before any environment
// setup. This will only run once, and before onPrepare.
// You can specify a file containing code to run by setting beforeLaunch to
// the filename string.
beforeLaunch: function() {
// At this point, global variable 'protractor' object will NOT be set up,
// and globals from the test framework will NOT be available. The main
// purpose of this function should be to bring up test dependencies.
},

// A callback function called once protractor is ready and available, and
// before the specs are executed.
// If multiple capabilities are being run, this will run once per
Expand All @@ -168,9 +178,15 @@ exports.config = {

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

// A callback function called once all tests have finished running and
// the WebDriver instance has been shut down. It is passed the exit code
// (0 if the tests passed). This is called only once before the program
// exits (after onCleanUp).
afterLaunch: function() {},

// The params object will be passed directly to the Protractor instance,
// and can be accessed from your test as browser.params. It is an arbitrary
// object and can contain anything you may need in your test.
Expand Down
2 changes: 1 addition & 1 deletion lib/frameworks/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ Requirements

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

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

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

Expand Down
2 changes: 1 addition & 1 deletion lib/frameworks/cucumber.js
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ exports.run = function(runner, specs) {
}
global.cucumber = Cucumber.Cli(execOptions);

return runner.runTestPreparers().then(function () {
return runner.runTestPreparer().then(function () {
return q.promise(function (resolve, reject) {
global.cucumber.run(function (succeeded) {
try {
Expand Down
2 changes: 1 addition & 1 deletion lib/frameworks/jasmine.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ exports.run = function(runner, specs) {
// get to complete first.
jasmine.getEnv().addReporter(new RunnerReporter(runner));

return runner.runTestPreparers().then(function() {
return runner.runTestPreparer().then(function() {
return q.promise(function (resolve, reject) {
var jasmineNodeOpts = runner.getConfig().jasmineNodeOpts;
var originalOnComplete = runner.getConfig().onComplete;
Expand Down
2 changes: 1 addition & 1 deletion lib/frameworks/mocha.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ exports.run = function(runner, specs) {

mocha.loadFiles();

runner.runTestPreparers().then(function() {
runner.runTestPreparer().then(function() {

specs.forEach(function(file) {
mocha.addFile(file);
Expand Down
121 changes: 69 additions & 52 deletions lib/launcher.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@

var child = require('child_process'),
ConfigParser = require('./configParser'),
TaskScheduler = require('./taskScheduler');
TaskScheduler = require('./taskScheduler'),
helper = require('./util');

var launcherPrefix = '[launcher]';
var RUNNERS_FAILED_EXIT_CODE = 100;
Expand Down Expand Up @@ -131,60 +132,76 @@ var init = function(configFile, additionalConfig) {
this.reporter.reportHeader_();
};

// Don't start new process if there is only 1 task.
var totalTasks = scheduler.numTasksRemaining();
if (totalTasks === 1) {
var Runner = require('./runner');
var task = scheduler.nextTask();
config.capabilities = task.capability;
config.specs = task.specs;
var cleanUpAndExit = function(exitCode) {
helper.runFilenameOrFn_(config.configDir, config.afterLaunch, [exitCode]).
then(function(returned) {
if (typeof returned === 'number') {
process.exit(returned);
} else {
process.exit(exitCode);
}
}, function(err) {
log_('Error:', err);
process.exit(1);
});
};

var runner = new Runner(config);
runner.run().then(function(exitCode) {
process.exit(exitCode);
}).catch(function(err) {
log_('Error:', err.stack || err.message || err);
process.exit(1);
});
} else {
if (config.debug) {
throw new Error('Cannot run in debug mode with ' +
'multiCapabilities, count > 1, or sharding');
}
for (var i = 0; i < scheduler.maxConcurrentTasks(); ++i) {
var createNextRunnerFork = function() {
var task = scheduler.nextTask();
if (task) {
var done = function() {
task.done();
createNextRunnerFork();
};
var runnerFork = new RunnerFork(task);
runnerFork.addEventHandlers(done);
runnerFork.run();
}
};
createNextRunnerFork();
}
log_('Running ' + scheduler.countActiveTasks() + ' instances of WebDriver');
helper.runFilenameOrFn_(config.configDir, config.beforeLaunch).then(function() {
// Don't start new process if there is only 1 task.
var totalTasks = scheduler.numTasksRemaining();
if (totalTasks === 1) {
var Runner = require('./runner');
var task = scheduler.nextTask();
config.capabilities = task.capability;
config.specs = task.specs;

process.on('exit', function(code) {
if (code) {
log_('Process exited with error code ' + code);
process.exit(code);
} else if (runnerErrorCount > 0) {
reporter.reportSummary();
process.exit(RUNNERS_FAILED_EXIT_CODE);
} else {
if (scheduler.numTasksRemaining() > 0) {
throw new Error('BUG: launcher exited with ' +
scheduler.numTasksRemaining() + ' tasks remaining');
}
reporter.reportSummary();
process.exit(0);
var runner = new Runner(config);
runner.run().then(function(exitCode) {
cleanUpAndExit(exitCode);
}).catch(function(err) {
log_('Error:', err.stack || err.message || err);
cleanUpAndExit(1);
});
} else {
if (config.debug) {
throw new Error('Cannot run in debug mode with ' +
'multiCapabilities, count > 1, or sharding');
}
});
}
for (var i = 0; i < scheduler.maxConcurrentTasks(); ++i) {
var createNextRunnerFork = function() {
var task = scheduler.nextTask();
if (task) {
var done = function() {
task.done();
createNextRunnerFork();
};
var runnerFork = new RunnerFork(task);
runnerFork.addEventHandlers(done);
runnerFork.run();
}
};
createNextRunnerFork();
}
log_('Running ' + scheduler.countActiveTasks() + ' instances of WebDriver');

process.on('exit', function(code) {
if (code) {
log_('Process exited with error code ' + code);
cleanUpAndExit(code);
} else if (runnerErrorCount > 0) {
reporter.reportSummary();
cleanUpAndExit(RUNNERS_FAILED_EXIT_CODE);
} else {
if (scheduler.numTasksRemaining() > 0) {
throw new Error('BUG: launcher exited with ' +
scheduler.numTasksRemaining() + ' tasks remaining');
}
reporter.reportSummary();
cleanUpAndExit(0);
}
});
}
});
};

//###### REPORTER #######//
Expand Down
64 changes: 18 additions & 46 deletions lib/runner.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
var protractor = require('./protractor'),
webdriver = require('selenium-webdriver'),
path = require('path'),
util = require('util'),
q = require('q'),
EventEmitter = require('events').EventEmitter;
EventEmitter = require('events').EventEmitter,
helper = require('./util');

/*
* Runner is responsible for starting the execution of a test run and triggering
Expand All @@ -18,7 +18,7 @@ var protractor = require('./protractor'),
* @constructor
*/
var Runner = function(config) {
this.preparers_ = [];
this.preparer_ = null;
this.driverprovider_ = null;
this.config_ = config;

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


/**
* Internal helper for abstraction of polymorphic filenameOrFn properties.
* @private
* @param {object} filenameOrFn The filename or function that we will execute.
* @return {object} A number or a promise that will resolve when the test
* preparers are finished.
*/
Runner.prototype.runFilenameOrFn_ = function(filenameOrFn, args) {
if (filenameOrFn) {
if (typeof filenameOrFn === 'function') {
return filenameOrFn.apply(null, args);
} else if (typeof filenameOrFn === 'string') {
return require(path.resolve(this.config_.configDir, filenameOrFn));
} else {
// TODO - this is not generic.
throw 'config.onPrepare and config.onCleanUp must be a string or function';
}
}
};

/**
* Registrar for testPreparers - executed right before tests run.
* @public
* @param {string/Fn} filenameOrFn
*/
Runner.prototype.registerTestPreparer = function(filenameOrFn) {
this.preparers_.push(filenameOrFn);
Runner.prototype.setTestPreparer = function(filenameOrFn) {
this.preparer_ = filenameOrFn;
};


/**
* Executor of testPreparers
* Executor of testPreparer
* @public
* @return {q.Promise} A promise that will resolve when the test preparers
* are finished.
*/
Runner.prototype.runTestPreparers = function() {
var filenameOrFn;
var promises = [];
var returned;
for (var i = 0; i < this.preparers_.length; i++) {
filenameOrFn = this.preparers_[i];
returned = this.runFilenameOrFn_(filenameOrFn);
if (q.isPromiseAlike(returned)) {
promises.push(returned);
}
}
return q.all(promises);
Runner.prototype.runTestPreparer = function() {
return helper.runFilenameOrFn_(this.config_.configDir, this.preparer_);
};


Expand Down Expand Up @@ -137,12 +107,14 @@ Runner.prototype.loadDriverProvider_ = function() {
* @param {int} Standard unix exit code
*/
Runner.prototype.exit_ = function(exitCode) {
var returned = this.runFilenameOrFn_(this.config_.onCleanUp, [exitCode]);
if (typeof returned === 'number' || q.isPromiseAlike(returned)) {
return returned;
} else {
return exitCode;
}
helper.runFilenameOrFn_(this.config_.configDir, this.config_.onCleanUp, [exitCode]).
then(function(returned) {
if (typeof returned === 'number') {
return returned;
} else {
return exitCode;
}
});
};


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

this.registerTestPreparer(this.config_.onPrepare);
this.setTestPreparer(this.config_.onPrepare);

// 1) Setup environment
//noinspection JSValidateTypes
Expand Down Expand Up @@ -280,7 +252,7 @@ Runner.prototype.run = function() {
}).then(function() {
var passed = testResult.failedCount === 0;
var exitCode = passed ? 0 : 1;
return q.when(self.exit_(exitCode));
return self.exit_(exitCode);
});
};

Expand Down
25 changes: 25 additions & 0 deletions lib/util.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
var q = require('q'),
path = require('path');

/**
* Internal helper for abstraction of polymorphic filenameOrFn properties.
* @param {object} filenameOrFn The filename or function that we will execute.
* @param {Array.<object>}} args The args to pass into filenameOrFn.
* @return {q.Promise} A promise that will resolve when filenameOrFn completes.
*/
exports.runFilenameOrFn_ = function(configDir, filenameOrFn, args) {
var returned;
if (filenameOrFn) {
if (typeof filenameOrFn === 'function') {
returned = filenameOrFn.apply(null, args);
} else if (typeof filenameOrFn === 'string') {
filenameOrFn = require(path.resolve(configDir, filenameOrFn));
if (typeof filenameOrFn === 'function') {
returned = filenameOrFn.apply(null, args);
}
} else {
throw 'filenameOrFn must be a string or function';
}
}
return q.when(returned);
};

0 comments on commit eedf50b

Please sign in to comment.