diff --git a/utils/testrunner/TestRunner.js b/utils/testrunner/TestRunner.js index ee58289ae4..2f9b98fe42 100644 --- a/utils/testrunner/TestRunner.js +++ b/utils/testrunner/TestRunner.js @@ -17,7 +17,6 @@ const util = require('util'); const url = require('url'); const inspector = require('inspector'); -const path = require('path'); const EventEmitter = require('events'); const Multimap = require('./Multimap'); const fs = require('fs'); @@ -452,12 +451,47 @@ class TestPass { } } -function specBuilder(defaultTimeout, action) { +function specBuilder(defaultTimeout, modifiers, attributes, action) { let mode = TestMode.Run; let expectation = TestExpectation.Ok; let repeat = 1; let timeout = defaultTimeout; + const spec = { + Modes: { ...TestMode }, + Expectations: { ...TestExpectation }, + mode() { + return mode; + }, + setMode(m) { + if (mode !== TestMode.Focus) + mode = m; + }, + expectations() { + return [expectation]; + }, + setExpectations(e) { + if (Array.isArray(e)) { + if (e.length > 1) + throw new Error(''); + e = e[0]; + } + expectation = e; + }, + timeout() { + return timeout; + }, + setTimeout(t) { + timeout = t; + }, + repeat() { + return repeat; + }, + setRepeat(r) { + repeat = r; + } + }; + const func = (...args) => { for (let i = 0; i < repeat; ++i) action(mode, expectation, timeout, ...args); @@ -466,25 +500,20 @@ function specBuilder(defaultTimeout, action) { repeat = 1; timeout = defaultTimeout; }; - - func.skip = condition => { - if (condition) - mode = TestMode.Skip; - return func; - }; - func.fail = condition => { - if (condition) - expectation = TestExpectation.Fail; - return func; - }; - func.slow = () => { - timeout = 3 * defaultTimeout; - return func; + for (const { name, callback } of modifiers) { + func[name] = (...args) => { + callback(spec, ...args); + return func; + }; + } + for (const { name, callback } of attributes) { + Object.defineProperty(func, name, { + get: () => { + callback(spec); + return func; + } + }); } - func.repeat = count => { - repeat = count; - return func; - }; return func; } @@ -507,6 +536,8 @@ class TestRunner extends EventEmitter { this._timeout = timeout === 0 ? INFINITE_TIMEOUT : timeout; this._parallel = parallel; this._breakOnFailure = breakOnFailure; + this._modifiers = []; + this._attributes = []; if (MAJOR_NODEJS_VERSION >= 8 && disableTimeoutWhenInspectorIsEnabled) { if (inspector.url()) { @@ -516,24 +547,47 @@ class TestRunner extends EventEmitter { } } - this.describe = specBuilder(this._timeout, (mode, expectation, timeout, ...args) => this._addSuite(mode, expectation, ...args)); - this.fdescribe = specBuilder(this._timeout, (mode, expectation, timeout, ...args) => this._addSuite(TestMode.Focus, expectation, ...args)); - this.xdescribe = specBuilder(this._timeout, (mode, expectation, timeout, ...args) => this._addSuite(TestMode.Skip, expectation, ...args)); - this.it = specBuilder(this._timeout, (mode, expectation, timeout, name, callback) => this._addTest(name, callback, mode, expectation, timeout)); - this.fit = specBuilder(this._timeout, (mode, expectation, timeout, name, callback) => this._addTest(name, callback, TestMode.Focus, expectation, timeout)); - this.xit = specBuilder(this._timeout, (mode, expectation, timeout, name, callback) => this._addTest(name, callback, TestMode.Skip, expectation, timeout)); - this.dit = specBuilder(this._timeout, (mode, expectation, timeout, name, callback) => { - const test = this._addTest(name, callback, TestMode.Focus, expectation, INFINITE_TIMEOUT); - const N = callback.toString().split('\n').length; - for (let i = 0; i < N; ++i) - this._debuggerLogBreakpointLines.set(test.location.filePath, i + test.location.lineNumber); - }); this._debuggerLogBreakpointLines = new Multimap(); this.beforeAll = this._addHook.bind(this, 'beforeAll'); this.beforeEach = this._addHook.bind(this, 'beforeEach'); this.afterAll = this._addHook.bind(this, 'afterAll'); this.afterEach = this._addHook.bind(this, 'afterEach'); + + this._modifiers.push({ name: 'skip', callback: (t, condition) => condition && t.setMode(t.Modes.Skip) }); + this._modifiers.push({ name: 'fail', callback: (t, condition) => condition && t.setExpectations(t.Expectations.Fail) }); + this._modifiers.push({ name: 'slow', callback: (t, condition) => condition && t.setTimeout(t.timeout() * 3) }); + this._modifiers.push({ name: 'repeat', callback: (t, count) => t.setRepeat(count) }); + this._attributes.push({ name: 'focus', callback: t => t.setMode(t.Modes.Focus) }); + this._buildSpecs(); + } + + _buildSpecs() { + this.describe = specBuilder(this._timeout, this._modifiers, this._attributes, (mode, expectation, timeout, ...args) => this._addSuite(mode, expectation, ...args)); + this.fdescribe = specBuilder(this._timeout, this._modifiers, this._attributes, (mode, expectation, timeout, ...args) => this._addSuite(TestMode.Focus, expectation, ...args)); + this.xdescribe = specBuilder(this._timeout, this._modifiers, this._attributes, (mode, expectation, timeout, ...args) => this._addSuite(TestMode.Skip, expectation, ...args)); + this.it = specBuilder(this._timeout, this._modifiers, this._attributes, (mode, expectation, timeout, name, callback) => this._addTest(name, callback, mode, expectation, timeout)); + this.fit = specBuilder(this._timeout, this._modifiers, this._attributes, (mode, expectation, timeout, name, callback) => this._addTest(name, callback, TestMode.Focus, expectation, timeout)); + this.xit = specBuilder(this._timeout, this._modifiers, this._attributes, (mode, expectation, timeout, name, callback) => this._addTest(name, callback, TestMode.Skip, expectation, timeout)); + this.dit = specBuilder(this._timeout, this._modifiers, this._attributes, (mode, expectation, timeout, name, callback) => { + const test = this._addTest(name, callback, TestMode.Focus, expectation, INFINITE_TIMEOUT); + const N = callback.toString().split('\n').length; + for (let i = 0; i < N; ++i) + this._debuggerLogBreakpointLines.set(test.location.filePath, i + test.location.lineNumber); + }); + } + + _buildSpec() { + } + + modifier(name, callback) { + this._modifiers.push({ name, callback }); + this._buildSpecs(); + } + + attribute(name, callback) { + this._attributes.push({ name, callback }); + this._buildSpecs(); } loadTests(module, ...args) { diff --git a/utils/testrunner/test/testrunner.spec.js b/utils/testrunner/test/testrunner.spec.js index 5683517532..126f4052ae 100644 --- a/utils/testrunner/test/testrunner.spec.js +++ b/utils/testrunner/test/testrunner.spec.js @@ -159,6 +159,49 @@ module.exports.addTests = function({testRunner, expect}) { }); }); + describe('TestRunner attributes', () => { + it('should work', async() => { + const t = newTestRunner({timeout: 123}); + const log = []; + + t.modifier('foo', (t, ...args) => { + log.push('foo'); + + expect(t.Modes.Run).toBeTruthy(); + expect(t.Modes.Skip).toBeTruthy(); + expect(t.Modes.Focus).toBeTruthy(); + expect(t.mode()).toBe(t.Modes.Run); + expect(t.Expectations.Ok).toBeTruthy(); + expect(t.Expectations.Fail).toBeTruthy(); + expect(t.expectations()).toEqual([t.Expectations.Ok]); + expect(t.timeout()).toBe(123); + expect(t.repeat()).toBe(1); + + expect(args.length).toBe(2); + expect(args[0]).toBe('uno'); + expect(args[1]).toBe('dos'); + + t.setMode(t.Modes.Focus); + t.setExpectations([t.Expectations.Fail]); + t.setTimeout(234); + t.setRepeat(42); + }); + + t.attribute('bar', t => { + log.push('bar'); + expect(t.mode()).toBe(t.Modes.Focus); + t.setMode(t.Modes.Skip); + expect(t.mode()).toBe(t.Modes.Focus); + expect(t.expectations()).toEqual([t.Expectations.Fail]); + expect(t.timeout()).toBe(234); + expect(t.repeat()).toBe(42); + }); + + t.it.foo('uno', 'dos').bar('test', () => { }); + expect(log).toEqual(['foo', 'bar']); + }); + }); + describe('TestRunner hooks', () => { it('should run all hooks in proper order', async() => { const log = [];