From 7b1853a1c1ac584e43ac7f0a5f55e5dba5371d32 Mon Sep 17 00:00:00 2001 From: dmitriy96 Date: Thu, 18 Jan 2018 17:38:03 +0300 Subject: [PATCH] feat: "skip" and "only" methods for tests API --- doc/tests.md | 113 ++++--- lib/suite.js | 18 +- lib/tests-api/skip/index.js | 42 +++ lib/tests-api/skip/only-builder.js | 59 ++++ lib/tests-api/skip/skip-builder.js | 52 ++++ lib/tests-api/suite-builder.js | 152 ++++----- test/unit/suite.js | 30 +- test/unit/tests-api/skip/only-builder.js | 258 ++++++++++++++++ test/unit/tests-api/skip/skip-builder.js | 239 +++++++++++++++ test/unit/tests-api/suite-builder.js | 375 +++++++++-------------- 10 files changed, 932 insertions(+), 406 deletions(-) create mode 100644 lib/tests-api/skip/index.js create mode 100644 lib/tests-api/skip/only-builder.js create mode 100644 lib/tests-api/skip/skip-builder.js create mode 100644 test/unit/tests-api/skip/only-builder.js create mode 100644 test/unit/tests-api/skip/skip-builder.js diff --git a/doc/tests.md b/doc/tests.md index 4d7ce336b..2998ed42d 100644 --- a/doc/tests.md +++ b/doc/tests.md @@ -71,47 +71,6 @@ All methods are chainable: (See `tolerance`option description in [config](./config.md) documentation for details). -* `skip([browser])` — skip all tests and nested suites for: - - - `skip()` — all browsers; - - - `skip('id')` — browser with specified `id`; - - - `skip('id', comment)` — browser with specified `id` and show `comment` in the report; - - - `skip(/some RegExp/)` — browser with `id` which matches `/some RegExp/`; - - - `skip(/some RegExp/, comment)` — browser with `id` which matches `/some RegExp/` and show `comment` in the report; - - - `skip(['id1', /RegExp1/, ...])` — multiple browsers; - - - `skip(['id1', /RegExp1/, ...], comment)` — multiple browsers and show `comment` in the report. - - All browsers from subsequent calls to `.skip()` are added to the skip list: - - ```js - suite - .skip('id1') - .skip(/RegExp1/); - ``` - - is equivalent to - - ```js - suite.skip([ - 'id1', - /RegExp1/ - ]); - ``` - -* `browsers([browser])` — run all tests and nested suites in specified browsers: - - - `browsers('id')` — browser with specified `id`; - - - `browsers(/some RegExp/)` — browser `id` which matches `/some RegExp/`; - - - `browsers(['id1', /RegExp1/, ...])` — multiple browsers. - * `capture(stateName, [options], callback(actions, find))` — defines a new state to capture. Optional callback describes a sequence of actions to bring the page to this state, starting from a **previous** state of the suite. @@ -169,6 +128,72 @@ All methods are chainable: * `after(callback(actions, find))` — use this function to execute some code after the last state. The arguments of a callback are the same as for `capture` and `before` callbacks and context is shared between all of them. + +### Skip tests + +Sometimes you need to skip tests in specific browsers. For example, tested features +are not available in some browsers yet. + +* `skip.in([browser])` — skip all tests and nested suites for: + + - `skip.in('id')` — browser with specified `id`; + + - `skip.in('id', comment)` — browser with specified `id` and show `comment` in the report; + + - `skip.in(/some RegExp/)` — browser with `id` which matches `/some RegExp/`; + + - `skip.in(/some RegExp/, comment)` — browser with `id` which matches `/some RegExp/` + and show `comment` in the report; + + - `skip.in(['id1', /RegExp1/, ...])` — multiple browsers; + + - `skip.in(['id1', /RegExp1/, ...], comment)` — multiple browsers and show `comment` in the report. + + To skip all tests in suite you can use `skip.in(/.*/)`. + + All browsers from subsequent calls to `.skip.in` are added to the skip list: + + ```js + suite + .skip.in('id1') + .skip.in(/RegExp1/); + ``` + + is equivalent to + + ```js + suite.skip.in([ + 'id1', + /RegExp1/ + ]); + ``` + +* ~`skip([browser])`~ — _deprecated_. +Works the same way as `skip.in`, except to skip all tests you can also write `skip()`. + +* `skip.notIn([browser])` — skip all tests and nested suites for all browsers, +except ones in the arguments. Accepts same arguments as `skip.in`. + +To skip test silently, use `only.in` function. This way, skipped browsers will not appear in the report. + +* `only.in([browser])` — run all tests and nested suites in specified browsers: + + - `only.in('id')` — browser with specified `id`; + + - `only.in(/some RegExp/)` — browser `id` which matches `/some RegExp/`; + + - `only.in('id1', /RegExp1/, ...)` — multiple browsers, also accepts an array as argument. + +* ~`browsers([browser])`~ — _deprecated_. Use `only.in` instead. + +* `only.notIn([browser])` — run all tests and nested suites in all browsers, except +ones in the arguments. Accepts same arguments as `only.in`. + +For example: +```js +suite.only.in(['chrome', 'firefox']); +suite.only.notIn(/ie/, 'opera'); +``` ## Nested suites @@ -180,7 +205,7 @@ browser, even if URL was not changed. ```js gemini.suite('parent', function(parent) { parent.setUrl('/some/path') - .setCaptureElements('.selector1', '.selector2'); + .setCaptureElements('.selector1', '.selector2') .capture('state'); gemini.suite('first child', function(child) { @@ -194,7 +219,7 @@ gemini.suite('parent', function(parent) { child.setCaptureElements('.next-selector') .capture('third state', function(actions, elements) { // ... - }) + }); gemini.suite('grandchild', function(grandchild) { //child suites can have own childs @@ -206,7 +231,7 @@ gemini.suite('parent', function(parent) { gemini.suite('third child', function(child) { //this suite uses completely different URL and set of elements child.setUrl('/some/another/path') - .setCaptureElements('.different-selector'); + .setCaptureElements('.different-selector') .capture('fifth state'); }); }); diff --git a/lib/suite.js b/lib/suite.js index d7ce13cd3..13f8c908f 100644 --- a/lib/suite.js +++ b/lib/suite.js @@ -16,7 +16,7 @@ module.exports = class Suite { constructor(name) { this.name = name; this.url = null; - this.skipped = false; + this.skipped = []; this.captureSelectors = null; this.tolerance = null; this.ignoreSelectors = []; @@ -52,24 +52,10 @@ module.exports = class Suite { } skip(browserSkipMatcher) { - if (this.skipped === true) { - return; - } - - if (!browserSkipMatcher) { - this.skipped = true; - } else { - this.skipped = _.isArray(this.skipped) - ? this.skipped.concat(browserSkipMatcher) - : [browserSkipMatcher]; - } + this.skipped = this.skipped.concat(browserSkipMatcher); } shouldSkip(browserId) { - if (_.isBoolean(this.skipped)) { - return this.skipped; - } - return this.skipped.some((browserSkipMatcher) => { if (browserSkipMatcher.matches(browserId)) { this.skipComment = browserSkipMatcher.comment; diff --git a/lib/tests-api/skip/index.js b/lib/tests-api/skip/index.js new file mode 100644 index 000000000..debd83784 --- /dev/null +++ b/lib/tests-api/skip/index.js @@ -0,0 +1,42 @@ +'use strict'; + +const _ = require('lodash'); + +module.exports = class Skip { + constructor(suite) { + this._suite = suite; + } + + _validateArgs(browsers) { + if (!this._isArrayOfStringsAndRegExps(browsers)) { + throw new TypeError('Browsers must be an array with strings and RegExp objects'); + } + } + + _shouldSkip(browsers, {negate} = {}) { + const fn = negate ? _.every : _.some; + return (browserId) => fn(this._mkBrowsersMatcher(browsers, {negate}), (matcher) => matcher(browserId)); + } + + _mkBrowsersMatcher(browsers, {negate}) { + const mkMatcher = (browser) => _.isRegExp(browser) ? browser.test.bind(browser) : _.isEqual.bind(null, browser); + + return browsers.map((browser) => negate ? _.negate(mkMatcher(browser)) : mkMatcher(browser)); + } + + _isArrayOfStringsAndRegExps(arr) { + return _.isArray(arr) && _.every(arr, (item) => _.isString(item) || _.isRegExp(item)); + } + + in() { + return new Error('Not implemented'); + } + + notIn() { + return new Error('Not implemented'); + } + + buildAPI() { + return new Error('Not implemented'); + } +}; diff --git a/lib/tests-api/skip/only-builder.js b/lib/tests-api/skip/only-builder.js new file mode 100644 index 000000000..1e9a9d9e4 --- /dev/null +++ b/lib/tests-api/skip/only-builder.js @@ -0,0 +1,59 @@ +'use strict'; + +const Skip = require('./index'); +const _ = require('lodash'); + +module.exports = class OnlyBuilder extends Skip { + static create(suite) { + return new OnlyBuilder(suite); + } + + constructor(suite) { + super(suite); + } + + in(...browsers) { + return this._process(browsers); + } + + notIn(...browsers) { + return this._process(browsers, {negate: true}); + } + + _process(browsers, opts = {negate: false}) { + browsers = normalizeArgs(browsers); + this._validateArgs(browsers); + + this._suite.browsers = this._suite.browsers.filter(this._shouldSkip(browsers, opts)); + return this; + } + + buildAPI(context) { + const only = { + in: (...browsers) => { + this.in(...browsers); + return context; + }, + notIn: (...browsers) => { + this.notIn(...browsers); + return context; + } + }; + const browsers = (...browsers) => { + this.in(...browsers); + return context; + }; + + return {only, browsers}; + } +}; + +function normalizeArgs(browsers) { + if (browsers.length === 0) { + return; + } + if (browsers.length === 1 && _.isArray(browsers[0])) { + return browsers[0]; + } + return browsers; +} diff --git a/lib/tests-api/skip/skip-builder.js b/lib/tests-api/skip/skip-builder.js new file mode 100644 index 000000000..b4ef625e5 --- /dev/null +++ b/lib/tests-api/skip/skip-builder.js @@ -0,0 +1,52 @@ +'use strict'; + +const Skip = require('./index'); + +module.exports = class SkipBuilder extends Skip { + static create(suite) { + return new SkipBuilder(suite); + } + + constructor(suite) { + super(suite); + } + + in(browsers, comment) { + return this._process(browsers, comment); + } + + notIn(browsers, comment) { + return this._process(browsers, comment, {negate: true}); + } + + _process(browsers, comment, opts = {negate: false}) { + browsers = [].concat(browsers); + this._validateArgs(browsers); + + this._suite.skip({matches: this._shouldSkip(browsers, opts), comment}); + return this; + } + + buildAPI(context) { + const skip = (browsers, comment) => { + if (!browsers) { + browsers = /.*/; + } + + this.in(browsers, comment); + return context; + }; + + skip.in = (browsers, comment) => { + this.in(browsers, comment); + return context; + }; + + skip.notIn = (browsers, comment) => { + this.notIn(browsers, comment); + return context; + }; + + return {skip}; + } +}; diff --git a/lib/tests-api/suite-builder.js b/lib/tests-api/suite-builder.js index 744554cb6..0cecfb676 100644 --- a/lib/tests-api/suite-builder.js +++ b/lib/tests-api/suite-builder.js @@ -1,62 +1,70 @@ 'use strict'; -var _ = require('lodash'), - State = require('../state'), - find = require('./find-func').find, - ActionsBuilder = require('./actions-builder'); -module.exports = function(suite) { - this.setCaptureElements = function() { - var selectors = argumentsToArray(arguments); +const _ = require('lodash'); +const State = require('../state'); +const {find} = require('./find-func'); +const ActionsBuilder = require('./actions-builder'); +const SkipBuilder = require('./skip/skip-builder'); +const OnlyBuilder = require('./skip/only-builder'); + +module.exports = class SuiteBuilder { + constructor(suite) { + this._suite = suite; + this._injectSkip(); + } + + setCaptureElements(...selectors) { + selectors = _.flatten(selectors); - if (selectors.some(notString)) { + if (!selectors.every(_.isString)) { throw new TypeError('suite.captureElements accepts only strings or array of strings'); } - suite.captureSelectors = selectors; + this._suite.captureSelectors = selectors; return this; - }; + } - this.before = function(hook) { - if (typeof hook !== 'function') { + before(hook) { + if (!_.isFunction(hook)) { throw new TypeError('before hook must be a function'); } - suite.beforeActions = _.clone(suite.beforeActions); - hook.call(suite.context, ActionsBuilder.create(suite.beforeActions), find); + this._suite.beforeActions = _.clone(this._suite.beforeActions); + hook.call(this._suite.context, ActionsBuilder.create(this._suite.beforeActions), find); return this; - }; + } - this.after = function(hook) { - if (typeof hook !== 'function') { + after(hook) { + if (!_.isFunction(hook)) { throw new TypeError('after hook must be a function'); } - var actions = []; - hook.call(suite.context, ActionsBuilder.create(actions), find); - suite.afterActions = actions.concat(suite.afterActions); + const actions = []; + hook.call(this._suite.context, ActionsBuilder.create(actions), find); + this._suite.afterActions = actions.concat(this._suite.afterActions); return this; - }; + } - this.setUrl = function setUrl(url) { - if (typeof url !== 'string') { + setUrl(url) { + if (!_.isString(url)) { throw new TypeError('URL must be string'); } - suite.url = url; + this._suite.url = url; return this; - }; + } - this.setTolerance = function setTolerance(tolerance) { - if (typeof tolerance !== 'number') { + setTolerance(tolerance) { + if (!_.isNumber(tolerance)) { throw new TypeError('tolerance must be number'); } - suite.tolerance = tolerance; + this._suite.tolerance = tolerance; return this; - }; + } - this.capture = function capture(name, opts, cb) { - if (typeof name !== 'string') { + capture(name, opts, cb) { + if (!_.isString(name)) { throw new TypeError('State name should be string'); } @@ -68,90 +76,48 @@ module.exports = function(suite) { cb = cb || _.noop; opts = opts || {}; - if (typeof cb !== 'function') { + if (!_.isFunction(cb)) { throw new TypeError('Second argument of suite.capture must be a function'); } - if (suite.hasStateNamed(name)) { - throw new Error('State "' + name + '" already exists in suite "' + suite.name + '". ' + - 'Choose different name'); + if (this._suite.hasStateNamed(name)) { + throw new Error(`State "${name}" already exists in suite "${this._suite.name}". Choose different name`); } - var state = new State(suite, name); - cb.call(suite.context, ActionsBuilder.create(state.actions), find); + const state = new State(this._suite, name); + cb.call(this._suite.context, ActionsBuilder.create(state.actions), find); if ('tolerance' in opts) { - if (typeof opts.tolerance !== 'number') { + if (!_.isNumber(opts.tolerance)) { throw new TypeError('Tolerance should be number'); } state.tolerance = opts.tolerance; } - suite.addState(state); + this._suite.addState(state); return this; - }; + } - this.ignoreElements = function ignoreElements() { - var selectors = argumentsToArray(arguments); + ignoreElements(...selectors) { + selectors = _.flatten(selectors); if (selectors.some(isNotValidSelector)) { throw new TypeError('suite.ignoreElements accepts strings, object with property "every" as string or array of them'); } - suite.ignoreSelectors = selectors; - return this; - }; - - this.skip = function skip(browser, comment) { - if (!browser) { - suite.skip(); - } else if (_.isArray(browser)) { - browser.forEach(_.bind(this.skip, this, _, comment)); - } else if (_.isString(browser) || _.isRegExp(browser)) { - suite.skip({matches: createMatcher(browser), comment: comment}); - } else { - throw new TypeError('suite.skip browser must be string or RegExp object'); - } - return this; - }; - this.browsers = function browsers(matchers) { - matchers = [].concat(matchers); - if (!isArrayOfStringsAndRegExps(matchers)) { - throw new TypeError('suite.browsers must be string or RegExp object'); - } - - matchers = matchers.map(createMatcher); - suite.browsers = suite.browsers.filter(function(browser) { - return _.some(matchers, function(match) { - return match(browser); - }); - }); + this._suite.ignoreSelectors = selectors; return this; - }; -}; + } -function notString(arg) { - return typeof arg !== 'string'; -} + _injectSkip() { + const skipBuilder = SkipBuilder.create(this._suite); + const onlyBuilder = OnlyBuilder.create(this._suite); -// Check if selector is not a string or not an object with "every" option. -function isNotValidSelector(arg) { - return !(_.isString(arg) || (_.isObject(arg) && _.isString(arg.every))); -} - -function argumentsToArray(args) { - if (args.length === 1 && Array.isArray(args[0])) { - return args[0]; - } else { - return Array.prototype.slice.call(args); + _.extend(this, skipBuilder.buildAPI(this), onlyBuilder.buildAPI(this)); } -} -function isArrayOfStringsAndRegExps(arr) { - return _.every(arr, function(item) { - return _.isString(item) || _.isRegExp(item); - }); -} +}; -function createMatcher(matcher) { - return _.isRegExp(matcher) ? matcher.test.bind(matcher) : _.isEqual.bind(null, matcher); +// Check if selector is not a string or not an object with "every" option. +function isNotValidSelector(selector) { + return !_.isString(selector) && !(_.isObject(selector) && _.isString(selector.every)); } diff --git a/test/unit/suite.js b/test/unit/suite.js index b0a969833..18659bb4c 100644 --- a/test/unit/suite.js +++ b/test/unit/suite.js @@ -172,20 +172,22 @@ describe('suite', () => { matcher2 = {b: 2}; }); - it('should be false by default', () => { - assert.isFalse(suite.skipped); + it('should be empty by default', () => { + assert.deepEqual(suite.skipped, []); }); it('should be changed by skip(object) method', () => { suite.skip(matcher); + assert.deepEqual(suite.skipped, [matcher]); }); it('should be inherited by children', () => { - suite.skip(); + suite.skip(matcher); const child = createSuite('child', suite); - assert.isTrue(child.skipped); + + assert.equal(suite.skipped, child.skipped); }); it('should merge multiple matchers together', () => { @@ -195,20 +197,6 @@ describe('suite', () => { assert.deepEqual(suite.skipped, [matcher, matcher2]); }); - it('should not override `true` by browser list', () => { - suite.skip(); - suite.skip(matcher); - - assert.isTrue(suite.skipped); - }); - - it('should override browser list by `true`', () => { - suite.skip(matcher); - suite.skip(); - - assert.isTrue(suite.skipped); - }); - it('should merge children list with parent', () => { suite.skip(matcher); const child = createSuite('child', suite); @@ -242,12 +230,6 @@ describe('suite', () => { assert.isFalse(suite.shouldSkip('browser')); }); - it('should be "true" for any browser if a suite is skipped', () => { - suite.skip(); - - assert.isTrue(suite.shouldSkip('browser')); - }); - it('should be "true" for a browser if a suite is skipped in it', () => { suite.skip(createBrowserSkipMatcher('1st-skipped-bro')); suite.skip(createBrowserSkipMatcher('2nd-skipped-bro')); diff --git a/test/unit/tests-api/skip/only-builder.js b/test/unit/tests-api/skip/only-builder.js new file mode 100644 index 000000000..818ebcd03 --- /dev/null +++ b/test/unit/tests-api/skip/only-builder.js @@ -0,0 +1,258 @@ +'use strict'; + +const Suite = require('lib/suite'); +const OnlyBuilder = require('lib/tests-api/skip/only-builder'); + +describe('tests-api/skip/only-builder', () => { + const sandbox = sinon.sandbox.create(); + let rootSuite; + let suite; + let onlyBuilder; + + beforeEach(() => { + rootSuite = Suite.create(''); + suite = Suite.create('some-suite', rootSuite); + onlyBuilder = new OnlyBuilder(suite); + }); + + afterEach(() => { + sandbox.restore(); + }); + + const errorMessage = 'Browsers must be an array with strings and RegExp objects'; + + const testShouldThrow = (method) => { + describe('thould throw', () => { + it('without an argument', () => { + assert.throws(() => { + onlyBuilder[method](); + }, TypeError, errorMessage); + }); + + it('if argument is not a string or RegExp', () => { + assert.throws(() => { + onlyBuilder[method](0); + }, TypeError, errorMessage); + }); + + it('if argument is an array of non-strings or non-RegExps', () => { + assert.throws(() => { + onlyBuilder[method]([false]); + }, TypeError, errorMessage); + }); + + it('if argument is an object', () => { + assert.throws(() => { + onlyBuilder[method]({browserName: 'chrome'}); + }, TypeError, errorMessage); + }); + }); + }; + + describe('in', () => { + testShouldThrow('in'); + + describe('should filter suite browsers by', () => { + it('a string', () => { + rootSuite.browsers = ['ie9', 'chrome']; + + onlyBuilder.in('chrome'); + + assert.deepEqual(suite.browsers, ['chrome']); + }); + + it('a RegExp', () => { + rootSuite.browsers = ['ie8', 'ie9', 'chrome']; + + onlyBuilder.in(/ie.+/); + + assert.deepEqual(suite.browsers, ['ie8', 'ie9']); + }); + + it('an empty array', () => { + rootSuite.browsers = ['ie8', 'ie9', 'chrome']; + + onlyBuilder.in([]); + + assert.deepEqual(suite.browsers, []); + }); + + it('an array of strings and RegExps', () => { + rootSuite.browsers = ['ie8', 'ie9', 'opera', 'chrome']; + + onlyBuilder.in([/ie.+/, 'chrome']); + + assert.deepEqual(suite.browsers, ['ie8', 'ie9', 'chrome']); + }); + + it('strings and RegExps', () => { + rootSuite.browsers = ['ie8', 'ie9', 'opera', 'chrome']; + + onlyBuilder.in(/ie.+/, 'chrome'); + + assert.deepEqual(suite.browsers, ['ie8', 'ie9', 'chrome']); + }); + }); + + it('should not set a browser for a suite if it is not specified in a root one', () => { + rootSuite.browsers = ['opera']; + + onlyBuilder.in('chrome'); + + assert.deepEqual(suite.browsers, []); + }); + + it('should be chainable', () => { + assert.equal(onlyBuilder.in('bro'), onlyBuilder); + }); + + it('should filter browsers in all children suites', () => { + const firstChild = Suite.create('firstChild', suite); + const secondChild = Suite.create('secondChild', suite); + + rootSuite.browsers = ['ie8', 'ie9', 'opera', 'chrome']; + + new OnlyBuilder(suite).in([/ie.+/, 'chrome']); + new OnlyBuilder(firstChild).in(/ie.+/); + new OnlyBuilder(secondChild).in('chrome'); + + assert.deepEqual(suite.browsers, ['ie8', 'ie9', 'chrome']); + assert.deepEqual(firstChild.browsers, ['ie8', 'ie9']); + assert.deepEqual(secondChild.browsers, ['chrome']); + }); + + it('should pass filtered browsers from a parent suite to a child one', () => { + const childSuite = Suite.create('child', suite); + rootSuite.browsers = ['ie', 'opera', 'chrome']; + + new OnlyBuilder(suite).in('ie', 'chrome'); + + assert.deepEqual(childSuite.browsers, ['ie', 'chrome']); + }); + }); + + describe('notIn', () => { + testShouldThrow('notIn'); + + describe('should filter suite browsers by', () => { + it('a string', () => { + rootSuite.browsers = ['ie8', 'ie9', 'chrome']; + + onlyBuilder.notIn('chrome'); + + assert.deepEqual(suite.browsers, ['ie8', 'ie9']); + }); + + it('a RegExp', () => { + rootSuite.browsers = ['ie8', 'ie9', 'chrome']; + + onlyBuilder.notIn(/ie.+/); + + assert.deepEqual(suite.browsers, ['chrome']); + }); + + it('an empty array', () => { + rootSuite.browsers = ['ie9', 'chrome']; + + onlyBuilder.notIn([]); + + assert.deepEqual(suite.browsers, ['ie9', 'chrome']); + }); + + it('an array of strings and RegExps', () => { + rootSuite.browsers = ['ie8', 'ie9', 'opera', 'chrome']; + + onlyBuilder.notIn([/ie.+/, 'chrome']); + + assert.deepEqual(suite.browsers, ['opera']); + }); + }); + + it('should be chainable', () => { + assert.equal(onlyBuilder.notIn('bro'), onlyBuilder); + }); + + it('should filter browsers in all children suites', () => { + const firstChild = Suite.create('firstChild', suite); + const secondChild = Suite.create('secondChild', suite); + + rootSuite.browsers = ['ie8', 'ie9', 'opera', 'chrome']; + + new OnlyBuilder(suite).notIn(['opera']); + new OnlyBuilder(firstChild).notIn(/ie.+/); + new OnlyBuilder(secondChild).notIn('chrome'); + + assert.deepEqual(suite.browsers, ['ie8', 'ie9', 'chrome']); + assert.deepEqual(firstChild.browsers, ['chrome']); + assert.deepEqual(secondChild.browsers, ['ie8', 'ie9']); + }); + + it('should pass filtered browsers from a parent suite to a child one', () => { + const childSuite = Suite.create('child', suite); + rootSuite.browsers = ['opera', 'chrome']; + + new OnlyBuilder(suite).notIn('chrome'); + + assert.deepEqual(childSuite.browsers, ['opera']); + }); + }); + + describe('buildAPI', () => { + let api; + + beforeEach(() => { + api = onlyBuilder.buildAPI(suite); + }); + + describe('only.in', () => { + it(`should call OnlyBuilder's .in method`, () => { + sandbox.spy(onlyBuilder, 'in'); + + api.only.in('browser1', 'browser2'); + + assert.calledWith(onlyBuilder.in, 'browser1', 'browser2'); + }); + + it('should return suite instance', () => { + const returnValue = api.only.in('browser1', 'browser2'); + + assert.equal(returnValue, suite); + }); + }); + + describe('only.notIn', () => { + it(`should call OnlyBuilder's .notIn method`, () => { + sandbox.spy(onlyBuilder, 'notIn'); + + api.only.notIn(['browser1', 'browser2']); + + assert.calledWith(onlyBuilder.notIn, ['browser1', 'browser2']); + }); + + it('should return suite instance', () => { + const returnValue = api.only.notIn('browser1', 'browser2'); + + assert.equal(returnValue, suite); + }); + }); + + describe('browsers', () => { + beforeEach(() => { + sandbox.spy(onlyBuilder, 'in'); + sandbox.spy(onlyBuilder, 'notIn'); + }); + + it(`should call OnlyBuilder's .in method`, () => { + api.browsers('browser1', 'browser2'); + + assert.calledWith(onlyBuilder.in, 'browser1', 'browser2'); + }); + + it('should return suite instance', () => { + const returnValue = api.browsers('browser1', 'browser2'); + + assert.equal(returnValue, suite); + }); + }); + }); +}); diff --git a/test/unit/tests-api/skip/skip-builder.js b/test/unit/tests-api/skip/skip-builder.js new file mode 100644 index 000000000..27ac96b6c --- /dev/null +++ b/test/unit/tests-api/skip/skip-builder.js @@ -0,0 +1,239 @@ +'use strict'; + +const Suite = require('lib/suite'); +const SkipBuilder = require('lib/tests-api/skip/skip-builder'); + +describe('tests-api/skip/skip-builder', () => { + const sandbox = sinon.sandbox.create(); + let suite; + let skipBuilder; + + beforeEach(() => { + suite = Suite.create(''); + skipBuilder = new SkipBuilder(suite); + }); + + afterEach(() => { + sandbox.restore(); + }); + + const errorMessage = 'Browsers must be an array with strings and RegExp objects'; + + const testShouldThrow = (method) => { + describe('should throw', () => { + it('without an argument', () => { + assert.throws(() => { + skipBuilder[method](); + }, TypeError, errorMessage); + }); + + it('if argument is not a string or RegExp', () => { + assert.throws(() => { + skipBuilder[method](0); + }, TypeError, errorMessage); + }); + + it('if argument is array with non-string or non-RegExp', () => { + assert.throws(() => { + skipBuilder[method]([false]); + }, TypeError, errorMessage); + }); + + it('if argument is an object', () => { + assert.throws(() => { + skipBuilder[method]({browserName: 'name', version: '123', id: 'browser'}); + }, TypeError, errorMessage); + }); + }); + }; + + describe('in', () => { + testShouldThrow('in'); + + describe('should accept', () => { + it('browser string id', () => { + skipBuilder.in('opera'); + + assert.isTrue(suite.shouldSkip('opera')); + assert.isFalse(suite.shouldSkip('firefox')); + }); + + it('browser RegExp', () => { + skipBuilder.in(/ie1.*/); + + assert.isTrue(suite.shouldSkip('ie11')); + assert.isFalse(suite.shouldSkip('ie8')); + }); + + it(`array of string ids and RegExp's`, () => { + skipBuilder.in([ + 'ie11', + /firefox/ + ]); + + assert.isTrue(suite.shouldSkip('ie11')); + assert.isTrue(suite.shouldSkip('firefox33')); + assert.isFalse(suite.shouldSkip('chrome')); + }); + + it('empty array', () => { + skipBuilder.in([]); + + assert.isFalse(suite.shouldSkip('any_browser')); + }); + + it('comments', () => { + skipBuilder.in('chrome', 'comment'); + + assert.isTrue(suite.shouldSkip('chrome')); + assert.equal(suite.skipComment, 'comment'); + }); + }); + + it('should chain skip methods', () => { + skipBuilder + .in('ie11') + .in(/firefox/); + + assert.isTrue(suite.shouldSkip('ie11')); + assert.isTrue(suite.shouldSkip('firefox33')); + assert.isFalse(suite.shouldSkip('chrome')); + }); + }); + + describe('notIn', () => { + testShouldThrow('notIn'); + + describe('should accept', () => { + it('browser string id', () => { + skipBuilder.notIn('opera'); + + assert.equal(suite.skipped.length, 1); + assert.isFalse(suite.shouldSkip('opera')); + assert.isTrue(suite.shouldSkip('firefox')); + }); + + it('browser RegExp', () => { + skipBuilder.notIn(/ie1.*/); + + assert.isFalse(suite.shouldSkip('ie11')); + assert.isTrue(suite.shouldSkip('ie8')); + }); + + it(`array of string ids and RegExp's`, () => { + skipBuilder.notIn([ + 'ie11', + /firefox/ + ]); + + assert.isTrue(suite.shouldSkip('chrome')); + assert.isFalse(suite.shouldSkip('ie11')); + assert.isFalse(suite.shouldSkip('firefox33')); + }); + + it('empty array', () => { + skipBuilder.notIn([]); + + assert.isTrue(suite.shouldSkip('any_browser')); + }); + + it('comments', () => { + skipBuilder.notIn('chrome', 'comment'); + + assert.isTrue(suite.shouldSkip('firefox')); + assert.equal(suite.skipComment, 'comment'); + }); + }); + + it('should chain skip.notIn methods', () => { + skipBuilder + .notIn('ie11', 'not in ie11') + .notIn(/firefox/, 'not in firefox'); + + assert.isTrue(suite.shouldSkip('firefox33')); + assert.equal(suite.skipComment, 'not in ie11'); + assert.isTrue(suite.shouldSkip('ie11')); + assert.equal(suite.skipComment, 'not in firefox'); + }); + }); + + describe('buildAPI', () => { + let api; + + beforeEach(() => { + api = skipBuilder.buildAPI(suite); + }); + + describe('skip.in', () => { + it(`should call SkipBuilder's .in method`, () => { + sandbox.spy(skipBuilder, 'in'); + + api.skip.in(['browsers'], 'comment'); + + assert.calledWith(skipBuilder.in, ['browsers'], 'comment'); + }); + + it('should return suite instance', () => { + const returnValue = api.skip.in(['browsers'], 'comment'); + + assert.equal(returnValue, suite); + }); + }); + + describe('skip.notIn', () => { + it(`should call SkipBuilder's .notIn method`, () => { + sandbox.spy(skipBuilder, 'notIn'); + + api.skip.notIn(['browsers'], 'comment'); + + assert.calledWith(skipBuilder.notIn, ['browsers'], 'comment'); + }); + + it('should return suite instance', () => { + const returnValue = api.skip.notIn(['browsers'], 'comment'); + + assert.equal(returnValue, suite); + }); + }); + + describe('skip', () => { + beforeEach(() => { + sandbox.spy(skipBuilder, 'in'); + }); + + it(`should call SkipBuilder's .in method`, () => { + api.skip(['browsers'], 'comment'); + + assert.calledWith(skipBuilder.in, ['browsers'], 'comment'); + }); + + it('should return suite instance', () => { + const returnValue = api.skip(['browsers'], 'comment'); + + assert.equal(returnValue, suite); + }); + + it('should skip all browsers if no argument', () => { + api.skip(); + + assert.calledWith(skipBuilder.in, /.*/); + }); + + describe('falsey values', () => { + const skipAllTest = (arg, argDescription = arg) => { + return it(`should skip all browsers if argument is ${argDescription}`, () => { + api.skip(arg); + + assert.calledWith(skipBuilder.in, /.*/); + }); + }; + + skipAllTest(undefined); + skipAllTest(false); + skipAllTest(0); + skipAllTest(null); + skipAllTest('', 'empty string'); + }); + }); + }); +}); diff --git a/test/unit/tests-api/suite-builder.js b/test/unit/tests-api/suite-builder.js index d27f54d60..0fa70f836 100644 --- a/test/unit/tests-api/suite-builder.js +++ b/test/unit/tests-api/suite-builder.js @@ -1,37 +1,39 @@ 'use strict'; -var SuiteBuilder = require('lib/tests-api/suite-builder'), - Suite = require('lib/suite'), - ActionsBuilder = require('lib/tests-api/actions-builder'), - find = require('lib/tests-api/find-func').find; - -describe('tests-api/suite-builder', function() { - var sandbox = sinon.sandbox.create(), - suite, - suiteBuilder; - - beforeEach(function() { +const SuiteBuilder = require('lib/tests-api/suite-builder'); +const Suite = require('lib/suite'); +const ActionsBuilder = require('lib/tests-api/actions-builder'); +const {find} = require('lib/tests-api/find-func'); +const SkipBuilder = require('lib/tests-api/skip/skip-builder'); +const OnlyBuilder = require('lib/tests-api/skip/only-builder'); + +describe('tests-api/suite-builder', () => { + const sandbox = sinon.sandbox.create(); + let suite; + let suiteBuilder; + + beforeEach(() => { suite = Suite.create(''); suiteBuilder = new SuiteBuilder(suite); }); - afterEach(function() { + afterEach(() => { sandbox.restore(); }); - describe('setUrl', function() { - it('should throw if argument is not a string', function() { - assert.throws(function() { + describe('setUrl', () => { + it('should throw if argument is not a string', () => { + assert.throws(() => { suiteBuilder.setUrl({not: 'a string'}); }, TypeError); }); - it('should set url property', function() { + it('should set url property', () => { suiteBuilder.setUrl('http://example.com'); assert.equal(suite.url, 'http://example.com'); }); - it('should be chainable', function() { + it('should be chainable', () => { assert.equal( suiteBuilder.setUrl(''), suiteBuilder @@ -39,19 +41,19 @@ describe('tests-api/suite-builder', function() { }); }); - describe('setTolerance', function() { - it('should throw if argument is not a string', function() { - assert.throws(function() { + describe('setTolerance', () => { + it('should throw if argument is not a string', () => { + assert.throws(() => { suiteBuilder.setTolerance('so much'); }, TypeError); }); - it('should set tolerance passed as number', function() { + it('should set tolerance passed as number', () => { suiteBuilder.setTolerance(25); assert.equal(suite.tolerance, 25); }); - it('should be chainable', function() { + it('should be chainable', () => { assert.equal( suiteBuilder.setTolerance(0), suiteBuilder @@ -59,94 +61,94 @@ describe('tests-api/suite-builder', function() { }); }); - describe('setCaptureElements', function() { - it('should throw if selector is not a string', function() { - assert.throws(function() { + describe('setCaptureElements', () => { + it('should throw if selector is not a string', () => { + assert.throws(() => { suiteBuilder.setCaptureElements({everything: true}); }, TypeError); }); - it('should throw if selector in array is not a string', function() { - assert.throws(function() { + it('should throw if selector in array is not a string', () => { + assert.throws(() => { suiteBuilder.setCaptureElements([{everything: true}, '.selector']); }, TypeError); }); - it('should set captureSelectors property', function() { + it('should set captureSelectors property', () => { suiteBuilder.setCaptureElements('.selector'); assert.deepEqual(suite.captureSelectors, ['.selector']); }); - it('should accept multiple arguments', function() { + it('should accept multiple arguments', () => { suiteBuilder.setCaptureElements('.selector1', '.selector2'); assert.deepEqual(suite.captureSelectors, ['.selector1', '.selector2']); }); - it('should accept array', function() { + it('should accept array', () => { suiteBuilder.setCaptureElements(['.selector1', '.selector2']); assert.deepEqual(suite.captureSelectors, ['.selector1', '.selector2']); }); }); - describe('ignoreElements', function() { - it('should throw if selector is a null', function() { - assert.throws(function() { + describe('ignoreElements', () => { + it('should throw if selector is a null', () => { + assert.throws(() => { suiteBuilder.ignoreElements(null); }, TypeError); }); - it('should throw if selector is object without property "every"', function() { - assert.throws(function() { + it('should throw if selector is object without property "every"', () => { + assert.throws(() => { suiteBuilder.ignoreElements({}); }, TypeError); }); - it('should throw if selector is an object with property "every" that not a string', function() { - assert.throws(function() { + it('should throw if selector is an object with property "every" that not a string', () => { + assert.throws(() => { suiteBuilder.ignoreElements({every: null}); }, TypeError); }); - it('should throw if one of selectors in array has wrong type', function() { - assert.throws(function() { + it('should throw if one of selectors in array has wrong type', () => { + assert.throws(() => { suiteBuilder.ignoreElements([{every: true}, '.selector']); }, TypeError); }); - it('should set ignoreSelectors property as string', function() { + it('should set ignoreSelectors property as string', () => { suiteBuilder.ignoreElements('.selector'); assert.deepEqual(suite.ignoreSelectors, ['.selector']); }); - it('should set ignoreSelectors property as object with property "every"', function() { + it('should set ignoreSelectors property as object with property "every"', () => { suiteBuilder.ignoreElements({every: '.selector'}); assert.deepEqual(suite.ignoreSelectors, [{every: '.selector'}]); }); - it('should accept multiple arguments', function() { + it('should accept multiple arguments', () => { suiteBuilder.ignoreElements('.selector1', {every: '.selector2'}); assert.deepEqual(suite.ignoreSelectors, ['.selector1', {every: '.selector2'}]); }); - it('should accept array', function() { + it('should accept array', () => { suiteBuilder.ignoreElements(['.selector1', {every: '.selector2'}]); assert.deepEqual(suite.ignoreSelectors, ['.selector1', {every: '.selector2'}]); }); }); - describe('before', function() { - beforeEach(function() { + describe('before', () => { + beforeEach(() => { sandbox.stub(ActionsBuilder, 'create').returns(new ActionsBuilder()); }); - it('should call before hook with actions builder and find', function() { - var hook = sinon.stub(); + it('should call before hook with actions builder and find', () => { + const hook = sinon.stub(); suiteBuilder.before(hook); @@ -154,18 +156,18 @@ describe('tests-api/suite-builder', function() { assert.calledWith(hook, sinon.match.instanceOf(ActionsBuilder), find); }); - it('should call before hook and set beforeActions property', function() { + it('should call before hook and set beforeActions property', () => { ActionsBuilder.create.returnsArg(0); - suiteBuilder.before(function(actions) { + suiteBuilder.before((actions) => { actions.push(1, 2, 3); }); assert.deepEqual([1, 2, 3], suite.beforeActions); }); - it('should call before hook with suite context', function() { - var hook = sinon.stub(); + it('should call before hook with suite context', () => { + const hook = sinon.stub(); suiteBuilder.before(hook); @@ -175,26 +177,26 @@ describe('tests-api/suite-builder', function() { ); }); - it('should prepend suite beforeActions with parent beforeActions', function() { - var parent = Suite.create('parent'), - suite = Suite.create('suite', parent); + it('should prepend suite beforeActions with parent beforeActions', () => { + const parent = Suite.create('parent'); + const suite = Suite.create('suite', parent); parent.beforeActions = [1, 2, 3]; ActionsBuilder.create.returnsArg(0); - new SuiteBuilder(suite).before(function(actions) { + new SuiteBuilder(suite).before((actions) => { actions.push(4, 5); }); assert.deepEqual([1, 2, 3, 4, 5], suite.beforeActions); }); - it('should not affect parent beforeActions property', function() { - var parent = Suite.create('parent'), - suite = Suite.create('suite', parent); + it('should not affect parent beforeActions property', () => { + const parent = Suite.create('parent'); + const suite = Suite.create('suite', parent); parent.beforeActions = [1, 2, 3]; ActionsBuilder.create.returnsArg(0); - new SuiteBuilder(suite).before(function(actions) { + new SuiteBuilder(suite).before((actions) => { actions.push(4, 5); }); @@ -202,13 +204,13 @@ describe('tests-api/suite-builder', function() { }); }); - describe('after', function() { - beforeEach(function() { + describe('after', () => { + beforeEach(() => { sandbox.stub(ActionsBuilder, 'create').returns(new ActionsBuilder()); }); - it('should call after hook with actions builder and find', function() { - var hook = sinon.stub(); + it('should call after hook with actions builder and find', () => { + const hook = sinon.stub(); suiteBuilder.after(hook); @@ -216,18 +218,18 @@ describe('tests-api/suite-builder', function() { assert.calledWith(hook, sinon.match.instanceOf(ActionsBuilder), find); }); - it('should call after hook and set afterActions property', function() { + it('should call after hook and set afterActions property', () => { ActionsBuilder.create.returnsArg(0); - suiteBuilder.after(function(actions) { + suiteBuilder.after((actions) => { actions.push(1, 2, 3); }); assert.deepEqual([1, 2, 3], suite.afterActions); }); - it('should call after hook with suite context', function() { - var hook = sinon.stub(); + it('should call after hook with suite context', () => { + const hook = sinon.stub(); suiteBuilder.after(hook); @@ -237,26 +239,26 @@ describe('tests-api/suite-builder', function() { ); }); - it('should append parent afterActions to suite afterActions', function() { - var parent = Suite.create('parent'), - suite = Suite.create('suite', parent); + it('should append parent afterActions to suite afterActions', () => { + const parent = Suite.create('parent'); + const suite = Suite.create('suite', parent); parent.afterActions = [4, 5]; ActionsBuilder.create.returnsArg(0); - new SuiteBuilder(suite).after(function(actions) { + new SuiteBuilder(suite).after((actions) => { actions.push(1, 2, 3); }); assert.deepEqual([1, 2, 3, 4, 5], suite.afterActions); }); - it('should not affect parent afterActions property', function() { - var parent = Suite.create('parent'), - suite = Suite.create('suite', parent); + it('should not affect parent afterActions property', () => { + const parent = Suite.create('parent'); + const suite = Suite.create('suite', parent); parent.afterActions = [4, 5]; ActionsBuilder.create.returnsArg(0); - new SuiteBuilder(suite).after(function(actions) { + new SuiteBuilder(suite).after((actions) => { actions.push(1, 2, 3); }); @@ -264,44 +266,44 @@ describe('tests-api/suite-builder', function() { }); }); - describe('capture', function() { - beforeEach(function() { + describe('capture', () => { + beforeEach(() => { suiteBuilder .setUrl('/path') .setCaptureElements('.element'); }); - it('should throw if first argument is not passed', function() { - assert.throws(function() { + it('should throw if first argument is not passed', () => { + assert.throws(() => { suiteBuilder.capture({not: 'a string'}); }, TypeError); }); - it('should throw if second argument is not a function', function() { - assert.throws(function() { + it('should throw if second argument is not a function', () => { + assert.throws(() => { suiteBuilder.capture('state', 'make me a sandwich'); }, TypeError); }); - it('should not throw if second argument is absent', function() { - assert.doesNotThrow(function() { + it('should not throw if second argument is absent', () => { + assert.doesNotThrow(() => { suiteBuilder.capture('state'); }); }); - it('should create named state', function() { + it('should create named state', () => { suiteBuilder.capture('state'); assert.equal(suite.states[0].name, 'state'); }); - it('should throw if state with such name already exists', function() { - assert.throws(function() { + it('should throw if state with such name already exists', () => { + assert.throws(() => { suiteBuilder.capture('state'); suiteBuilder.capture('state'); }); }); - it('should allow to have multiple states of different names', function() { + it('should allow to have multiple states of different names', () => { suiteBuilder .capture('state 1') .capture('state 2'); @@ -310,13 +312,13 @@ describe('tests-api/suite-builder', function() { assert.equal(suite.states[1].name, 'state 2'); }); - it('should make new state reference the suite', function() { + it('should make new state reference the suite', () => { suiteBuilder.capture('state'); assert.equal(suite.states[0].suite, suite); }); - it('should call passed callback with actions builder and find', function() { - var cb = sinon.stub(); + it('should call passed callback with actions builder and find', () => { + const cb = sinon.stub(); suiteBuilder.capture('state', cb); @@ -324,8 +326,8 @@ describe('tests-api/suite-builder', function() { assert.calledWith(cb, sinon.match.instanceOf(ActionsBuilder), find); }); - it('should call passed callback with suite context', function() { - var cb = sinon.stub(); + it('should call passed callback with suite context', () => { + const cb = sinon.stub(); suiteBuilder.capture('state', cb); @@ -335,184 +337,99 @@ describe('tests-api/suite-builder', function() { ); }); - it('should set `actions` property', function() { + it('should set `actions` property', () => { sandbox.stub(ActionsBuilder, 'create').returnsArg(0); - suiteBuilder.capture('state', function(actions) { + suiteBuilder.capture('state', (actions) => { actions.push(1, 2, 3); }); assert.deepEqual([1, 2, 3], suite.states[0].actions); }); - it('should allow to set tolerance', function() { - suiteBuilder.capture('state', {tolerance: 25}, function() {}); + it('should allow to set tolerance', () => { + suiteBuilder.capture('state', {tolerance: 25}, () => { + }); assert.equal(suite.states[0].tolerance, 25); }); - it('should throw if tolerance is not a number', function() { - assert.throws(function() { - suiteBuilder.capture('state', {tolerance: 'so much'}, function() {}); + it('should throw if tolerance is not a number', () => { + assert.throws(() => { + suiteBuilder.capture('state', {tolerance: 'so much'}, () => { + }); }, TypeError); }); - it('should be chainable', function() { + it('should be chainable', () => { assert.equal(suiteBuilder.capture('state'), suiteBuilder); }); }); - describe('skip', function() { - it('should throw if argument is not a string nor RegExp', function() { - assert.throws(function() { - suiteBuilder.skip(123); - }, TypeError); - }); - - it('should throw if argument is array with non-string or non-RegExp', function() { - assert.throws(function() { - suiteBuilder.skip([123]); - }, TypeError); - }); - - it('should throw if argument is an object', function() { - assert.throws(function() { - suiteBuilder.skip({browserName: 'name', version: '123', id: 'browser'}); - }, Error); - }); + describe('SkipBuilder API', () => { + let suite; + let skipBuilder; + let suiteBuilder; - it('should mark suite as skipped', function() { - suiteBuilder.skip(); - assert.isTrue(suite.skipped); - }); + beforeEach(() => { + suite = Suite.create(''); - it('should accept skipped browser string id', function() { - suiteBuilder.skip('opera'); + skipBuilder = new SkipBuilder(suite); + sandbox.stub(SkipBuilder, 'create').returns(skipBuilder); + sandbox.spy(skipBuilder, 'buildAPI'); - assert.equal(suite.skipped.length, 1); - assert.isTrue(suite.skipped[0].matches('opera')); - assert.isFalse(suite.skipped[0].matches('firefox')); - }); - - it('should accept skipped browser RegExp', function() { - suiteBuilder.skip(/ie1.*/); - - assert.isTrue(suite.skipped[0].matches('ie11')); - assert.isFalse(suite.skipped[0].matches('ie8')); - }); - - it('should accept array of string ids and RegExp\'s', function() { - suiteBuilder.skip([ - 'ie11', - /firefox/ - ]); - - assert.isTrue(suite.skipped[0].matches('ie11')); - assert.isTrue(suite.skipped[1].matches('firefox33')); - }); - }); - - describe('browsers', function() { - var rootSuite; - - beforeEach(function() { - rootSuite = Suite.create(''); - suite = Suite.create('some-suite', rootSuite); suiteBuilder = new SuiteBuilder(suite); }); - it('should throw without an argument', function() { - assert.throws(function() { - suiteBuilder.browsers(); - }, /string or RegExp/); - }); - - it('should throw if an argument is not a string or RegExp', function() { - assert.throws(function() { - suiteBuilder.browsers(123); - }, /string or RegExp/); + it(`should call SkipBuilder's .buildAPI method`, () => { + assert.calledOnce(skipBuilder.buildAPI); }); - it('should throw if an argument is an array of non-strings or non-RegExps', function() { - assert.throws(function() { - suiteBuilder.browsers([123]); - }, /string or RegExp/); - }); - - it('should filter suite browsers by a string', function() { - rootSuite.browsers = ['ie8', 'ie9', 'chrome']; - - suiteBuilder.browsers('chrome'); - - assert.deepEqual(suite.browsers, ['chrome']); - }); - - it('should filter suite browsers by a RegExp', function() { - rootSuite.browsers = ['ie8', 'ie9', 'chrome']; + it(`should extend SkipBuilder API's methods`, () => { + const api = skipBuilder.buildAPI.returnValues[0]; - suiteBuilder.browsers(/ie.+/); - - assert.deepEqual(suite.browsers, ['ie8', 'ie9']); + assert.equal(suiteBuilder.skip, api.skip); + assert.equal(suiteBuilder.skip.in, api.skip.in); + assert.equal(suiteBuilder.skip.notIn, api.skip.notIn); }); - it('should filter suite browsers by an array of strings', function() { - rootSuite.browsers = ['ie8', 'ie9', 'chrome']; - - suiteBuilder.browsers(['chrome']); - - assert.deepEqual(suite.browsers, ['chrome']); + it('should be chainable', () => { + assert.equal(suiteBuilder.skip('bro'), suiteBuilder); + assert.equal(suiteBuilder.skip.in('bro'), suiteBuilder); + assert.equal(suiteBuilder.skip.notIn('bro'), suiteBuilder); }); + }); - it('should filter suite browsers by an array of RegExps', function() { - rootSuite.browsers = ['ie8', 'ie9', 'chrome']; - - suiteBuilder.browsers([/ie.+/]); - - assert.deepEqual(suite.browsers, ['ie8', 'ie9']); - }); - - it('should filter suite browsers by an array of strings and RegExps', function() { - rootSuite.browsers = ['ie8', 'ie9', 'opera', 'chrome']; - - suiteBuilder.browsers([/ie.+/, 'chrome']); - - assert.deepEqual(suite.browsers, ['ie8', 'ie9', 'chrome']); - }); + describe('OnlyBuilder API', () => { + let suite; + let onlyBuilder; + let suiteBuilder; - it('should not set a browser for a suite if it is not specified in a root one', function() { - rootSuite.browsers = ['opera']; + beforeEach(() => { + suite = Suite.create(''); - suiteBuilder.browsers('chrome'); + onlyBuilder = new OnlyBuilder(suite); + sandbox.stub(OnlyBuilder, 'create').returns(onlyBuilder); + sandbox.spy(onlyBuilder, 'buildAPI'); - assert.deepEqual(suite.browsers, []); + suiteBuilder = new SuiteBuilder(suite); }); - it('should be chainable', function() { - assert.equal(suiteBuilder.browsers([]), suiteBuilder); + it(`should call OnlyBuilder's .buildAPI method`, () => { + assert.calledOnce(onlyBuilder.buildAPI); }); - it('should filter browsers in all children suites', function() { - var firstChild = Suite.create('firstChild', suite), - secondChild = Suite.create('secondChild', suite); + it(`should extend OnlyBuilder API's methods`, () => { + const api = onlyBuilder.buildAPI.returnValues[0]; - rootSuite.browsers = ['ie8', 'ie9', 'opera', 'chrome']; - - suiteBuilder.browsers([/ie.+/, 'chrome']); - new SuiteBuilder(firstChild).browsers(/ie.+/); - new SuiteBuilder(secondChild).browsers('chrome'); - - assert.deepEqual(suite.browsers, ['ie8', 'ie9', 'chrome']); - assert.deepEqual(firstChild.browsers, ['ie8', 'ie9']); - assert.deepEqual(secondChild.browsers, ['chrome']); + assert.equal(suiteBuilder.browsers, api.browsers); + assert.equal(suiteBuilder.only.in, api.only.in); + assert.equal(suiteBuilder.only.notIn, api.only.notIn); }); - it('should pass filtered browsers from a parent suite to a child one', function() { - var childSuite = Suite.create('child', suite); - - rootSuite.browsers = ['ie8', 'ie9', 'opera', 'chrome']; - - suiteBuilder.browsers('chrome'); - - assert.deepEqual(childSuite.browsers, ['chrome']); + it('should be chainable', () => { + assert.equal(suiteBuilder.browsers('bro'), suiteBuilder); + assert.equal(suiteBuilder.only.in('bro'), suiteBuilder); + assert.equal(suiteBuilder.only.notIn('bro'), suiteBuilder); }); }); });