diff --git a/lib/jasmine-core/jasmine.js b/lib/jasmine-core/jasmine.js index e058f6a5..f2ebfa23 100644 --- a/lib/jasmine-core/jasmine.js +++ b/lib/jasmine-core/jasmine.js @@ -100,6 +100,7 @@ var getJasmineRequireObj = (function(jasmineGlobal) { j$.StringContaining = jRequire.StringContaining(j$); j$.UserContext = jRequire.UserContext(j$); j$.Suite = jRequire.Suite(j$); + j$.SuiteBuilder = jRequire.SuiteBuilder(j$); j$.Timer = jRequire.Timer(); j$.TreeProcessor = jRequire.TreeProcessor(); j$.version = jRequire.version(); @@ -1100,8 +1101,6 @@ getJasmineRequireObj().Env = function(j$) { const self = this; const global = options.global || j$.getGlobal(); - let totalSpecsDefined = 0; - const realSetTimeout = global.setTimeout; const realClearTimeout = global.clearTimeout; const clearStack = j$.getClearStack(global); @@ -1118,14 +1117,11 @@ getJasmineRequireObj().Env = function(j$) { return r ? r.id : null; }); - let topSuite; let currentSpec = null; const currentlyExecutingSuites = []; - const focusedRunables = []; - let currentDeclarationSuite = null; let hasFailures = false; - let deprecator; let reporter; + let topSuite; /** * This represents the available options to configure Jasmine. @@ -1345,18 +1341,6 @@ getJasmineRequireObj().Env = function(j$) { j$.Expectation.addCoreMatchers(j$.matchers); j$.Expectation.addAsyncCoreMatchers(j$.asyncMatchers); - let nextSpecId = 0; - - function getNextSpecId() { - return 'spec' + nextSpecId++; - } - - let nextSuiteId = 0; - - function getNextSuiteId() { - return 'suite' + nextSuiteId++; - } - const expectationFactory = function(actual, spec) { return j$.Expectation.factory({ matchersUtil: runableResources.makeMatchersUtil(), @@ -1450,43 +1434,6 @@ getJasmineRequireObj().Env = function(j$) { return spec.addExpectationResult(passed, result); } }; - const suiteAsyncExpectationFactory = function(actual, suite) { - return asyncExpectationFactory(actual, suite, 'Suite'); - }; - - const specAsyncExpectationFactory = function(actual, suite) { - return asyncExpectationFactory(actual, suite, 'Spec'); - }; - - function beforeAndAfterFns(targetSuite) { - return function() { - let befores = [], - afters = [], - suite = targetSuite; - - while (suite) { - befores = befores.concat(suite.beforeFns); - afters = afters.concat(suite.afterFns); - - suite = suite.parentSuite; - } - - return { - befores: befores.reverse(), - afters: afters - }; - }; - } - - function getSpecName(spec, suite) { - const fullName = [spec.description], - suiteFullName = suite.getFullName(); - - if (suiteFullName !== '') { - fullName.unshift(suiteFullName); - } - return fullName.join(' '); - } /** * Causes a deprecation warning to be logged to the console and reported to @@ -1548,16 +1495,17 @@ getJasmineRequireObj().Env = function(j$) { new j$.QueueRunner(options).execute(args); } - topSuite = new j$.Suite({ - id: getNextSuiteId(), - description: 'Jasmine__TopLevel__Suite', - expectationFactory: expectationFactory, - asyncExpectationFactory: suiteAsyncExpectationFactory, - autoCleanClosures: config.autoCleanClosures, - onLateError: recordLateError + const suiteBuilder = new j$.SuiteBuilder({ + env: this, + expectationFactory, + asyncExpectationFactory, + onLateError: recordLateError, + specResultCallback, + specStarted, + queueRunnerFactory }); - deprecator = new j$.Deprecator(topSuite); - currentDeclarationSuite = topSuite; + topSuite = suiteBuilder.topSuite; + const deprecator = new j$.Deprecator(topSuite); /** * Provides the root suite, through which all suites and specs can be @@ -1687,8 +1635,8 @@ getJasmineRequireObj().Env = function(j$) { installGlobalErrors(); if (!runablesToRun) { - if (focusedRunables.length) { - runablesToRun = focusedRunables; + if (suiteBuilder.focusedRunables.length) { + runablesToRun = suiteBuilder.focusedRunables; } else { runablesToRun = [topSuite.id]; } @@ -1768,7 +1716,7 @@ getJasmineRequireObj().Env = function(j$) { */ reporter.jasmineStarted( { - totalSpecsDefined: totalSpecsDefined, + totalSpecsDefined: suiteBuilder.totalSpecsDefined, order: order }, function() { @@ -1789,10 +1737,10 @@ getJasmineRequireObj().Env = function(j$) { topSuite.result.failedExpectations.length > 0 ) { overallStatus = 'failed'; - } else if (focusedRunables.length > 0) { + } else if (suiteBuilder.focusedRunables.length > 0) { overallStatus = 'incomplete'; incompleteReason = 'fit() or fdescribe() was found'; - } else if (totalSpecsDefined === 0) { + } else if (suiteBuilder.totalSpecsDefined === 0) { overallStatus = 'incomplete'; incompleteReason = 'No specs found'; } else { @@ -1950,22 +1898,6 @@ getJasmineRequireObj().Env = function(j$) { ); }; - function ensureIsFunction(fn, caller) { - if (!j$.isFunction_(fn)) { - throw new Error( - caller + ' expects a function argument; received ' + j$.getType_(fn) - ); - } - } - - function ensureIsFunctionOrAsync(fn, caller) { - if (!j$.isFunction_(fn) && !j$.isAsyncFunction_(fn)) { - throw new Error( - caller + ' expects a function argument; received ' + j$.getType_(fn) - ); - } - } - function ensureIsNotNested(method) { const runable = currentRunable(); if (runable !== null && runable !== undefined) { @@ -1975,150 +1907,38 @@ getJasmineRequireObj().Env = function(j$) { } } - function suiteFactory(description) { - return new j$.Suite({ - id: getNextSuiteId(), - description: description, - parentSuite: currentDeclarationSuite, - timer: new j$.Timer(), - expectationFactory: expectationFactory, - asyncExpectationFactory: suiteAsyncExpectationFactory, - throwOnExpectationFailure: config.stopSpecOnExpectationFailure, - autoCleanClosures: config.autoCleanClosures, - onLateError: recordLateError - }); - } - - this.describe = function(description, specDefinitions) { + this.describe = function(description, definitionFn) { ensureIsNotNested('describe'); - ensureIsFunction(specDefinitions, 'describe'); - const suite = suiteFactory(description); - if (specDefinitions.length > 0) { - throw new Error('describe does not expect any arguments'); - } - if (currentDeclarationSuite.markedExcluding) { - suite.exclude(); - } - addSpecsToSuite(suite, specDefinitions); - if (suite.parentSuite && !suite.children.length) { - throw new Error( - 'describe with no children (describe() or it()): ' + - suite.getFullName() - ); - } - return suite.metadata; + return suiteBuilder.describe(description, definitionFn).metadata; }; - this.xdescribe = function(description, specDefinitions) { + this.xdescribe = function(description, definitionFn) { ensureIsNotNested('xdescribe'); - ensureIsFunction(specDefinitions, 'xdescribe'); - const suite = suiteFactory(description); - suite.exclude(); - addSpecsToSuite(suite, specDefinitions); - return suite.metadata; + return suiteBuilder.xdescribe(description, definitionFn).metadata; }; - this.fdescribe = function(description, specDefinitions) { + this.fdescribe = function(description, definitionFn) { ensureIsNotNested('fdescribe'); - ensureIsFunction(specDefinitions, 'fdescribe'); - const suite = suiteFactory(description); - suite.isFocused = true; - - focusedRunables.push(suite.id); - unfocusAncestor(); - addSpecsToSuite(suite, specDefinitions); - - return suite.metadata; + return suiteBuilder.fdescribe(description, definitionFn).metadata; }; - function addSpecsToSuite(suite, specDefinitions) { - const parentSuite = currentDeclarationSuite; - parentSuite.addChild(suite); - currentDeclarationSuite = suite; + function specResultCallback(spec, result, next) { + runableResources.clearForRunable(spec.id); + currentSpec = null; - let declarationError = null; - try { - specDefinitions(); - } catch (e) { - declarationError = e; + if (result.status === 'failed') { + hasFailures = true; } - if (declarationError) { - suite.handleException(declarationError); - } - - currentDeclarationSuite = parentSuite; + reportSpecDone(spec, result, next); } - function findFocusedAncestor(suite) { - while (suite) { - if (suite.isFocused) { - return suite.id; - } - suite = suite.parentSuite; - } - - return null; + function specStarted(spec, suite, next) { + currentSpec = spec; + runableResources.initForRunable(spec.id, suite.id); + reporter.specStarted(spec.result, next); } - function unfocusAncestor() { - const focusedAncestor = findFocusedAncestor(currentDeclarationSuite); - if (focusedAncestor) { - for (let i = 0; i < focusedRunables.length; i++) { - if (focusedRunables[i] === focusedAncestor) { - focusedRunables.splice(i, 1); - break; - } - } - } - } - - const specFactory = function(description, fn, suite, timeout) { - totalSpecsDefined++; - const spec = new j$.Spec({ - id: getNextSpecId(), - beforeAndAfterFns: beforeAndAfterFns(suite), - expectationFactory: expectationFactory, - asyncExpectationFactory: specAsyncExpectationFactory, - onLateError: recordLateError, - resultCallback: specResultCallback, - getSpecName: function(spec) { - return getSpecName(spec, suite); - }, - onStart: specStarted, - description: description, - queueRunnerFactory: queueRunnerFactory, - userContext: function() { - return suite.clonedSharedUserContext(); - }, - queueableFn: { - fn: fn, - timeout: timeout || 0 - }, - throwOnExpectationFailure: config.stopSpecOnExpectationFailure, - autoCleanClosures: config.autoCleanClosures, - timer: new j$.Timer() - }); - return spec; - - function specResultCallback(result, next) { - runableResources.clearForRunable(spec.id); - currentSpec = null; - - if (result.status === 'failed') { - hasFailures = true; - } - - reportSpecDone(spec, result, next); - } - - function specStarted(spec, next) { - currentSpec = spec; - runableResources.initForRunable(spec.id, suite.id); - reporter.specStarted(spec.result, next); - } - }; - function reportSpecDone(spec, result, next) { spec.reportedDone = true; reporter.specDone(result, next); @@ -2129,66 +1949,19 @@ getJasmineRequireObj().Env = function(j$) { reporter.suiteDone(result, next); } - this.it_ = function(description, fn, timeout) { - ensureIsNotNested('it'); - // it() sometimes doesn't have a fn argument, so only check the type if - // it's given. - if (arguments.length > 1 && typeof fn !== 'undefined') { - ensureIsFunctionOrAsync(fn, 'it'); - } - - if (timeout) { - j$.util.validateTimeout(timeout); - } - - const spec = specFactory( - description, - fn, - currentDeclarationSuite, - timeout - ); - if (currentDeclarationSuite.markedExcluding) { - spec.exclude(); - } - currentDeclarationSuite.addChild(spec); - - return spec; - }; - this.it = function(description, fn, timeout) { - const spec = this.it_(description, fn, timeout); - return spec.metadata; + ensureIsNotNested('it'); + return suiteBuilder.it(description, fn, timeout).metadata; }; this.xit = function(description, fn, timeout) { ensureIsNotNested('xit'); - // xit(), like it(), doesn't always have a fn argument, so only check the - // type when needed. - if (arguments.length > 1 && typeof fn !== 'undefined') { - ensureIsFunctionOrAsync(fn, 'xit'); - } - const spec = this.it_.apply(this, arguments); - spec.exclude('Temporarily disabled with xit'); - return spec.metadata; + return suiteBuilder.xit(description, fn, timeout).metadata; }; this.fit = function(description, fn, timeout) { ensureIsNotNested('fit'); - ensureIsFunctionOrAsync(fn, 'fit'); - - if (timeout) { - j$.util.validateTimeout(timeout); - } - const spec = specFactory( - description, - fn, - currentDeclarationSuite, - timeout - ); - currentDeclarationSuite.addChild(spec); - focusedRunables.push(spec.id); - unfocusAncestor(); - return spec.metadata; + return suiteBuilder.fit(description, fn, timeout).metadata; }; /** @@ -2257,59 +2030,22 @@ getJasmineRequireObj().Env = function(j$) { this.beforeEach = function(beforeEachFunction, timeout) { ensureIsNotNested('beforeEach'); - ensureIsFunctionOrAsync(beforeEachFunction, 'beforeEach'); - - if (timeout) { - j$.util.validateTimeout(timeout); - } - - currentDeclarationSuite.beforeEach({ - fn: beforeEachFunction, - timeout: timeout || 0 - }); + suiteBuilder.beforeEach(beforeEachFunction, timeout); }; this.beforeAll = function(beforeAllFunction, timeout) { ensureIsNotNested('beforeAll'); - ensureIsFunctionOrAsync(beforeAllFunction, 'beforeAll'); - - if (timeout) { - j$.util.validateTimeout(timeout); - } - - currentDeclarationSuite.beforeAll({ - fn: beforeAllFunction, - timeout: timeout || 0 - }); + suiteBuilder.beforeAll(beforeAllFunction, timeout); }; this.afterEach = function(afterEachFunction, timeout) { ensureIsNotNested('afterEach'); - ensureIsFunctionOrAsync(afterEachFunction, 'afterEach'); - - if (timeout) { - j$.util.validateTimeout(timeout); - } - - afterEachFunction.isCleanup = true; - currentDeclarationSuite.afterEach({ - fn: afterEachFunction, - timeout: timeout || 0 - }); + suiteBuilder.afterEach(afterEachFunction, timeout); }; this.afterAll = function(afterAllFunction, timeout) { ensureIsNotNested('afterAll'); - ensureIsFunctionOrAsync(afterAllFunction, 'afterAll'); - - if (timeout) { - j$.util.validateTimeout(timeout); - } - - currentDeclarationSuite.afterAll({ - fn: afterAllFunction, - timeout: timeout || 0 - }); + suiteBuilder.afterAll(afterAllFunction, timeout); }; this.pending = function(message) { @@ -9899,6 +9635,311 @@ getJasmineRequireObj().Suite = function(j$) { return Suite; }; +getJasmineRequireObj().SuiteBuilder = function(j$) { + class SuiteBuilder { + constructor(options) { + this.env_ = options.env; + this.expectationFactory_ = options.expectationFactory; + this.suiteAsyncExpectationFactory_ = function(actual, suite) { + return options.asyncExpectationFactory(actual, suite, 'Suite'); + }; + this.specAsyncExpectationFactory_ = function(actual, suite) { + return options.asyncExpectationFactory(actual, suite, 'Spec'); + }; + this.onLateError_ = options.onLateError; + this.specResultCallback_ = options.specResultCallback; + this.specStarted_ = options.specStarted; + this.queueRunnerFactory_ = options.queueRunnerFactory; + + this.nextSuiteId_ = 0; + this.nextSpecId_ = 0; + + this.topSuite = this.suiteFactory_('Jasmine__TopLevel__Suite'); + this.currentDeclarationSuite_ = this.topSuite; + this.totalSpecsDefined = 0; + this.focusedRunables = []; + } + + describe(description, definitionFn) { + ensureIsFunction(definitionFn, 'describe'); + const suite = this.suiteFactory_(description); + if (definitionFn.length > 0) { + throw new Error('describe does not expect any arguments'); + } + if (this.currentDeclarationSuite_.markedExcluding) { + suite.exclude(); + } + this.addSpecsToSuite_(suite, definitionFn); + if (suite.parentSuite && !suite.children.length) { + throw new Error( + `describe with no children (describe() or it()): ${suite.getFullName()}` + ); + } + return suite; + } + + fdescribe(description, definitionFn) { + ensureIsFunction(definitionFn, 'fdescribe'); + const suite = this.suiteFactory_(description); + suite.isFocused = true; + + this.focusedRunables.push(suite.id); + this.unfocusAncestor_(); + this.addSpecsToSuite_(suite, definitionFn); + + return suite; + } + + xdescribe(description, definitionFn) { + ensureIsFunction(definitionFn, 'xdescribe'); + const suite = this.suiteFactory_(description); + suite.exclude(); + this.addSpecsToSuite_(suite, definitionFn); + + return suite; + } + + it(description, fn, timeout) { + // it() sometimes doesn't have a fn argument, so only check the type if + // it's given. + if (arguments.length > 1 && typeof fn !== 'undefined') { + ensureIsFunctionOrAsync(fn, 'it'); + } + + return this.it_(description, fn, timeout); + } + + xit(description, fn, timeout) { + // xit(), like it(), doesn't always have a fn argument, so only check the + // type when needed. + if (arguments.length > 1 && typeof fn !== 'undefined') { + ensureIsFunctionOrAsync(fn, 'xit'); + } + const spec = this.it_(description, fn, timeout); + spec.exclude('Temporarily disabled with xit'); + return spec; + } + + fit(description, fn, timeout) { + // Unlike it and xit, the function is required because it doesn't make + // sense to focus on nothing. + ensureIsFunctionOrAsync(fn, 'fit'); + + if (timeout) { + j$.util.validateTimeout(timeout); + } + const spec = this.specFactory_(description, fn, timeout); + this.currentDeclarationSuite_.addChild(spec); + this.focusedRunables.push(spec.id); + this.unfocusAncestor_(); + return spec; + } + + beforeEach(beforeEachFunction, timeout) { + ensureIsFunctionOrAsync(beforeEachFunction, 'beforeEach'); + + if (timeout) { + j$.util.validateTimeout(timeout); + } + + this.currentDeclarationSuite_.beforeEach({ + fn: beforeEachFunction, + timeout: timeout || 0 + }); + } + + beforeAll(beforeAllFunction, timeout) { + ensureIsFunctionOrAsync(beforeAllFunction, 'beforeAll'); + + if (timeout) { + j$.util.validateTimeout(timeout); + } + + this.currentDeclarationSuite_.beforeAll({ + fn: beforeAllFunction, + timeout: timeout || 0 + }); + } + + afterEach(afterEachFunction, timeout) { + ensureIsFunctionOrAsync(afterEachFunction, 'afterEach'); + + if (timeout) { + j$.util.validateTimeout(timeout); + } + + afterEachFunction.isCleanup = true; + this.currentDeclarationSuite_.afterEach({ + fn: afterEachFunction, + timeout: timeout || 0 + }); + } + + afterAll(afterAllFunction, timeout) { + ensureIsFunctionOrAsync(afterAllFunction, 'afterAll'); + + if (timeout) { + j$.util.validateTimeout(timeout); + } + + this.currentDeclarationSuite_.afterAll({ + fn: afterAllFunction, + timeout: timeout || 0 + }); + } + + it_(description, fn, timeout) { + if (timeout) { + j$.util.validateTimeout(timeout); + } + + const spec = this.specFactory_(description, fn, timeout); + if (this.currentDeclarationSuite_.markedExcluding) { + spec.exclude(); + } + this.currentDeclarationSuite_.addChild(spec); + + return spec; + } + + suiteFactory_(description) { + const config = this.env_.configuration(); + return new j$.Suite({ + id: 'suite' + this.nextSuiteId_++, + description, + parentSuite: this.currentDeclarationSuite_, + timer: new j$.Timer(), + expectationFactory: this.expectationFactory_, + asyncExpectationFactory: this.suiteAsyncExpectationFactory_, + throwOnExpectationFailure: config.stopSpecOnExpectationFailure, + autoCleanClosures: config.autoCleanClosures, + onLateError: this.onLateError_ + }); + } + + addSpecsToSuite_(suite, definitionFn) { + const parentSuite = this.currentDeclarationSuite_; + parentSuite.addChild(suite); + this.currentDeclarationSuite_ = suite; + + try { + definitionFn(); + } catch (e) { + suite.handleException(e); + } + + this.currentDeclarationSuite_ = parentSuite; + } + + specFactory_(description, fn, timeout) { + this.totalSpecsDefined++; + const config = this.env_.configuration(); + const suite = this.currentDeclarationSuite_; + const spec = new j$.Spec({ + id: 'spec' + this.nextSpecId_++, + beforeAndAfterFns: beforeAndAfterFns(suite), + expectationFactory: this.expectationFactory_, + asyncExpectationFactory: this.specAsyncExpectationFactory_, + onLateError: this.onLateError_, + resultCallback: (result, next) => { + this.specResultCallback_(spec, result, next); + }, + getSpecName: function(spec) { + return getSpecName(spec, suite); + }, + onStart: (spec, next) => this.specStarted_(spec, suite, next), + description: description, + queueRunnerFactory: this.queueRunnerFactory_, + userContext: function() { + return suite.clonedSharedUserContext(); + }, + queueableFn: { + fn: fn, + timeout: timeout || 0 + }, + throwOnExpectationFailure: config.stopSpecOnExpectationFailure, + autoCleanClosures: config.autoCleanClosures, + timer: new j$.Timer() + }); + return spec; + } + + unfocusAncestor_() { + const focusedAncestor = findFocusedAncestor( + this.currentDeclarationSuite_ + ); + + if (focusedAncestor) { + for (let i = 0; i < this.focusedRunables.length; i++) { + if (this.focusedRunables[i] === focusedAncestor) { + this.focusedRunables.splice(i, 1); + break; + } + } + } + } + } + + function findFocusedAncestor(suite) { + while (suite) { + if (suite.isFocused) { + return suite.id; + } + suite = suite.parentSuite; + } + + return null; + } + + function ensureIsFunction(fn, caller) { + if (!j$.isFunction_(fn)) { + throw new Error( + caller + ' expects a function argument; received ' + j$.getType_(fn) + ); + } + } + + function ensureIsFunctionOrAsync(fn, caller) { + if (!j$.isFunction_(fn) && !j$.isAsyncFunction_(fn)) { + throw new Error( + caller + ' expects a function argument; received ' + j$.getType_(fn) + ); + } + } + + function beforeAndAfterFns(targetSuite) { + return function() { + let befores = [], + afters = [], + suite = targetSuite; + + while (suite) { + befores = befores.concat(suite.beforeFns); + afters = afters.concat(suite.afterFns); + + suite = suite.parentSuite; + } + + return { + befores: befores.reverse(), + afters: afters + }; + }; + } + + function getSpecName(spec, suite) { + const fullName = [spec.description], + suiteFullName = suite.getFullName(); + + if (suiteFullName !== '') { + fullName.unshift(suiteFullName); + } + return fullName.join(' '); + } + + return SuiteBuilder; +}; + getJasmineRequireObj().Timer = function() { const defaultNow = (function(Date) { return function() { diff --git a/spec/core/EnvSpec.js b/spec/core/EnvSpec.js index 679bd0d0..3d68f24c 100644 --- a/spec/core/EnvSpec.js +++ b/spec/core/EnvSpec.js @@ -316,7 +316,7 @@ describe('Env', function() { it('calls spec.exclude with "Temporarily disabled with xit"', function() { const excludeSpy = jasmine.createSpy(); - spyOn(env, 'it_').and.returnValue({ + spyOn(jasmineUnderTest.SuiteBuilder.prototype, 'it_').and.returnValue({ exclude: excludeSpy }); env.xit('foo', function() {}); @@ -327,7 +327,7 @@ describe('Env', function() { const pendSpy = jasmine.createSpy(); const realExclude = jasmineUnderTest.Spec.prototype.exclude; - spyOn(env, 'it_').and.returnValue({ + spyOn(jasmineUnderTest.SuiteBuilder.prototype, 'it_').and.returnValue({ exclude: realExclude, pend: pendSpy }); diff --git a/spec/core/SuiteBuilderSpec.js b/spec/core/SuiteBuilderSpec.js new file mode 100644 index 00000000..a24fca1e --- /dev/null +++ b/spec/core/SuiteBuilderSpec.js @@ -0,0 +1,178 @@ +describe('SuiteBuilder', function() { + beforeEach(function() { + // Rethrow exceptions to ease debugging + spyOn(jasmineUnderTest.Suite.prototype, 'handleException').and.callFake( + function(e) { + throw e; + } + ); + spyOn(jasmineUnderTest.Spec.prototype, 'handleException').and.callFake( + function(e) { + throw e; + } + ); + }); + + it('creates the top suite', function() { + const env = { configuration: () => ({}) }; + const suiteBuilder = new jasmineUnderTest.SuiteBuilder({ env }); + + expect(suiteBuilder.topSuite).toBeInstanceOf(jasmineUnderTest.Suite); + expect(suiteBuilder.topSuite.description).toEqual( + 'Jasmine__TopLevel__Suite' + ); + expect(suiteBuilder.topSuite.parentSuite).toBeUndefined(); + }); + + describe('#describe', function() { + definesSuites('describe'); + }); + + describe('#fdescribe', function() { + definesSuites('fdescribe'); + + it('focuses the suite', function() { + const env = { configuration: () => ({}) }; + const suiteBuilder = new jasmineUnderTest.SuiteBuilder({ env }); + + const suite = suiteBuilder.fdescribe('a suite', function() { + suiteBuilder.it('a spec'); + }); + + expect(suite.isFocused).toBeTrue(); + expect(suiteBuilder.focusedRunables).toEqual([suite.id]); + }); + + it('unfocuses any focused ancestor suite', function() { + const env = { configuration: () => ({}) }; + const suiteBuilder = new jasmineUnderTest.SuiteBuilder({ env }); + + const grandparent = suiteBuilder.fdescribe('a suite', function() { + suiteBuilder.describe('another suite', function() { + suiteBuilder.fdescribe('the focused suite', function() { + suiteBuilder.it('a spec'); + }); + }); + }); + + expect(suiteBuilder.focusedRunables).not.toContain(grandparent.id); + }); + }); + + describe('#xdescribe', function() { + definesSuites('xdescribe'); + + it('excludes the suite', function() { + const env = { configuration: () => ({}) }; + const suiteBuilder = new jasmineUnderTest.SuiteBuilder({ env }); + + const suite = suiteBuilder.xdescribe('a suite', function() { + suiteBuilder.it('a spec'); + }); + + expect(suite.markedExcluding).toBeTrue(); + }); + + it('causes child suites to be marked excluded', function() { + const env = { configuration: () => ({}) }; + const suiteBuilder = new jasmineUnderTest.SuiteBuilder({ env }); + + let suite; + suiteBuilder.xdescribe('a suite', function() { + suite = suiteBuilder.describe('another suite', function() { + suiteBuilder.it('a spec'); + }); + }); + + expect(suite.markedExcluding).toBeTrue(); + }); + }); + + describe('#it', function() { + definesSpecs('it'); + }); + + describe('#fit', function() { + definesSpecs('fit'); + }); + + describe('#xit', function() { + definesSpecs('xit'); + }); + + function definesSuites(fnName) { + it('links suites to their parents and children', function() { + const env = { configuration: () => ({}) }; + const suiteBuilder = new jasmineUnderTest.SuiteBuilder({ env }); + + let child; + const parent = suiteBuilder[fnName]('parent', function() { + child = suiteBuilder[fnName]('child', function() { + suiteBuilder.it('a spec'); + }); + }); + + expect(suiteBuilder.topSuite.children).toEqual([sameInstanceAs(parent)]); + expect(parent.children).toEqual([sameInstanceAs(child)]); + expect(child.parentSuite).toBe(parent); + expect(parent.parentSuite).toBe(suiteBuilder.topSuite); + }); + + it('gives each suite a unique ID', function() { + const env = { configuration: () => ({}) }; + const suiteBuilder = new jasmineUnderTest.SuiteBuilder({ env }); + + let child; + const parent = suiteBuilder[fnName]('parent', function() { + child = suiteBuilder[fnName]('child', function() { + suiteBuilder.it('a spec'); + }); + }); + + const ids = [suiteBuilder.topSuite.id, parent.id, child.id]; + + for (const id of ids) { + expect(id).toMatch(/^suite[0-9]$/); + } + + expect(new Set(ids).size).toEqual(3); + }); + } + + function definesSpecs(fnName) { + it('adds the spec to its suite', function() { + const env = { configuration: () => ({}) }; + const suiteBuilder = new jasmineUnderTest.SuiteBuilder({ env }); + + let spec; + const suite = suiteBuilder.describe('a suite', function() { + spec = suiteBuilder[fnName]('a spec', function() {}); + }); + + expect(suite.children).toEqual([sameInstanceAs(spec)]); + }); + + it('gives each spec a unique ID', function() { + const env = { configuration: () => ({}) }; + const suiteBuilder = new jasmineUnderTest.SuiteBuilder({ env }); + + const spec1 = suiteBuilder[fnName]('a spec', function() {}); + const spec2 = suiteBuilder[fnName]('another spec', function() {}); + + expect(spec1.id).toMatch(/^spec[0-9]+$/); + expect(spec2.id).toMatch(/^spec[0-9]+$/); + expect(spec1.id).not.toEqual(spec2.id); + }); + } + + function sameInstanceAs(expected) { + return { + asymmetricMatch: function(actual) { + return actual === expected; + }, + jasmineToString: function(pp) { + return ''; + } + }; + } +}); diff --git a/src/core/Env.js b/src/core/Env.js index fc8ccf0c..852e0a8f 100644 --- a/src/core/Env.js +++ b/src/core/Env.js @@ -13,8 +13,6 @@ getJasmineRequireObj().Env = function(j$) { const self = this; const global = options.global || j$.getGlobal(); - let totalSpecsDefined = 0; - const realSetTimeout = global.setTimeout; const realClearTimeout = global.clearTimeout; const clearStack = j$.getClearStack(global); @@ -31,14 +29,11 @@ getJasmineRequireObj().Env = function(j$) { return r ? r.id : null; }); - let topSuite; let currentSpec = null; const currentlyExecutingSuites = []; - const focusedRunables = []; - let currentDeclarationSuite = null; let hasFailures = false; - let deprecator; let reporter; + let topSuite; /** * This represents the available options to configure Jasmine. @@ -258,18 +253,6 @@ getJasmineRequireObj().Env = function(j$) { j$.Expectation.addCoreMatchers(j$.matchers); j$.Expectation.addAsyncCoreMatchers(j$.asyncMatchers); - let nextSpecId = 0; - - function getNextSpecId() { - return 'spec' + nextSpecId++; - } - - let nextSuiteId = 0; - - function getNextSuiteId() { - return 'suite' + nextSuiteId++; - } - const expectationFactory = function(actual, spec) { return j$.Expectation.factory({ matchersUtil: runableResources.makeMatchersUtil(), @@ -363,43 +346,6 @@ getJasmineRequireObj().Env = function(j$) { return spec.addExpectationResult(passed, result); } }; - const suiteAsyncExpectationFactory = function(actual, suite) { - return asyncExpectationFactory(actual, suite, 'Suite'); - }; - - const specAsyncExpectationFactory = function(actual, suite) { - return asyncExpectationFactory(actual, suite, 'Spec'); - }; - - function beforeAndAfterFns(targetSuite) { - return function() { - let befores = [], - afters = [], - suite = targetSuite; - - while (suite) { - befores = befores.concat(suite.beforeFns); - afters = afters.concat(suite.afterFns); - - suite = suite.parentSuite; - } - - return { - befores: befores.reverse(), - afters: afters - }; - }; - } - - function getSpecName(spec, suite) { - const fullName = [spec.description], - suiteFullName = suite.getFullName(); - - if (suiteFullName !== '') { - fullName.unshift(suiteFullName); - } - return fullName.join(' '); - } /** * Causes a deprecation warning to be logged to the console and reported to @@ -461,16 +407,17 @@ getJasmineRequireObj().Env = function(j$) { new j$.QueueRunner(options).execute(args); } - topSuite = new j$.Suite({ - id: getNextSuiteId(), - description: 'Jasmine__TopLevel__Suite', - expectationFactory: expectationFactory, - asyncExpectationFactory: suiteAsyncExpectationFactory, - autoCleanClosures: config.autoCleanClosures, - onLateError: recordLateError + const suiteBuilder = new j$.SuiteBuilder({ + env: this, + expectationFactory, + asyncExpectationFactory, + onLateError: recordLateError, + specResultCallback, + specStarted, + queueRunnerFactory }); - deprecator = new j$.Deprecator(topSuite); - currentDeclarationSuite = topSuite; + topSuite = suiteBuilder.topSuite; + const deprecator = new j$.Deprecator(topSuite); /** * Provides the root suite, through which all suites and specs can be @@ -600,8 +547,8 @@ getJasmineRequireObj().Env = function(j$) { installGlobalErrors(); if (!runablesToRun) { - if (focusedRunables.length) { - runablesToRun = focusedRunables; + if (suiteBuilder.focusedRunables.length) { + runablesToRun = suiteBuilder.focusedRunables; } else { runablesToRun = [topSuite.id]; } @@ -681,7 +628,7 @@ getJasmineRequireObj().Env = function(j$) { */ reporter.jasmineStarted( { - totalSpecsDefined: totalSpecsDefined, + totalSpecsDefined: suiteBuilder.totalSpecsDefined, order: order }, function() { @@ -702,10 +649,10 @@ getJasmineRequireObj().Env = function(j$) { topSuite.result.failedExpectations.length > 0 ) { overallStatus = 'failed'; - } else if (focusedRunables.length > 0) { + } else if (suiteBuilder.focusedRunables.length > 0) { overallStatus = 'incomplete'; incompleteReason = 'fit() or fdescribe() was found'; - } else if (totalSpecsDefined === 0) { + } else if (suiteBuilder.totalSpecsDefined === 0) { overallStatus = 'incomplete'; incompleteReason = 'No specs found'; } else { @@ -863,22 +810,6 @@ getJasmineRequireObj().Env = function(j$) { ); }; - function ensureIsFunction(fn, caller) { - if (!j$.isFunction_(fn)) { - throw new Error( - caller + ' expects a function argument; received ' + j$.getType_(fn) - ); - } - } - - function ensureIsFunctionOrAsync(fn, caller) { - if (!j$.isFunction_(fn) && !j$.isAsyncFunction_(fn)) { - throw new Error( - caller + ' expects a function argument; received ' + j$.getType_(fn) - ); - } - } - function ensureIsNotNested(method) { const runable = currentRunable(); if (runable !== null && runable !== undefined) { @@ -888,150 +819,38 @@ getJasmineRequireObj().Env = function(j$) { } } - function suiteFactory(description) { - return new j$.Suite({ - id: getNextSuiteId(), - description: description, - parentSuite: currentDeclarationSuite, - timer: new j$.Timer(), - expectationFactory: expectationFactory, - asyncExpectationFactory: suiteAsyncExpectationFactory, - throwOnExpectationFailure: config.stopSpecOnExpectationFailure, - autoCleanClosures: config.autoCleanClosures, - onLateError: recordLateError - }); - } - - this.describe = function(description, specDefinitions) { + this.describe = function(description, definitionFn) { ensureIsNotNested('describe'); - ensureIsFunction(specDefinitions, 'describe'); - const suite = suiteFactory(description); - if (specDefinitions.length > 0) { - throw new Error('describe does not expect any arguments'); - } - if (currentDeclarationSuite.markedExcluding) { - suite.exclude(); - } - addSpecsToSuite(suite, specDefinitions); - if (suite.parentSuite && !suite.children.length) { - throw new Error( - 'describe with no children (describe() or it()): ' + - suite.getFullName() - ); - } - return suite.metadata; + return suiteBuilder.describe(description, definitionFn).metadata; }; - this.xdescribe = function(description, specDefinitions) { + this.xdescribe = function(description, definitionFn) { ensureIsNotNested('xdescribe'); - ensureIsFunction(specDefinitions, 'xdescribe'); - const suite = suiteFactory(description); - suite.exclude(); - addSpecsToSuite(suite, specDefinitions); - return suite.metadata; + return suiteBuilder.xdescribe(description, definitionFn).metadata; }; - this.fdescribe = function(description, specDefinitions) { + this.fdescribe = function(description, definitionFn) { ensureIsNotNested('fdescribe'); - ensureIsFunction(specDefinitions, 'fdescribe'); - const suite = suiteFactory(description); - suite.isFocused = true; - - focusedRunables.push(suite.id); - unfocusAncestor(); - addSpecsToSuite(suite, specDefinitions); - - return suite.metadata; + return suiteBuilder.fdescribe(description, definitionFn).metadata; }; - function addSpecsToSuite(suite, specDefinitions) { - const parentSuite = currentDeclarationSuite; - parentSuite.addChild(suite); - currentDeclarationSuite = suite; + function specResultCallback(spec, result, next) { + runableResources.clearForRunable(spec.id); + currentSpec = null; - let declarationError = null; - try { - specDefinitions(); - } catch (e) { - declarationError = e; + if (result.status === 'failed') { + hasFailures = true; } - if (declarationError) { - suite.handleException(declarationError); - } - - currentDeclarationSuite = parentSuite; + reportSpecDone(spec, result, next); } - function findFocusedAncestor(suite) { - while (suite) { - if (suite.isFocused) { - return suite.id; - } - suite = suite.parentSuite; - } - - return null; + function specStarted(spec, suite, next) { + currentSpec = spec; + runableResources.initForRunable(spec.id, suite.id); + reporter.specStarted(spec.result, next); } - function unfocusAncestor() { - const focusedAncestor = findFocusedAncestor(currentDeclarationSuite); - if (focusedAncestor) { - for (let i = 0; i < focusedRunables.length; i++) { - if (focusedRunables[i] === focusedAncestor) { - focusedRunables.splice(i, 1); - break; - } - } - } - } - - const specFactory = function(description, fn, suite, timeout) { - totalSpecsDefined++; - const spec = new j$.Spec({ - id: getNextSpecId(), - beforeAndAfterFns: beforeAndAfterFns(suite), - expectationFactory: expectationFactory, - asyncExpectationFactory: specAsyncExpectationFactory, - onLateError: recordLateError, - resultCallback: specResultCallback, - getSpecName: function(spec) { - return getSpecName(spec, suite); - }, - onStart: specStarted, - description: description, - queueRunnerFactory: queueRunnerFactory, - userContext: function() { - return suite.clonedSharedUserContext(); - }, - queueableFn: { - fn: fn, - timeout: timeout || 0 - }, - throwOnExpectationFailure: config.stopSpecOnExpectationFailure, - autoCleanClosures: config.autoCleanClosures, - timer: new j$.Timer() - }); - return spec; - - function specResultCallback(result, next) { - runableResources.clearForRunable(spec.id); - currentSpec = null; - - if (result.status === 'failed') { - hasFailures = true; - } - - reportSpecDone(spec, result, next); - } - - function specStarted(spec, next) { - currentSpec = spec; - runableResources.initForRunable(spec.id, suite.id); - reporter.specStarted(spec.result, next); - } - }; - function reportSpecDone(spec, result, next) { spec.reportedDone = true; reporter.specDone(result, next); @@ -1042,66 +861,19 @@ getJasmineRequireObj().Env = function(j$) { reporter.suiteDone(result, next); } - this.it_ = function(description, fn, timeout) { - ensureIsNotNested('it'); - // it() sometimes doesn't have a fn argument, so only check the type if - // it's given. - if (arguments.length > 1 && typeof fn !== 'undefined') { - ensureIsFunctionOrAsync(fn, 'it'); - } - - if (timeout) { - j$.util.validateTimeout(timeout); - } - - const spec = specFactory( - description, - fn, - currentDeclarationSuite, - timeout - ); - if (currentDeclarationSuite.markedExcluding) { - spec.exclude(); - } - currentDeclarationSuite.addChild(spec); - - return spec; - }; - this.it = function(description, fn, timeout) { - const spec = this.it_(description, fn, timeout); - return spec.metadata; + ensureIsNotNested('it'); + return suiteBuilder.it(description, fn, timeout).metadata; }; this.xit = function(description, fn, timeout) { ensureIsNotNested('xit'); - // xit(), like it(), doesn't always have a fn argument, so only check the - // type when needed. - if (arguments.length > 1 && typeof fn !== 'undefined') { - ensureIsFunctionOrAsync(fn, 'xit'); - } - const spec = this.it_.apply(this, arguments); - spec.exclude('Temporarily disabled with xit'); - return spec.metadata; + return suiteBuilder.xit(description, fn, timeout).metadata; }; this.fit = function(description, fn, timeout) { ensureIsNotNested('fit'); - ensureIsFunctionOrAsync(fn, 'fit'); - - if (timeout) { - j$.util.validateTimeout(timeout); - } - const spec = specFactory( - description, - fn, - currentDeclarationSuite, - timeout - ); - currentDeclarationSuite.addChild(spec); - focusedRunables.push(spec.id); - unfocusAncestor(); - return spec.metadata; + return suiteBuilder.fit(description, fn, timeout).metadata; }; /** @@ -1170,59 +942,22 @@ getJasmineRequireObj().Env = function(j$) { this.beforeEach = function(beforeEachFunction, timeout) { ensureIsNotNested('beforeEach'); - ensureIsFunctionOrAsync(beforeEachFunction, 'beforeEach'); - - if (timeout) { - j$.util.validateTimeout(timeout); - } - - currentDeclarationSuite.beforeEach({ - fn: beforeEachFunction, - timeout: timeout || 0 - }); + suiteBuilder.beforeEach(beforeEachFunction, timeout); }; this.beforeAll = function(beforeAllFunction, timeout) { ensureIsNotNested('beforeAll'); - ensureIsFunctionOrAsync(beforeAllFunction, 'beforeAll'); - - if (timeout) { - j$.util.validateTimeout(timeout); - } - - currentDeclarationSuite.beforeAll({ - fn: beforeAllFunction, - timeout: timeout || 0 - }); + suiteBuilder.beforeAll(beforeAllFunction, timeout); }; this.afterEach = function(afterEachFunction, timeout) { ensureIsNotNested('afterEach'); - ensureIsFunctionOrAsync(afterEachFunction, 'afterEach'); - - if (timeout) { - j$.util.validateTimeout(timeout); - } - - afterEachFunction.isCleanup = true; - currentDeclarationSuite.afterEach({ - fn: afterEachFunction, - timeout: timeout || 0 - }); + suiteBuilder.afterEach(afterEachFunction, timeout); }; this.afterAll = function(afterAllFunction, timeout) { ensureIsNotNested('afterAll'); - ensureIsFunctionOrAsync(afterAllFunction, 'afterAll'); - - if (timeout) { - j$.util.validateTimeout(timeout); - } - - currentDeclarationSuite.afterAll({ - fn: afterAllFunction, - timeout: timeout || 0 - }); + suiteBuilder.afterAll(afterAllFunction, timeout); }; this.pending = function(message) { diff --git a/src/core/SuiteBuilder.js b/src/core/SuiteBuilder.js new file mode 100644 index 00000000..f5017529 --- /dev/null +++ b/src/core/SuiteBuilder.js @@ -0,0 +1,304 @@ +getJasmineRequireObj().SuiteBuilder = function(j$) { + class SuiteBuilder { + constructor(options) { + this.env_ = options.env; + this.expectationFactory_ = options.expectationFactory; + this.suiteAsyncExpectationFactory_ = function(actual, suite) { + return options.asyncExpectationFactory(actual, suite, 'Suite'); + }; + this.specAsyncExpectationFactory_ = function(actual, suite) { + return options.asyncExpectationFactory(actual, suite, 'Spec'); + }; + this.onLateError_ = options.onLateError; + this.specResultCallback_ = options.specResultCallback; + this.specStarted_ = options.specStarted; + this.queueRunnerFactory_ = options.queueRunnerFactory; + + this.nextSuiteId_ = 0; + this.nextSpecId_ = 0; + + this.topSuite = this.suiteFactory_('Jasmine__TopLevel__Suite'); + this.currentDeclarationSuite_ = this.topSuite; + this.totalSpecsDefined = 0; + this.focusedRunables = []; + } + + describe(description, definitionFn) { + ensureIsFunction(definitionFn, 'describe'); + const suite = this.suiteFactory_(description); + if (definitionFn.length > 0) { + throw new Error('describe does not expect any arguments'); + } + if (this.currentDeclarationSuite_.markedExcluding) { + suite.exclude(); + } + this.addSpecsToSuite_(suite, definitionFn); + if (suite.parentSuite && !suite.children.length) { + throw new Error( + `describe with no children (describe() or it()): ${suite.getFullName()}` + ); + } + return suite; + } + + fdescribe(description, definitionFn) { + ensureIsFunction(definitionFn, 'fdescribe'); + const suite = this.suiteFactory_(description); + suite.isFocused = true; + + this.focusedRunables.push(suite.id); + this.unfocusAncestor_(); + this.addSpecsToSuite_(suite, definitionFn); + + return suite; + } + + xdescribe(description, definitionFn) { + ensureIsFunction(definitionFn, 'xdescribe'); + const suite = this.suiteFactory_(description); + suite.exclude(); + this.addSpecsToSuite_(suite, definitionFn); + + return suite; + } + + it(description, fn, timeout) { + // it() sometimes doesn't have a fn argument, so only check the type if + // it's given. + if (arguments.length > 1 && typeof fn !== 'undefined') { + ensureIsFunctionOrAsync(fn, 'it'); + } + + return this.it_(description, fn, timeout); + } + + xit(description, fn, timeout) { + // xit(), like it(), doesn't always have a fn argument, so only check the + // type when needed. + if (arguments.length > 1 && typeof fn !== 'undefined') { + ensureIsFunctionOrAsync(fn, 'xit'); + } + const spec = this.it_(description, fn, timeout); + spec.exclude('Temporarily disabled with xit'); + return spec; + } + + fit(description, fn, timeout) { + // Unlike it and xit, the function is required because it doesn't make + // sense to focus on nothing. + ensureIsFunctionOrAsync(fn, 'fit'); + + if (timeout) { + j$.util.validateTimeout(timeout); + } + const spec = this.specFactory_(description, fn, timeout); + this.currentDeclarationSuite_.addChild(spec); + this.focusedRunables.push(spec.id); + this.unfocusAncestor_(); + return spec; + } + + beforeEach(beforeEachFunction, timeout) { + ensureIsFunctionOrAsync(beforeEachFunction, 'beforeEach'); + + if (timeout) { + j$.util.validateTimeout(timeout); + } + + this.currentDeclarationSuite_.beforeEach({ + fn: beforeEachFunction, + timeout: timeout || 0 + }); + } + + beforeAll(beforeAllFunction, timeout) { + ensureIsFunctionOrAsync(beforeAllFunction, 'beforeAll'); + + if (timeout) { + j$.util.validateTimeout(timeout); + } + + this.currentDeclarationSuite_.beforeAll({ + fn: beforeAllFunction, + timeout: timeout || 0 + }); + } + + afterEach(afterEachFunction, timeout) { + ensureIsFunctionOrAsync(afterEachFunction, 'afterEach'); + + if (timeout) { + j$.util.validateTimeout(timeout); + } + + afterEachFunction.isCleanup = true; + this.currentDeclarationSuite_.afterEach({ + fn: afterEachFunction, + timeout: timeout || 0 + }); + } + + afterAll(afterAllFunction, timeout) { + ensureIsFunctionOrAsync(afterAllFunction, 'afterAll'); + + if (timeout) { + j$.util.validateTimeout(timeout); + } + + this.currentDeclarationSuite_.afterAll({ + fn: afterAllFunction, + timeout: timeout || 0 + }); + } + + it_(description, fn, timeout) { + if (timeout) { + j$.util.validateTimeout(timeout); + } + + const spec = this.specFactory_(description, fn, timeout); + if (this.currentDeclarationSuite_.markedExcluding) { + spec.exclude(); + } + this.currentDeclarationSuite_.addChild(spec); + + return spec; + } + + suiteFactory_(description) { + const config = this.env_.configuration(); + return new j$.Suite({ + id: 'suite' + this.nextSuiteId_++, + description, + parentSuite: this.currentDeclarationSuite_, + timer: new j$.Timer(), + expectationFactory: this.expectationFactory_, + asyncExpectationFactory: this.suiteAsyncExpectationFactory_, + throwOnExpectationFailure: config.stopSpecOnExpectationFailure, + autoCleanClosures: config.autoCleanClosures, + onLateError: this.onLateError_ + }); + } + + addSpecsToSuite_(suite, definitionFn) { + const parentSuite = this.currentDeclarationSuite_; + parentSuite.addChild(suite); + this.currentDeclarationSuite_ = suite; + + try { + definitionFn(); + } catch (e) { + suite.handleException(e); + } + + this.currentDeclarationSuite_ = parentSuite; + } + + specFactory_(description, fn, timeout) { + this.totalSpecsDefined++; + const config = this.env_.configuration(); + const suite = this.currentDeclarationSuite_; + const spec = new j$.Spec({ + id: 'spec' + this.nextSpecId_++, + beforeAndAfterFns: beforeAndAfterFns(suite), + expectationFactory: this.expectationFactory_, + asyncExpectationFactory: this.specAsyncExpectationFactory_, + onLateError: this.onLateError_, + resultCallback: (result, next) => { + this.specResultCallback_(spec, result, next); + }, + getSpecName: function(spec) { + return getSpecName(spec, suite); + }, + onStart: (spec, next) => this.specStarted_(spec, suite, next), + description: description, + queueRunnerFactory: this.queueRunnerFactory_, + userContext: function() { + return suite.clonedSharedUserContext(); + }, + queueableFn: { + fn: fn, + timeout: timeout || 0 + }, + throwOnExpectationFailure: config.stopSpecOnExpectationFailure, + autoCleanClosures: config.autoCleanClosures, + timer: new j$.Timer() + }); + return spec; + } + + unfocusAncestor_() { + const focusedAncestor = findFocusedAncestor( + this.currentDeclarationSuite_ + ); + + if (focusedAncestor) { + for (let i = 0; i < this.focusedRunables.length; i++) { + if (this.focusedRunables[i] === focusedAncestor) { + this.focusedRunables.splice(i, 1); + break; + } + } + } + } + } + + function findFocusedAncestor(suite) { + while (suite) { + if (suite.isFocused) { + return suite.id; + } + suite = suite.parentSuite; + } + + return null; + } + + function ensureIsFunction(fn, caller) { + if (!j$.isFunction_(fn)) { + throw new Error( + caller + ' expects a function argument; received ' + j$.getType_(fn) + ); + } + } + + function ensureIsFunctionOrAsync(fn, caller) { + if (!j$.isFunction_(fn) && !j$.isAsyncFunction_(fn)) { + throw new Error( + caller + ' expects a function argument; received ' + j$.getType_(fn) + ); + } + } + + function beforeAndAfterFns(targetSuite) { + return function() { + let befores = [], + afters = [], + suite = targetSuite; + + while (suite) { + befores = befores.concat(suite.beforeFns); + afters = afters.concat(suite.afterFns); + + suite = suite.parentSuite; + } + + return { + befores: befores.reverse(), + afters: afters + }; + }; + } + + function getSpecName(spec, suite) { + const fullName = [spec.description], + suiteFullName = suite.getFullName(); + + if (suiteFullName !== '') { + fullName.unshift(suiteFullName); + } + return fullName.join(' '); + } + + return SuiteBuilder; +}; diff --git a/src/core/requireCore.js b/src/core/requireCore.js index faa6b86e..a628e6ea 100644 --- a/src/core/requireCore.js +++ b/src/core/requireCore.js @@ -78,6 +78,7 @@ var getJasmineRequireObj = (function(jasmineGlobal) { j$.StringContaining = jRequire.StringContaining(j$); j$.UserContext = jRequire.UserContext(j$); j$.Suite = jRequire.Suite(j$); + j$.SuiteBuilder = jRequire.SuiteBuilder(j$); j$.Timer = jRequire.Timer(); j$.TreeProcessor = jRequire.TreeProcessor(); j$.version = jRequire.version();