Skip to content

Commit

Permalink
Introduce support for multiple test pages
Browse files Browse the repository at this point in the history
  • Loading branch information
Trent Willis committed Mar 2, 2016
1 parent f22cd07 commit b20b971
Show file tree
Hide file tree
Showing 24 changed files with 283 additions and 43 deletions.
17 changes: 16 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,21 @@ Example:
<link rel="stylesheet" href="{{src}}">
{{/css_files}}

### Multiple Test Pages

You can also specify multiple test pages to run by passing an array to the `test_page` option.

```json
{
"test_page": [
"unit-tests.html",
"integration-tests.html"
]
}
```

This will cause Testem to run each test page in a separate launcher instance for each launcher you are using. This means that if you define 2 test pages and are using 3 launchers you will get 6 unique runs (2 per launcher).

Launchers
---------

Expand Down Expand Up @@ -357,7 +372,7 @@ If you want to use any of the [PhantomJS command line options](http://phantomjs.

```javascript
"phantomjs_args": [
"--ignore-ssl-errors=true"
"--ignore-ssl-errors=true"
]
```

Expand Down
1 change: 1 addition & 0 deletions examples/multiple_test_pages/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
node_modules
9 changes: 9 additions & 0 deletions examples/multiple_test_pages/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
## Setup

First install dependencies

npm install

Then, just run tests

npm test
3 changes: 3 additions & 0 deletions examples/multiple_test_pages/hello.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
function hello(name){
return "hello " + (name || 'world');
}
13 changes: 13 additions & 0 deletions examples/multiple_test_pages/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"devDependencies": {
"qunitjs": "^1.20.0"
},
"scripts": {
"test": "node ../../testem.js ci -P 10"
},
"repository": {
"type": "git",
"url": "git://github.com/testem/testem.git"
},
"license": "MIT"
}
18 changes: 18 additions & 0 deletions examples/multiple_test_pages/test.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<!doctype html>
<html>
<head>
<title>Test'em</title>
<link rel="stylesheet" href="/node_modules/qunitjs/qunit/qunit.css">
<script src="/testem.js"></script>
<script src="/node_modules/qunitjs/qunit/qunit.js"></script>
<script src="hello.js"></script>
<script src="tests.js"></script>
<script>
Testem.hookIntoTestFramework();
</script>
</head>
<body>
<div id="qunit"></div>
<div id="qunit-fixture"></div>
</body>
</html>
11 changes: 11 additions & 0 deletions examples/multiple_test_pages/testem.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"framework": "qunit",
"test_page": [
"test.html?batch=1",
"test.html?batch=2"
],
"src_files": [
"*.js"
],
"parallel": "-1"
}
11 changes: 11 additions & 0 deletions examples/multiple_test_pages/tests.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
for (var i = 0; i < 200; i++) {
if (window.location.search === '?batch=1') {
QUnit.test('says hello world', function(assert){
assert.equal(hello(), 'hello world', 'should equal hello world');
});
} else {
QUnit.test('says hello to person', function(assert){
assert.equal(hello('Bob'), 'hello Bob', 'should equal hello Bob');
});
}
}
2 changes: 1 addition & 1 deletion lib/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ var Server = require('./server');
launch: [Array] list of launchers to use for current runs (defaults to current mode)
skip: [Array] list of launchers to skip
debug: [Boolean] debug mode (false)
test_page: [String] path to the page to use to run tests
test_page: [String|Array] path (or list of paths) to the page (or pages) to use to run tests
growl: [Boolean] enables growl / native notifications (false)
Config-level options:
Expand Down
9 changes: 7 additions & 2 deletions lib/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -296,10 +296,15 @@ App.prototype = {
return callback(err);
}

var testPages = self.config.get('test_page');
launchers.forEach(function(launcher) {
var runner = self.createTestRunner(launcher, reporter);
self.addRunner(runner);
for (var i = 0; i < testPages.length; i++) {
var launcherInstance = launcher.create({ test_page: testPages[i] });
var runner = self.createTestRunner(launcherInstance, reporter);
self.addRunner(runner);
}
});

callback(null);
});
},
Expand Down
73 changes: 49 additions & 24 deletions lib/browser_launcher.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,11 @@ function browsersForPlatform(config, cb) {
var userDataDir = config.getUserDataDir();
var userHomeDir = config.getHomeDir();

function getUserDataDir(browser, id, platform) {
var separator = platform === 'win32' ? '\\' : '/';
return userDataDir + separator + 'testem-' + id + '.' + browser;
}

if (platform === 'win32') {
cb([
{
Expand All @@ -74,9 +79,11 @@ function browsersForPlatform(config, cb) {
'C:\\Program Files\\Mozilla Firefox\\firefox.exe',
'C:\\Program Files (x86)\\Mozilla Firefox\\firefox.exe'
],
args: ['-profile', userDataDir + '\\testem.firefox'],
args: function(config, url) {
return ['-profile', getUserDataDir('firefox', this.id, platform), url];
},
setup: function(config, done) {
setupFirefoxProfile(userDataDir + '/testem.firefox', done);
setupFirefoxProfile(getUserDataDir('firefox', this.id), done);
},
supported: browserExeExists
},
Expand All @@ -88,9 +95,11 @@ function browsersForPlatform(config, cb) {
'C:\\Program Files\\Google\\Chrome\\Application\\Chrome.exe',
'C:\\Program Files (x86)\\Google\\Chrome\\Application\\Chrome.exe'
],
args: ['--user-data-dir=' + userDataDir + '\\testem.chrome', '--no-default-browser-check', '--no-first-run', '--ignore-certificate-errors', '--test-type'],
args: function(config, url) {
return ['--user-data-dir=' + getUserDataDir('chrome', this.id, platform), '--no-default-browser-check', '--no-first-run', '--ignore-certificate-errors', '--test-type', url];
},
setup: function(config, done) {
rimraf(userDataDir + '\\testem.chrome', done);
rimraf(getUserDataDir('chrome', this.id, platform), done);
},
supported: browserExeExists
},
Expand All @@ -108,9 +117,12 @@ function browsersForPlatform(config, cb) {
'C:\\Program Files\\Opera\\opera.exe',
'C:\\Program Files (x86)\\Opera\\opera.exe'
],
args: ['--user-data-dir=' + userDataDir + '\\testem.opera', '-pd', userDataDir + '\\testem.opera'],
args: function(config, url) {
var operaDataDir = getUserDataDir('opera', this.id, platform);
return ['--user-data-dir=' + operaDataDir, '-pd', operaDataDir, url];
},
setup: function(config, done) {
rimraf(userDataDir + '\\testem.opera', done);
rimraf(getUserDataDir('opera', this.id, platform), done);
},
supported: browserExeExists
},
Expand All @@ -130,9 +142,11 @@ function browsersForPlatform(config, cb) {
process.env.HOME + '/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome',
'/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome'
],
args: ['--user-data-dir=' + userDataDir + '/testem.chrome', '--no-default-browser-check', '--no-first-run', '--ignore-certificate-errors', '--test-type'],
args: function(config, url) {
return ['--user-data-dir=' + getUserDataDir('chrome', this.id), '--no-default-browser-check', '--no-first-run', '--ignore-certificate-errors', '--test-type', url];
},
setup: function(config, done) {
rimraf(userDataDir + '/testem.chrome', done);
rimraf(getUserDataDir('chrome', this.id), done);
},
supported: browserExeExists
},
Expand All @@ -142,9 +156,11 @@ function browsersForPlatform(config, cb) {
process.env.HOME + '/Applications/Google\ Chrome\ Canary.app/Contents/MacOS/Google\ Chrome\ Canary',
'/Applications/Google\ Chrome\ Canary.app/Contents/MacOS/Google\ Chrome\ Canary'
],
args: ['--user-data-dir=' + userDataDir + '/testem.chrome-canary', '--no-default-browser-check', '--no-first-run', '--ignore-certificate-errors', '--test-type'],
args: function(config, url) {
return ['--user-data-dir=' + getUserDataDir('chrome-canary', this.id), '--no-default-browser-check', '--no-first-run', '--ignore-certificate-errors', '--test-type', url];
},
setup: function(config, done) {
rimraf(userDataDir + '/testem.chrome-canary', done);
rimraf(getUserDataDir('chrome-canary', this.id), done);
},
supported: browserExeExists
},
Expand All @@ -154,9 +170,11 @@ function browsersForPlatform(config, cb) {
process.env.HOME + '/Applications/Firefox.app/Contents/MacOS/firefox',
'/Applications/Firefox.app/Contents/MacOS/firefox'
],
args: ['-profile', userDataDir + '/testem.firefox'],
args: function(config, url) {
return ['-profile', getUserDataDir('firefox', this.id), url];
},
setup: function(config, done) {
setupFirefoxProfile(userDataDir + '/testem.firefox', done);
setupFirefoxProfile(getUserDataDir('firefox', this.id), done);
},
supported: browserExeExists
},
Expand All @@ -168,10 +186,10 @@ function browsersForPlatform(config, cb) {
],
setup: function(config, done) {
var url = this.getUrl();
fs.writeFile(userDataDir + '/testem.safari.html', '<script>window.location = \'' + url + '\'</script>', done);
fs.writeFile(getUserDataDir('safari', this.id) + '.html', '<script>window.location = \'' + url + '\'</script>', done);
},
args: function() {
return [userDataDir + '/testem.safari.html'];
return [getUserDataDir('safari', this.id) + '.html'];
},
supported: browserExeExists
},
Expand All @@ -181,9 +199,12 @@ function browsersForPlatform(config, cb) {
process.env.HOME + '/Applications/Opera.app/Contents/MacOS/Opera',
'/Applications/Opera.app/Contents/MacOS/Opera'
],
args: ['--user-data-dir=' + userDataDir + '/testem.opera', '-pd', userDataDir + '/testem.opera'],
args: function(config, url) {
var operaDataDir = getUserDataDir('opera', this.id);
return ['--user-data-dir=' + operaDataDir, '-pd', operaDataDir, url];
},
setup: function(config, done) {
rimraf(userDataDir + '/testem.opera', done);
rimraf(getUserDataDir('opera', this.id), done);
},
supported: browserExeExists
},
Expand All @@ -199,29 +220,33 @@ function browsersForPlatform(config, cb) {
{
name: 'Firefox',
exe: 'firefox',
args: ['-no-remote', '-profile', userDataDir + '/testem.firefox'],
args: function(config, url) {
return ['-no-remote', '-profile', getUserDataDir('firefox', this.id), url];
},
setup: function(config, done) {
setupFirefoxProfile(userDataDir + '/testem.firefox', done);
setupFirefoxProfile(getUserDataDir('firefox', this.id), done);
},
supported: findableByWhich
},
{
name: 'Chrome',
exe: 'google-chrome',
args: ['--user-data-dir=' + userDataDir + '/testem.chrome',
'--no-default-browser-check', '--no-first-run', '--ignore-certificate-errors'],
args: function(config, url) {
return ['--user-data-dir=' + getUserDataDir('chrome', this.id), '--no-default-browser-check', '--no-first-run', '--ignore-certificate-errors', url];
},
setup: function(config, done) {
rimraf(userDataDir + '/testem.chrome', done);
rimraf(getUserDataDir('chrome', this.id), done);
},
supported: findableByWhich
},
{
name: 'Chromium',
exe: ['chromium', 'chromium-browser'],
args: ['--user-data-dir=' + userDataDir + '/testem.chromium',
'--no-default-browser-check', '--no-first-run', '--ignore-certificate-errors'],
args: function(config, url) {
return ['--user-data-dir=' + getUserDataDir('chromium', this.id), '--no-default-browser-check', '--no-first-run', '--ignore-certificate-errors', url];
},
setup: function(config, done) {
rimraf(userDataDir + '/testem.chromium', done);
rimraf(getUserDataDir('chromium', this.id), done);
},
supported: findableByWhich
},
Expand Down
22 changes: 14 additions & 8 deletions lib/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ var log = require('npmlog');
var path = require('path');
var async = require('async');
var browser_launcher = require('./browser_launcher');
var Launcher = require('./launcher');
var LauncherFactory = require('./launcher-factory');
var Chars = require('./chars');
var pad = require('./strutils').pad;
var isa = require('./isa');
Expand Down Expand Up @@ -164,6 +164,10 @@ Config.prototype.defaults = {
};

Config.prototype.mergeUrlAndQueryParams = function(urlString, queryParamsObj) {
if (!queryParamsObj) {
return urlString;
}

if (typeof queryParamsObj === 'string') {
if (queryParamsObj[0] === '?') {
queryParamsObj = queryParamsObj.substr(1);
Expand All @@ -185,12 +189,15 @@ Config.prototype.mergeUrlAndQueryParams = function(urlString, queryParamsObj) {
Config.prototype.getTestPage = function() {
var testPage = this.getConfigProperty('test_page');
var queryParams = this.getConfigProperty('query_params');
var self = this;

if (queryParams) {
return this.mergeUrlAndQueryParams(testPage, queryParams);
} else {
return testPage;
if (!Array.isArray(testPage)) {
testPage = [testPage];
}

return testPage.map(function(page) {
return self.mergeUrlAndQueryParams(page, queryParams);
});
};

Config.prototype.getConfigProperty = function(key) {
Expand Down Expand Up @@ -239,16 +246,15 @@ Config.prototype.getAvailableLaunchers = function(cb) {
browser_launcher.getAvailableBrowsers(self, function(availableBrowsers) {
var availableLaunchers = {};
availableBrowsers.forEach(function(browser) {
var newLauncher = new Launcher(browser.name, browser, self);
var newLauncher = new LauncherFactory(browser.name, browser, self);
availableLaunchers[browser.name.toLowerCase()] = newLauncher;

});

// add custom launchers
var customLaunchers = self.get('launchers');
if (customLaunchers) {
for (var name in customLaunchers) {
var newLauncher = new Launcher(name, customLaunchers[name], self);
var newLauncher = new LauncherFactory(name, customLaunchers[name], self);
availableLaunchers[name.toLowerCase()] = newLauncher;
}
}
Expand Down
17 changes: 17 additions & 0 deletions lib/launcher-factory.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
var Launcher = require('./launcher');
var extend = require('lodash').extend;

function LauncherFactory(name, settings, config) {
this.name = name;
this.config = config;
this.settings = settings;
}

LauncherFactory.prototype = {
create: function(options) {
var settings = extend({}, this.settings, options);
return new Launcher(this.name, settings, this.config);
}
};

module.exports = LauncherFactory;
Loading

0 comments on commit b20b971

Please sign in to comment.