From 0e035966de365a8a9843132039e2de01edee1304 Mon Sep 17 00:00:00 2001 From: Trent Willis Date: Fri, 5 Aug 2016 08:03:21 -0700 Subject: [PATCH 1/2] Read in testem config for constructing test page urls --- lib/commands/exam.js | 117 ++++++++++++++++++++++++-- node-tests/unit/commands/exam-test.js | 60 +++++++++++-- package.json | 7 +- testem.json | 2 +- testem.multiple-test-page.js | 16 ++++ testem.no-test-page.js | 12 +++ 6 files changed, 198 insertions(+), 16 deletions(-) create mode 100644 testem.multiple-test-page.js create mode 100644 testem.no-test-page.js diff --git a/lib/commands/exam.js b/lib/commands/exam.js index 03d8f0af..a1cd095a 100644 --- a/lib/commands/exam.js +++ b/lib/commands/exam.js @@ -1,6 +1,9 @@ 'use strict'; +var fs = require('fs-extra'); +var path = require('path'); var TestCommand = require('ember-cli/lib/commands/test'); +var debug = require('debug')('exam'); function addToQuery(query, param, value) { if (!value) { @@ -95,18 +98,118 @@ module.exports = TestCommand.extend({ var config = this._super._generateCustomConfigs.apply(this, arguments); if (this.validator.shouldParallelize) { + // Get the testPage from the generated config or the Testem config and + // use it as the baseUrl for the parallelized test pages + var baseUrl = config.testPage || this._getTestPage(commandOptions.configFile); var count = commandOptions.split; - var baseUrl = config.testPage || 'tests/index.html?hidepassed'; - var splitUrl = addToUrl(baseUrl, '_split', count); - config.testPage = []; - - for (var i = 0; i < count; i++) { - var url = addToUrl(splitUrl, '_partition', i + 1); - config.testPage.push(url); + if (Array.isArray(baseUrl)) { + var command = this; + config.testPage = baseUrl.reduce(function(testPages, baseUrl) { + return testPages.concat(command._generateTestPages(baseUrl, count)); + }, []); + } else { + config.testPage = this._generateTestPages(baseUrl, count); } } return config; + }, + + /** + * Gets the test page specified by the application's Testem config. + * + * @param {String} [configFile] - Path to the config file to use + * @return {String} testPage + */ + _getTestPage: function(configFile) { + // Attempt to read in the testem config and use the test_page definition + var testemConfig = this._readTestemConfig(configFile); + var testPage = testemConfig && testemConfig.test_page; + + // If there is no test_page to use as the testPage, we warn that we're using + // a default value + if (!testPage) { + console.warn('No test_page value found in the config. Defaulting to "tests/index.html?hidepassed"'); + testPage = 'tests/index.html?hidepassed'; + } + + return testPage; + }, + + /** + * Generates the test pages to use for parallel runs. For a given baseUrl, + * it appends the split count and a parition number as query params. + * + * @param {String} baseUrl + * @param {Number} count + * @return {Array} testPages + */ + _generateTestPages: function(baseUrl, count) { + var splitUrl = addToUrl(baseUrl, '_split', count); + var testPages = []; + + for (var i = 0; i < count; i++) { + var url = addToUrl(splitUrl, '_partition', i + 1); + testPages.push(url); + } + + return testPages; + }, + + /** + * Gets the application's testem config by trying a custom file first and then + * defaulting to either `testem.js` or `testem.json`. + * + * @param {String} file + * @return {Object} config + */ + _readTestemConfig: function(file) { + var potentialFiles = [ 'testem.js', 'testem.json' ]; + + if (file) { + potentialFiles.unshift(file); + } + + var configFile = this._findValidFile(potentialFiles); + + return configFile && this._readFileByType(configFile); + }, + + /** + * Given an array of file paths, returns the first one that exists and is + * accessible. Paths are relative to the process' cwd. + * + * @param {Array} files + * @return {String} file + */ + _findValidFile: function(files) { + for (var i = 0; i < files.length; i++) { + var file = path.join(process.cwd(), files[i]); + try { + fs.accessSync(file, fs.F_OK); + return file; + } catch (error) { + debug('Failed to find ' + file + ' due to error: ' + error); + continue; + } + } + }, + + /** + * Reads in a given file according to it's "type" as determined by file + * extension. Supported types are `js` and `json`. + * + * @param {String} file + * @return {Object} fileContents + */ + _readFileByType: function(file) { + var fileType = file.split('.').pop(); + switch (fileType) { + case 'js': + return require(file); + case 'json': + return fs.readJsonSync(file); + } } }); diff --git a/node-tests/unit/commands/exam-test.js b/node-tests/unit/commands/exam-test.js index 390539ad..62d515ce 100644 --- a/node-tests/unit/commands/exam-test.js +++ b/node-tests/unit/commands/exam-test.js @@ -2,6 +2,7 @@ var assert = require('assert'); var MockProject = require('ember-cli/tests/helpers/mock-project'); var Task = require('ember-cli/lib/models/task'); var RSVP = require('rsvp'); +var sinon = require('sinon'); var ExamCommand = require('../../../lib/commands/exam'); var TestOptionsValidator = require('../../../lib/utils/tests-options-validator'); @@ -73,6 +74,20 @@ describe('ExamCommand', function() { assert.equal(called.testRunOptions.query, 'someQuery=derp&hidepassed&seed=1337'); }); }); + + it('should set \'seed=random_seed\' on the query option', function() { + var randomStub = sinon.stub(Math, 'random').returns(' random_seed'); + return command.run({ random: '' }).then(function() { + assert.equal(called.testRunOptions.query, 'seed=random_seed'); + randomStub.restore(); + }); + }); + + it('should set \'weighted\' on the query option', function() { + return command.run({ split: 2, partition: 2, weighted: true }).then(function() { + assert.equal(called.testRunOptions.query, '_split=2&_partition=2&_weighted'); + }); + }); }); describe('_generateCustomConfigs', function() { @@ -90,22 +105,36 @@ describe('ExamCommand', function() { return command._generateCustomConfigs(options); } - it('should have a null test page', function() { + it('should have a null test page when not parallelizing', function() { var config = generateConfig({}); assert.deepEqual(config.testPage, undefined); }); it('should modify the config to have multiple test pages', function() { + var config = generateConfig({ + parallel: true, + split: 2 + }); + + assert.deepEqual(config.testPage, [ + "tests/index.html?hidepassed&derp=herp&_split=2&_partition=1", + "tests/index.html?hidepassed&derp=herp&_split=2&_partition=2" + ]); + }); + + it('should modify the config to have multiple test pages for each test_page in the config file', function() { var config = generateConfig({ parallel: true, split: 2, - useAst: true + configFile: 'testem.multiple-test-page.js' }); assert.deepEqual(config.testPage, [ - "tests/index.html?hidepassed&_split=2&_partition=1", - "tests/index.html?hidepassed&_split=2&_partition=2" + "tests/index.html?hidepassed&derp=herp&_split=2&_partition=1", + "tests/index.html?hidepassed&derp=herp&_split=2&_partition=2", + "tests/index.html?hidepassed&foo=bar&_split=2&_partition=1", + "tests/index.html?hidepassed&foo=bar&_split=2&_partition=2" ]); }); @@ -123,8 +152,7 @@ describe('ExamCommand', function() { parallel: true, split: 2, query: 'foo=bar', - 'test-page': 'tests.html', - useAst: true + 'test-page': 'tests.html' }); assert.deepEqual(config.testPage, [ @@ -132,5 +160,25 @@ describe('ExamCommand', function() { "tests.html?foo=bar&_split=2&_partition=2" ]); }); + + it('should warn if no test_page is defined but use a default', function() { + var warnStub = sinon.stub(console, 'warn'); + + var config = generateConfig({ + parallel: true, + split: 2, + configFile: 'testem.no-test-page.js' + }); + + assert.deepEqual(config.testPage, [ + "tests/index.html?hidepassed&_split=2&_partition=1", + "tests/index.html?hidepassed&_split=2&_partition=2" + ]); + + sinon.assert.calledOnce(warnStub); + sinon.assert.calledWithExactly(warnStub, 'No test_page value found in the config. Defaulting to "tests/index.html?hidepassed"'); + + warnStub.restore(); + }); }) }); diff --git a/package.json b/package.json index e3e76135..b4125a8f 100644 --- a/package.json +++ b/package.json @@ -47,13 +47,16 @@ "istanbul": "^0.4.3", "loader.js": "^4.0.1", "mocha": "^2.3.4", - "rsvp": "^3.2.1" + "rsvp": "^3.2.1", + "sinon": "^1.17.5" }, "keywords": [ "ember-addon" ], "dependencies": { - "ember-cli-babel": "^5.1.6" + "debug": "^2.2.0", + "ember-cli-babel": "^5.1.6", + "fs-extra": "^0.30.0" }, "ember-addon": { "configPath": "tests/dummy/config" diff --git a/testem.json b/testem.json index 360e4c2e..87b81995 100644 --- a/testem.json +++ b/testem.json @@ -1,6 +1,6 @@ { "framework": "qunit", - "test_page": "tests/index.html?hidepassed", + "test_page": "tests/index.html?hidepassed&derp=herp", "disable_watching": true, "launch_in_ci": [ "PhantomJS" diff --git a/testem.multiple-test-page.js b/testem.multiple-test-page.js new file mode 100644 index 00000000..3f417f5e --- /dev/null +++ b/testem.multiple-test-page.js @@ -0,0 +1,16 @@ +module.exports = { + "framework": "qunit", + "test_page": [ + "tests/index.html?hidepassed&derp=herp", + "tests/index.html?hidepassed&foo=bar" + ], + "disable_watching": true, + "launch_in_ci": [ + "PhantomJS" + ], + "launch_in_dev": [ + "PhantomJS", + "Chrome" + ], + "parallel": -1 +}; diff --git a/testem.no-test-page.js b/testem.no-test-page.js new file mode 100644 index 00000000..673f09fb --- /dev/null +++ b/testem.no-test-page.js @@ -0,0 +1,12 @@ +module.exports = { + "framework": "qunit", + "disable_watching": true, + "launch_in_ci": [ + "PhantomJS" + ], + "launch_in_dev": [ + "PhantomJS", + "Chrome" + ], + "parallel": -1 +}; From 6e18049eca4bb355d41be278f0c2211aa9072443 Mon Sep 17 00:00:00 2001 From: Trent Willis Date: Sat, 6 Aug 2016 23:28:51 -0700 Subject: [PATCH 2/2] Don't run Travis on non-master branches --- .travis.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.travis.yml b/.travis.yml index 2489810d..168de99b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -20,6 +20,10 @@ matrix: allow_failures: - env: EMBER_TRY_SCENARIO=ember-canary +branches: + only: + - master + before_install: - export PATH=/usr/local/phantomjs-2.0.0/bin:$PATH - "npm config set spin false"