diff --git a/lib/config/options.js b/lib/config/options.js index b126ef9ab..387f8a330 100644 --- a/lib/config/options.js +++ b/lib/config/options.js @@ -87,7 +87,6 @@ module.exports = root( sets: map(section({ files: option({ - defaultValue: 'gemini', validate: function(value) { if (!_.isArray(value) && !_.isString(value)) { throw new GeminiError('"sets.files" must be an array or string'); @@ -116,9 +115,7 @@ module.exports = root( } } }) - }), { - all: {} // Use `all` set with default values if no set specified in config - }), + })), browsers: map(section(browserOptions.getPerBrowser())) })) diff --git a/lib/test-reader/files-filter/identity-filter.js b/lib/test-reader/files-filter/identity-filter.js new file mode 100644 index 000000000..cdc019d5d --- /dev/null +++ b/lib/test-reader/files-filter/identity-filter.js @@ -0,0 +1,9 @@ +'use strict'; + +const FilesFilter = require('./'); + +module.exports = class IdentityFilter extends FilesFilter { + filter(filesToFilter) { + return filesToFilter; + } +}; diff --git a/lib/test-reader/files-filter/index.js b/lib/test-reader/files-filter/index.js new file mode 100644 index 000000000..70ce3d0e1 --- /dev/null +++ b/lib/test-reader/files-filter/index.js @@ -0,0 +1,7 @@ +'use strict'; + +module.exports = class FilesFilter { + filter() { + throw new Error('Cannot call method of the base class'); + } +}; diff --git a/lib/test-reader/files-filter/intersection-filter.js b/lib/test-reader/files-filter/intersection-filter.js new file mode 100644 index 000000000..899ab7b29 --- /dev/null +++ b/lib/test-reader/files-filter/intersection-filter.js @@ -0,0 +1,19 @@ +'use strict'; + +const _ = require('lodash'); + +const FilesFilter = require('./'); + +module.exports = class IntersectionFilter extends FilesFilter { + constructor(files) { + super(); + + this._files = files; + } + + filter(filesToFilter) { + return _.isEmpty(filesToFilter) + ? this._files + : _.intersection(filesToFilter, this._files); + } +}; diff --git a/lib/test-reader/index.js b/lib/test-reader/index.js index 561c42e90..588871b3e 100644 --- a/lib/test-reader/index.js +++ b/lib/test-reader/index.js @@ -1,8 +1,11 @@ 'use strict'; -const q = require('q'); +const path = require('path'); +const _ = require('lodash'); const globExtra = require('glob-extra'); +const q = require('q'); + const SetCollection = require('./set-collection'); const Suite = require('../suite'); const testsApi = require('../tests-api'); @@ -24,10 +27,22 @@ const loadSuites = (sets, emitter) => { return rootSuite; }; +const filesExist = (configSets, cliPaths) => { + return !_.isEmpty(configSets) || !_.isEmpty(cliPaths); +}; + +const getGeminiPath = (projectRoot) => { + return path.resolve(projectRoot, 'gemini'); +}; + module.exports = (cli, config, emitter) => { + const files = filesExist(config.sets, cli.paths) + ? cli.paths + : [getGeminiPath(config.system.projectRoot)]; + return q.all([ SetCollection.create(config, cli.sets), - globExtra.expandPaths(cli.paths, {formats: ['.js']}) + globExtra.expandPaths(files, {formats: ['.js']}) ]) .spread((sets, paths) => { sets.filterFiles(paths); diff --git a/lib/test-reader/set-collection.js b/lib/test-reader/set-collection.js index b3945da71..eebd989d3 100644 --- a/lib/test-reader/set-collection.js +++ b/lib/test-reader/set-collection.js @@ -9,7 +9,11 @@ const Set = require('./set'); module.exports = class SetCollection { static create(config, sets) { - const filteredSets = SetCollection._filter(config.sets, sets); + let filteredSets = SetCollection._filter(config.sets, sets); + + if (_.isEmpty(filteredSets)) { + filteredSets = [{files: {}, browsers: config.getBrowserIds()}]; + } return SetCollection._expand(filteredSets, config.system.projectRoot) .then((sets) => new SetCollection(sets)); @@ -29,10 +33,13 @@ module.exports = class SetCollection { const unknownSets = _.difference(setsToUse, _.keys(sets)); if (!_.isEmpty(unknownSets)) { - throw new GeminiError( - `No such sets: ${unknownSets.join(', ')}. Use one of the sets, specified ` + - `in config file: ${_.keys(sets).join(', ')}` - ); + let error = `No such sets: ${unknownSets.join(', ')}.`; + + if (!_.isEmpty(sets)) { + error += ` Use one of the sets, specified in config file: ${_.keys(sets).join(', ')}`; + } + + throw new GeminiError(error); } } diff --git a/lib/test-reader/set.js b/lib/test-reader/set.js index 5f8b70446..832ed2855 100644 --- a/lib/test-reader/set.js +++ b/lib/test-reader/set.js @@ -2,6 +2,9 @@ const _ = require('lodash'); +const IdentityFilter = require('./files-filter/identity-filter'); +const IntersectionFilter = require('./files-filter/intersection-filter'); + module.exports = class Set { static create(set) { return new Set(set); @@ -9,6 +12,10 @@ module.exports = class Set { constructor(set) { this._set = set; + + this._filesFilter = _.isEmpty(this._set.files) + ? new IdentityFilter() + : new IntersectionFilter(this._set.files); } getFiles() { @@ -20,8 +27,6 @@ module.exports = class Set { } filterFiles(files) { - if (!_.isEmpty(files)) { - this._set.files = _.intersection(files, this._set.files); - } + this._set.files = this._filesFilter.filter(files); } }; diff --git a/test/unit/config-options/sets.test.js b/test/unit/config-options/sets.test.js index 77d22993a..dd15245bd 100644 --- a/test/unit/config-options/sets.test.js +++ b/test/unit/config-options/sets.test.js @@ -1,12 +1,14 @@ 'use strict'; -var parser = require('lib/config/options'), - GeminiError = require('lib/errors/gemini-error'), - _ = require('lodash'); - -describe('config.sets', function() { - /// - function createConfig(opts) { - var REQUIRED_OPTIONS = { + +const MissingOptionError = require('gemini-configparser').MissingOptionError; +const _ = require('lodash'); + +const parser = require('lib/config/options'); +const GeminiError = require('lib/errors/gemini-error'); + +describe('config.sets', () => { + const createConfig = (opts) => { + const REQUIRED_OPTIONS = { system: { projectRoot: '/some/path' }, @@ -14,28 +16,28 @@ describe('config.sets', function() { desiredCapabilities: {} }; - var options = _.extend({}, REQUIRED_OPTIONS, opts); + const options = _.extend({}, REQUIRED_OPTIONS, opts); return parser({ options: options, env: {}, argv: [] }); - } + }; - describe('files', function() { - it('should be `gemini` by default', function() { - var config = createConfig({ - sets: { - someSet: {} - } - }); - - assert.deepEqual(config.sets.someSet.files, ['gemini']); + describe('files', () => { + it('should throw an error if files are not specified', () => { + assert.throws(() => { + createConfig({ + sets: { + someSet: {} + } + }); + }, MissingOptionError); }); - it('should convert string to array of strings', function() { - var config = createConfig({ + it('should convert string to array of strings', () => { + const config = createConfig({ sets: { someSet: { files: 'some/path' @@ -46,8 +48,8 @@ describe('config.sets', function() { assert.deepEqual(config.sets.someSet.files, ['some/path']); }); - it('should not accept non-string arrays', function() { - assert.throws(function() { + it('should not accept non-string arrays', () => { + assert.throws(() => { createConfig({ sets: { someSet: { @@ -58,8 +60,8 @@ describe('config.sets', function() { }, GeminiError); }); - it('should accept array with strings', function() { - var config = createConfig({ + it('should accept array with strings', () => { + const config = createConfig({ sets: { someSet: { files: [ @@ -77,26 +79,29 @@ describe('config.sets', function() { }); }); - describe('browsers', function() { - it('should contain all browsers by default', function() { + describe('browsers', () => { + it('should contain all browsers by default', () => { var config = createConfig({ browsers: { b1: {}, b2: {} }, sets: { - someSet: {} + someSet: { + files: ['some/path'] + } } }); assert.deepEqual(config.sets.someSet.browsers, ['b1', 'b2']); }); - it('should not accept non-arrays', function() { - assert.throws(function() { + it('should not accept non-arrays', () => { + assert.throws(() => { createConfig({ sets: { someSet: { + files: ['some/path'], browsers: 'something' } } @@ -104,8 +109,8 @@ describe('config.sets', function() { }, GeminiError); }); - it('should not accept unknown browsers', function() { - assert.throws(function() { + it('should not accept unknown browsers', () => { + assert.throws(() => { createConfig({ browsers: { b1: {}, @@ -113,6 +118,7 @@ describe('config.sets', function() { }, sets: { someSet: { + files: ['some/path'], browsers: ['b3'] } } @@ -120,17 +126,19 @@ describe('config.sets', function() { }, GeminiError); }); - it('should accept configured browsers', function() { - var config = createConfig({ + it('should accept configured browsers', () => { + const config = createConfig({ browsers: { b1: {}, b2: {} }, sets: { set1: { + files: ['some/path'], browsers: ['b1'] }, set2: { + files: ['other/path'], browsers: ['b2'] } } @@ -141,15 +149,14 @@ describe('config.sets', function() { }); }); - it('should have `all` set with default values if no set specified', function() { - var config = createConfig({ + it('should not have default set if sets are not specified', () => { + const config = createConfig({ browsers: { b1: {}, b2: {} } }); - assert.deepEqual(config.sets.all.files, ['gemini']); - assert.deepEqual(config.sets.all.browsers, ['b1', 'b2']); + assert.deepEqual(config.sets, {}); }); }); diff --git a/test/unit/test-reader/index.test.js b/test/unit/test-reader/index.test.js index 90089962c..0518a7ae8 100644 --- a/test/unit/test-reader/index.test.js +++ b/test/unit/test-reader/index.test.js @@ -100,6 +100,17 @@ describe('test-reader', () => { }); }); + it('should use gemini folder if sets are not specified in config and paths are not passed', () => { + const config = { + getBrowserIds: () => [] + }; + + globExtra.expandPaths.withArgs(['/root/gemini']).returns(q(['/root/gemini/file.js'])); + + return readTests_({config}) + .then(() => assert.calledWith(utils.requireWithNoCache, '/root/gemini/file.js')); + }); + it('should load suites related to sets from config', () => { const config = { sets: { diff --git a/test/unit/test-reader/set-collection.test.js b/test/unit/test-reader/set-collection.test.js index 9b0e1bc3a..6d87da28d 100644 --- a/test/unit/test-reader/set-collection.test.js +++ b/test/unit/test-reader/set-collection.test.js @@ -20,6 +20,7 @@ describe('set-collection', () => { const mkConfigStub = (opts) => { return _.defaults(opts || {}, { sets: opts.sets || {}, + getBrowserIds: opts.getBrowserIds, system: { projectRoot: '/root' } @@ -32,6 +33,21 @@ describe('set-collection', () => { afterEach(() => sandbox.restore()); + describe('sets are not specified in config', () => { + it('should throw an error if an unknown set was passed', () => { + assert.throws(() => SetCollection.create(mkConfigStub(), ['unknown-set'], /unknown-set/)); + }); + + it('should create new set with empty files and browsers from config', () => { + sandbox.stub(Set, 'create').returns(mkSetStub()); + + const getBrowserIds = sandbox.stub().returns(['b1', 'b2']); + + return SetCollection.create(mkConfigStub({getBrowserIds})) + .then(() => assert.calledWith(Set.create, {files: [], browsers: ['b1', 'b2']})); + }); + }); + it('should create collection for specified sets', () => { const config = mkConfigStub({ sets: { @@ -94,10 +110,10 @@ describe('set-collection', () => { } }); - assert.throws(() => SetCollection.create(config, ['set3']), /set3(.+) set1, set2/); + assert.throws(() => SetCollection.create(config, ['unknown-set']), /unknown-set(.+) set1, set2/); }); - it('should filter sets files by passed files', () => { + it('should filter passed files if sets are specified in config', () => { const sets = { set1: mkSetStub(), set2: mkSetStub() diff --git a/test/unit/test-reader/set.test.js b/test/unit/test-reader/set.test.js index 5471afc04..d4ecd4680 100644 --- a/test/unit/test-reader/set.test.js +++ b/test/unit/test-reader/set.test.js @@ -46,5 +46,14 @@ describe('Set', () => { assert.deepEqual(files, ['some/path/file.js']); }); + + it('should return passed files if set files are empty', () => { + set = new Set({files: []}); + set.filterFiles(['some/path/file.js']); + + const files = set.getFiles(); + + assert.deepEqual(files, ['some/path/file.js']); + }); }); });