diff --git a/lib/jasmine-core/jasmine.js b/lib/jasmine-core/jasmine.js index 70298560..10f28f93 100644 --- a/lib/jasmine-core/jasmine.js +++ b/lib/jasmine-core/jasmine.js @@ -108,6 +108,7 @@ var getJasmineRequireObj = (function(jasmineGlobal) { j$.SuiteBuilder = jRequire.SuiteBuilder(j$); j$.Timer = jRequire.Timer(); j$.TreeProcessor = jRequire.TreeProcessor(j$); + j$.TreeRunner = jRequire.TreeRunner(j$); j$.version = jRequire.version(); j$.Order = jRequire.Order(); j$.DiffBuilder = jRequire.DiffBuilder(j$); @@ -1961,7 +1962,7 @@ getJasmineRequireObj().Env = function(j$) { function specResultCallback(spec, result, next) { runableResources.clearForRunable(spec.id); - runner.currentSpec = null; + runner.setCurrentSpec(null); if (result.status === 'failed') { runner.hasFailures = true; @@ -1971,7 +1972,7 @@ getJasmineRequireObj().Env = function(j$) { } function specStarted(spec, suite, next) { - runner.currentSpec = spec; + runner.setCurrentSpec(spec); runableResources.initForRunable(spec.id, suite.id); reportDispatcher.specStarted(spec.result).then(next); } @@ -9413,7 +9414,7 @@ getJasmineRequireObj().Runner = function(j$) { #getConfig; #reportSpecDone; #executedBefore; - #currentlyExecutingSuites; + #currentRunableTracker; constructor(options) { this.#topSuite = options.topSuite; @@ -9428,19 +9429,23 @@ getJasmineRequireObj().Runner = function(j$) { this.#reportSpecDone = options.reportSpecDone; this.hasFailures = false; this.#executedBefore = false; + this.#currentRunableTracker = new CurrentRunableTracker(); + } - this.#currentlyExecutingSuites = []; - this.currentSpec = null; + currentSpec() { + return this.#currentRunableTracker.currentSpec(); + } + + setCurrentSpec(spec) { + this.#currentRunableTracker.setCurrentSpec(spec); } currentRunable() { - return this.currentSpec || this.currentSuite(); + return this.#currentRunableTracker.currentRunable(); } currentSuite() { - return this.#currentlyExecutingSuites[ - this.#currentlyExecutingSuites.length - 1 - ]; + return this.#currentRunableTracker.currentSuite(); } parallelReset() { @@ -9508,15 +9513,28 @@ getJasmineRequireObj().Runner = function(j$) { parallel: false }); - this.#currentlyExecutingSuites.push(this.#topSuite); - await this.#executeTopSuite(); + this.#currentRunableTracker.pushSuite(this.#topSuite); + const treeRunner = new j$.TreeRunner({ + executionTree: this.#executionTree, + globalErrors: this.#globalErrors, + runableResources: this.#runableResources, + reportDispatcher: this.#reportDispatcher, + runQueue: this.#runQueue, + getConfig: this.#getConfig, + reportChildrenOfBeforeAllFailure: this.#reportChildrenOfBeforeAllFailure.bind( + this + ), + currentRunableTracker: this.#currentRunableTracker + }); + await treeRunner.execute(); + this.hasFailures = this.hasFailures || treeRunner.hasFailures; if (this.#topSuite.hadBeforeAllFailure) { await this.#reportChildrenOfBeforeAllFailure(this.#topSuite); } this.#runableResources.clearForRunable(this.#topSuite.id); - this.#currentlyExecutingSuites.pop(); + this.#currentRunableTracker.popSuite(); let overallStatus, incompleteReason, incompleteCode; if ( @@ -9563,152 +9581,6 @@ getJasmineRequireObj().Runner = function(j$) { return jasmineDoneInfo; } - async #executeTopSuite() { - const wrappedChildren = this.#wrapNodes( - this.#executionTree.childrenOfTopSuite() - ); - const queueableFns = this.#addBeforeAndAfterAlls( - this.#topSuite, - wrappedChildren - ); - - await new Promise(resolve => { - this.#runQueueWithSkipPolicy({ - queueableFns, - userContext: this.#topSuite.sharedUserContext(), - onException: function() { - this.#topSuite.handleException.apply(this.#topSuite, arguments); - }.bind(this), - onComplete: resolve, - onMultipleDone: this.#topSuite.onMultipleDone - ? this.#topSuite.onMultipleDone.bind(this.#topSuite) - : null - }); - }); - } - - #executeSuiteSegment(suite, segmentNumber, done) { - const wrappedChildren = this.#wrapNodes( - this.#executionTree.childrenOfSuiteSegment(suite, segmentNumber) - ); - const onStart = { - fn: next => { - this.#suiteSegmentStart(suite, next); - } - }; - const queueableFns = [ - onStart, - ...this.#addBeforeAndAfterAlls(suite, wrappedChildren) - ]; - - this.#runQueueWithSkipPolicy({ - // TODO: if onComplete always takes 0-1 arguments (and it probably does) - // then it can be switched to an arrow fn with a named arg. - onComplete: function() { - const args = Array.prototype.slice.call(arguments, [0]); - this.#suiteSegmentComplete(suite, suite.getResult(), () => { - done.apply(undefined, args); - }); - }.bind(this), - queueableFns, - userContext: suite.sharedUserContext(), - onException: function() { - suite.handleException.apply(suite, arguments); - }, - onMultipleDone: suite.onMultipleDone - ? suite.onMultipleDone.bind(suite) - : null - }); - } - - #executeSpec(spec, done) { - const config = this.#getConfig(); - spec.execute( - this.#runQueueWithSkipPolicy.bind(this), - this.#globalErrors, - done, - this.#executionTree.isExcluded(spec), - config.failSpecWithNoExpectations, - config.detectLateRejectionHandling - ); - } - - #wrapNodes(nodes) { - return nodes.map(node => { - return { - fn: done => { - if (node.suite) { - this.#executeSuiteSegment(node.suite, node.segmentNumber, done); - } else { - this.#executeSpec(node.spec, done); - } - } - }; - }); - } - - #addBeforeAndAfterAlls(suite, wrappedChildren) { - if (this.#executionTree.isExcluded(suite)) { - return wrappedChildren; - } - - return suite.beforeAllFns - .concat(wrappedChildren) - .concat(suite.afterAllFns); - } - - #suiteSegmentStart(suite, next) { - this.#currentlyExecutingSuites.push(suite); - this.#runableResources.initForRunable(suite.id, suite.parentSuite.id); - this.#reportDispatcher.suiteStarted(suite.result).then(next); - suite.startTimer(); - } - - #suiteSegmentComplete(suite, result, next) { - suite.cleanupBeforeAfter(); - - if (suite !== this.currentSuite()) { - throw new Error('Tried to complete the wrong suite'); - } - - this.#runableResources.clearForRunable(suite.id); - this.#currentlyExecutingSuites.pop(); - - if (result.status === 'failed') { - this.hasFailures = true; - } - suite.endTimer(); - - if (suite.hadBeforeAllFailure) { - this.#reportChildrenOfBeforeAllFailure(suite).then(() => { - this.#reportSuiteDone(suite, result, next); - }); - } else { - this.#reportSuiteDone(suite, result, next); - } - } - - #runQueueWithSkipPolicy(options) { - if (options.isLeaf) { - // A spec - options.SkipPolicy = j$.CompleteOnFirstErrorSkipPolicy; - } else { - // A suite - if (this.#getConfig().stopOnSpecFailure) { - options.SkipPolicy = j$.CompleteOnFirstErrorSkipPolicy; - } else { - options.SkipPolicy = j$.SkipAfterBeforeAllErrorPolicy; - } - } - - return this.#runQueue(options); - } - - #reportSuiteDone(suite, result, next) { - suite.reportedDone = true; - this.#reportDispatcher.suiteDone(result).then(next); - } - async #reportChildrenOfBeforeAllFailure(suite) { for (const child of suite.children) { if (child instanceof j$.Suite) { @@ -9745,6 +9617,41 @@ getJasmineRequireObj().Runner = function(j$) { } } + class CurrentRunableTracker { + #currentSpec; + #currentlyExecutingSuites; + + constructor() { + this.#currentlyExecutingSuites = []; + } + + currentRunable() { + return this.currentSpec() || this.currentSuite(); + } + + currentSpec() { + return this.#currentSpec; + } + + setCurrentSpec(spec) { + this.#currentSpec = spec; + } + + currentSuite() { + return this.#currentlyExecutingSuites[ + this.#currentlyExecutingSuites.length - 1 + ]; + } + + pushSuite(suite) { + this.#currentlyExecutingSuites.push(suite); + } + + popSuite() { + this.#currentlyExecutingSuites.pop(); + } + } + return Runner; }; @@ -11500,16 +11407,18 @@ getJasmineRequireObj().TreeProcessor = function(j$) { } class ExecutionTree { - #topSuite; #stats; constructor(topSuite, stats) { - this.#topSuite = topSuite; + Object.defineProperty(this, 'topSuite', { + writable: false, + value: topSuite + }); this.#stats = stats; } childrenOfTopSuite() { - return this.childrenOfSuiteSegment(this.#topSuite, 0); + return this.childrenOfSuiteSegment(this.topSuite, 0); } childrenOfSuiteSegment(suite, segmentNumber) { @@ -11612,6 +11521,181 @@ getJasmineRequireObj().TreeProcessor = function(j$) { return TreeProcessor; }; +getJasmineRequireObj().TreeRunner = function(j$) { + class TreeRunner { + #executionTree; + #globalErrors; + #runableResources; + #reportDispatcher; + #runQueue; + #getConfig; + #reportChildrenOfBeforeAllFailure; + #currentRunableTracker; + + constructor(attrs) { + this.#executionTree = attrs.executionTree; + this.#globalErrors = attrs.globalErrors; + this.#runableResources = attrs.runableResources; + this.#reportDispatcher = attrs.reportDispatcher; + this.#runQueue = attrs.runQueue; + this.#getConfig = attrs.getConfig; + this.#reportChildrenOfBeforeAllFailure = + attrs.reportChildrenOfBeforeAllFailure; + this.#currentRunableTracker = attrs.currentRunableTracker; + } + + async execute() { + this.hasFailures = false; + const topSuite = this.#executionTree.topSuite; + const wrappedChildren = this.#wrapNodes( + this.#executionTree.childrenOfTopSuite() + ); + const queueableFns = this.#addBeforeAndAfterAlls( + topSuite, + wrappedChildren + ); + + await new Promise(resolve => { + this.#runQueueWithSkipPolicy({ + queueableFns, + userContext: this.#executionTree.topSuite.sharedUserContext(), + onException: function() { + topSuite.handleException.apply(topSuite, arguments); + }.bind(this), + onComplete: resolve, + onMultipleDone: topSuite.onMultipleDone + ? topSuite.onMultipleDone.bind(topSuite) + : null + }); + }); + } + + #wrapNodes(nodes) { + return nodes.map(node => { + return { + fn: done => { + if (node.suite) { + this.#executeSuiteSegment(node.suite, node.segmentNumber, done); + } else { + this.#executeSpec(node.spec, done); + } + } + }; + }); + } + + #executeSpec(spec, done) { + const config = this.#getConfig(); + spec.execute( + this.#runQueueWithSkipPolicy.bind(this), + this.#globalErrors, + done, + this.#executionTree.isExcluded(spec), + config.failSpecWithNoExpectations, + config.detectLateRejectionHandling + ); + } + + #executeSuiteSegment(suite, segmentNumber, done) { + const wrappedChildren = this.#wrapNodes( + this.#executionTree.childrenOfSuiteSegment(suite, segmentNumber) + ); + const onStart = { + fn: next => { + this.#suiteSegmentStart(suite, next); + } + }; + const queueableFns = [ + onStart, + ...this.#addBeforeAndAfterAlls(suite, wrappedChildren) + ]; + + this.#runQueueWithSkipPolicy({ + // TODO: if onComplete always takes 0-1 arguments (and it probably does) + // then it can be switched to an arrow fn with a named arg. + onComplete: function() { + const args = Array.prototype.slice.call(arguments, [0]); + this.#suiteSegmentComplete(suite, suite.getResult(), () => { + done.apply(undefined, args); + }); + }.bind(this), + queueableFns, + userContext: suite.sharedUserContext(), + onException: function() { + suite.handleException.apply(suite, arguments); + }, + onMultipleDone: suite.onMultipleDone + ? suite.onMultipleDone.bind(suite) + : null + }); + } + + #suiteSegmentStart(suite, next) { + this.#currentRunableTracker.pushSuite(suite); + this.#runableResources.initForRunable(suite.id, suite.parentSuite.id); + this.#reportDispatcher.suiteStarted(suite.result).then(next); + suite.startTimer(); + } + + #suiteSegmentComplete(suite, result, next) { + suite.cleanupBeforeAfter(); + + if (suite !== this.#currentRunableTracker.currentSuite()) { + throw new Error('Tried to complete the wrong suite'); + } + + this.#runableResources.clearForRunable(suite.id); + this.#currentRunableTracker.popSuite(); + + if (result.status === 'failed') { + this.hasFailures = true; + } + suite.endTimer(); + + if (suite.hadBeforeAllFailure) { + this.#reportChildrenOfBeforeAllFailure(suite).then(() => { + this.#reportSuiteDone(suite, result, next); + }); + } else { + this.#reportSuiteDone(suite, result, next); + } + } + + #reportSuiteDone(suite, result, next) { + suite.reportedDone = true; + this.#reportDispatcher.suiteDone(result).then(next); + } + + #addBeforeAndAfterAlls(suite, wrappedChildren) { + if (this.#executionTree.isExcluded(suite)) { + return wrappedChildren; + } + + return suite.beforeAllFns + .concat(wrappedChildren) + .concat(suite.afterAllFns); + } + + #runQueueWithSkipPolicy(options) { + if (options.isLeaf) { + // A spec + options.SkipPolicy = j$.CompleteOnFirstErrorSkipPolicy; + } else { + // A suite + if (this.#getConfig().stopOnSpecFailure) { + options.SkipPolicy = j$.CompleteOnFirstErrorSkipPolicy; + } else { + options.SkipPolicy = j$.SkipAfterBeforeAllErrorPolicy; + } + } + + return this.#runQueue(options); + } + } + + return TreeRunner; +}; + getJasmineRequireObj().UserContext = function(j$) { function UserContext() {} diff --git a/src/core/Env.js b/src/core/Env.js index 78ac15a6..e4ff5d7c 100644 --- a/src/core/Env.js +++ b/src/core/Env.js @@ -765,7 +765,7 @@ getJasmineRequireObj().Env = function(j$) { function specResultCallback(spec, result, next) { runableResources.clearForRunable(spec.id); - runner.currentSpec = null; + runner.setCurrentSpec(null); if (result.status === 'failed') { runner.hasFailures = true; @@ -775,7 +775,7 @@ getJasmineRequireObj().Env = function(j$) { } function specStarted(spec, suite, next) { - runner.currentSpec = spec; + runner.setCurrentSpec(spec); runableResources.initForRunable(spec.id, suite.id); reportDispatcher.specStarted(spec.result).then(next); } diff --git a/src/core/Runner.js b/src/core/Runner.js index fdf70755..6feb1f71 100644 --- a/src/core/Runner.js +++ b/src/core/Runner.js @@ -12,7 +12,7 @@ getJasmineRequireObj().Runner = function(j$) { #getConfig; #reportSpecDone; #executedBefore; - #currentlyExecutingSuites; + #currentRunableTracker; constructor(options) { this.#topSuite = options.topSuite; @@ -27,19 +27,23 @@ getJasmineRequireObj().Runner = function(j$) { this.#reportSpecDone = options.reportSpecDone; this.hasFailures = false; this.#executedBefore = false; + this.#currentRunableTracker = new CurrentRunableTracker(); + } - this.#currentlyExecutingSuites = []; - this.currentSpec = null; + currentSpec() { + return this.#currentRunableTracker.currentSpec(); + } + + setCurrentSpec(spec) { + this.#currentRunableTracker.setCurrentSpec(spec); } currentRunable() { - return this.currentSpec || this.currentSuite(); + return this.#currentRunableTracker.currentRunable(); } currentSuite() { - return this.#currentlyExecutingSuites[ - this.#currentlyExecutingSuites.length - 1 - ]; + return this.#currentRunableTracker.currentSuite(); } parallelReset() { @@ -107,15 +111,28 @@ getJasmineRequireObj().Runner = function(j$) { parallel: false }); - this.#currentlyExecutingSuites.push(this.#topSuite); - await this.#executeTopSuite(); + this.#currentRunableTracker.pushSuite(this.#topSuite); + const treeRunner = new j$.TreeRunner({ + executionTree: this.#executionTree, + globalErrors: this.#globalErrors, + runableResources: this.#runableResources, + reportDispatcher: this.#reportDispatcher, + runQueue: this.#runQueue, + getConfig: this.#getConfig, + reportChildrenOfBeforeAllFailure: this.#reportChildrenOfBeforeAllFailure.bind( + this + ), + currentRunableTracker: this.#currentRunableTracker + }); + await treeRunner.execute(); + this.hasFailures = this.hasFailures || treeRunner.hasFailures; if (this.#topSuite.hadBeforeAllFailure) { await this.#reportChildrenOfBeforeAllFailure(this.#topSuite); } this.#runableResources.clearForRunable(this.#topSuite.id); - this.#currentlyExecutingSuites.pop(); + this.#currentRunableTracker.popSuite(); let overallStatus, incompleteReason, incompleteCode; if ( @@ -162,152 +179,6 @@ getJasmineRequireObj().Runner = function(j$) { return jasmineDoneInfo; } - async #executeTopSuite() { - const wrappedChildren = this.#wrapNodes( - this.#executionTree.childrenOfTopSuite() - ); - const queueableFns = this.#addBeforeAndAfterAlls( - this.#topSuite, - wrappedChildren - ); - - await new Promise(resolve => { - this.#runQueueWithSkipPolicy({ - queueableFns, - userContext: this.#topSuite.sharedUserContext(), - onException: function() { - this.#topSuite.handleException.apply(this.#topSuite, arguments); - }.bind(this), - onComplete: resolve, - onMultipleDone: this.#topSuite.onMultipleDone - ? this.#topSuite.onMultipleDone.bind(this.#topSuite) - : null - }); - }); - } - - #executeSuiteSegment(suite, segmentNumber, done) { - const wrappedChildren = this.#wrapNodes( - this.#executionTree.childrenOfSuiteSegment(suite, segmentNumber) - ); - const onStart = { - fn: next => { - this.#suiteSegmentStart(suite, next); - } - }; - const queueableFns = [ - onStart, - ...this.#addBeforeAndAfterAlls(suite, wrappedChildren) - ]; - - this.#runQueueWithSkipPolicy({ - // TODO: if onComplete always takes 0-1 arguments (and it probably does) - // then it can be switched to an arrow fn with a named arg. - onComplete: function() { - const args = Array.prototype.slice.call(arguments, [0]); - this.#suiteSegmentComplete(suite, suite.getResult(), () => { - done.apply(undefined, args); - }); - }.bind(this), - queueableFns, - userContext: suite.sharedUserContext(), - onException: function() { - suite.handleException.apply(suite, arguments); - }, - onMultipleDone: suite.onMultipleDone - ? suite.onMultipleDone.bind(suite) - : null - }); - } - - #executeSpec(spec, done) { - const config = this.#getConfig(); - spec.execute( - this.#runQueueWithSkipPolicy.bind(this), - this.#globalErrors, - done, - this.#executionTree.isExcluded(spec), - config.failSpecWithNoExpectations, - config.detectLateRejectionHandling - ); - } - - #wrapNodes(nodes) { - return nodes.map(node => { - return { - fn: done => { - if (node.suite) { - this.#executeSuiteSegment(node.suite, node.segmentNumber, done); - } else { - this.#executeSpec(node.spec, done); - } - } - }; - }); - } - - #addBeforeAndAfterAlls(suite, wrappedChildren) { - if (this.#executionTree.isExcluded(suite)) { - return wrappedChildren; - } - - return suite.beforeAllFns - .concat(wrappedChildren) - .concat(suite.afterAllFns); - } - - #suiteSegmentStart(suite, next) { - this.#currentlyExecutingSuites.push(suite); - this.#runableResources.initForRunable(suite.id, suite.parentSuite.id); - this.#reportDispatcher.suiteStarted(suite.result).then(next); - suite.startTimer(); - } - - #suiteSegmentComplete(suite, result, next) { - suite.cleanupBeforeAfter(); - - if (suite !== this.currentSuite()) { - throw new Error('Tried to complete the wrong suite'); - } - - this.#runableResources.clearForRunable(suite.id); - this.#currentlyExecutingSuites.pop(); - - if (result.status === 'failed') { - this.hasFailures = true; - } - suite.endTimer(); - - if (suite.hadBeforeAllFailure) { - this.#reportChildrenOfBeforeAllFailure(suite).then(() => { - this.#reportSuiteDone(suite, result, next); - }); - } else { - this.#reportSuiteDone(suite, result, next); - } - } - - #runQueueWithSkipPolicy(options) { - if (options.isLeaf) { - // A spec - options.SkipPolicy = j$.CompleteOnFirstErrorSkipPolicy; - } else { - // A suite - if (this.#getConfig().stopOnSpecFailure) { - options.SkipPolicy = j$.CompleteOnFirstErrorSkipPolicy; - } else { - options.SkipPolicy = j$.SkipAfterBeforeAllErrorPolicy; - } - } - - return this.#runQueue(options); - } - - #reportSuiteDone(suite, result, next) { - suite.reportedDone = true; - this.#reportDispatcher.suiteDone(result).then(next); - } - async #reportChildrenOfBeforeAllFailure(suite) { for (const child of suite.children) { if (child instanceof j$.Suite) { @@ -344,5 +215,40 @@ getJasmineRequireObj().Runner = function(j$) { } } + class CurrentRunableTracker { + #currentSpec; + #currentlyExecutingSuites; + + constructor() { + this.#currentlyExecutingSuites = []; + } + + currentRunable() { + return this.currentSpec() || this.currentSuite(); + } + + currentSpec() { + return this.#currentSpec; + } + + setCurrentSpec(spec) { + this.#currentSpec = spec; + } + + currentSuite() { + return this.#currentlyExecutingSuites[ + this.#currentlyExecutingSuites.length - 1 + ]; + } + + pushSuite(suite) { + this.#currentlyExecutingSuites.push(suite); + } + + popSuite() { + this.#currentlyExecutingSuites.pop(); + } + } + return Runner; }; diff --git a/src/core/TreeProcessor.js b/src/core/TreeProcessor.js index ff3dba4d..a26a80b6 100644 --- a/src/core/TreeProcessor.js +++ b/src/core/TreeProcessor.js @@ -101,16 +101,18 @@ getJasmineRequireObj().TreeProcessor = function(j$) { } class ExecutionTree { - #topSuite; #stats; constructor(topSuite, stats) { - this.#topSuite = topSuite; + Object.defineProperty(this, 'topSuite', { + writable: false, + value: topSuite + }); this.#stats = stats; } childrenOfTopSuite() { - return this.childrenOfSuiteSegment(this.#topSuite, 0); + return this.childrenOfSuiteSegment(this.topSuite, 0); } childrenOfSuiteSegment(suite, segmentNumber) { diff --git a/src/core/TreeRunner.js b/src/core/TreeRunner.js new file mode 100644 index 00000000..80b2859a --- /dev/null +++ b/src/core/TreeRunner.js @@ -0,0 +1,174 @@ +getJasmineRequireObj().TreeRunner = function(j$) { + class TreeRunner { + #executionTree; + #globalErrors; + #runableResources; + #reportDispatcher; + #runQueue; + #getConfig; + #reportChildrenOfBeforeAllFailure; + #currentRunableTracker; + + constructor(attrs) { + this.#executionTree = attrs.executionTree; + this.#globalErrors = attrs.globalErrors; + this.#runableResources = attrs.runableResources; + this.#reportDispatcher = attrs.reportDispatcher; + this.#runQueue = attrs.runQueue; + this.#getConfig = attrs.getConfig; + this.#reportChildrenOfBeforeAllFailure = + attrs.reportChildrenOfBeforeAllFailure; + this.#currentRunableTracker = attrs.currentRunableTracker; + } + + async execute() { + this.hasFailures = false; + const topSuite = this.#executionTree.topSuite; + const wrappedChildren = this.#wrapNodes( + this.#executionTree.childrenOfTopSuite() + ); + const queueableFns = this.#addBeforeAndAfterAlls( + topSuite, + wrappedChildren + ); + + await new Promise(resolve => { + this.#runQueueWithSkipPolicy({ + queueableFns, + userContext: this.#executionTree.topSuite.sharedUserContext(), + onException: function() { + topSuite.handleException.apply(topSuite, arguments); + }.bind(this), + onComplete: resolve, + onMultipleDone: topSuite.onMultipleDone + ? topSuite.onMultipleDone.bind(topSuite) + : null + }); + }); + } + + #wrapNodes(nodes) { + return nodes.map(node => { + return { + fn: done => { + if (node.suite) { + this.#executeSuiteSegment(node.suite, node.segmentNumber, done); + } else { + this.#executeSpec(node.spec, done); + } + } + }; + }); + } + + #executeSpec(spec, done) { + const config = this.#getConfig(); + spec.execute( + this.#runQueueWithSkipPolicy.bind(this), + this.#globalErrors, + done, + this.#executionTree.isExcluded(spec), + config.failSpecWithNoExpectations, + config.detectLateRejectionHandling + ); + } + + #executeSuiteSegment(suite, segmentNumber, done) { + const wrappedChildren = this.#wrapNodes( + this.#executionTree.childrenOfSuiteSegment(suite, segmentNumber) + ); + const onStart = { + fn: next => { + this.#suiteSegmentStart(suite, next); + } + }; + const queueableFns = [ + onStart, + ...this.#addBeforeAndAfterAlls(suite, wrappedChildren) + ]; + + this.#runQueueWithSkipPolicy({ + // TODO: if onComplete always takes 0-1 arguments (and it probably does) + // then it can be switched to an arrow fn with a named arg. + onComplete: function() { + const args = Array.prototype.slice.call(arguments, [0]); + this.#suiteSegmentComplete(suite, suite.getResult(), () => { + done.apply(undefined, args); + }); + }.bind(this), + queueableFns, + userContext: suite.sharedUserContext(), + onException: function() { + suite.handleException.apply(suite, arguments); + }, + onMultipleDone: suite.onMultipleDone + ? suite.onMultipleDone.bind(suite) + : null + }); + } + + #suiteSegmentStart(suite, next) { + this.#currentRunableTracker.pushSuite(suite); + this.#runableResources.initForRunable(suite.id, suite.parentSuite.id); + this.#reportDispatcher.suiteStarted(suite.result).then(next); + suite.startTimer(); + } + + #suiteSegmentComplete(suite, result, next) { + suite.cleanupBeforeAfter(); + + if (suite !== this.#currentRunableTracker.currentSuite()) { + throw new Error('Tried to complete the wrong suite'); + } + + this.#runableResources.clearForRunable(suite.id); + this.#currentRunableTracker.popSuite(); + + if (result.status === 'failed') { + this.hasFailures = true; + } + suite.endTimer(); + + if (suite.hadBeforeAllFailure) { + this.#reportChildrenOfBeforeAllFailure(suite).then(() => { + this.#reportSuiteDone(suite, result, next); + }); + } else { + this.#reportSuiteDone(suite, result, next); + } + } + + #reportSuiteDone(suite, result, next) { + suite.reportedDone = true; + this.#reportDispatcher.suiteDone(result).then(next); + } + + #addBeforeAndAfterAlls(suite, wrappedChildren) { + if (this.#executionTree.isExcluded(suite)) { + return wrappedChildren; + } + + return suite.beforeAllFns + .concat(wrappedChildren) + .concat(suite.afterAllFns); + } + + #runQueueWithSkipPolicy(options) { + if (options.isLeaf) { + // A spec + options.SkipPolicy = j$.CompleteOnFirstErrorSkipPolicy; + } else { + // A suite + if (this.#getConfig().stopOnSpecFailure) { + options.SkipPolicy = j$.CompleteOnFirstErrorSkipPolicy; + } else { + options.SkipPolicy = j$.SkipAfterBeforeAllErrorPolicy; + } + } + + return this.#runQueue(options); + } + } + + return TreeRunner; +}; diff --git a/src/core/requireCore.js b/src/core/requireCore.js index 7959c9ff..46d9c235 100644 --- a/src/core/requireCore.js +++ b/src/core/requireCore.js @@ -84,6 +84,7 @@ var getJasmineRequireObj = (function(jasmineGlobal) { j$.SuiteBuilder = jRequire.SuiteBuilder(j$); j$.Timer = jRequire.Timer(); j$.TreeProcessor = jRequire.TreeProcessor(j$); + j$.TreeRunner = jRequire.TreeRunner(j$); j$.version = jRequire.version(); j$.Order = jRequire.Order(); j$.DiffBuilder = jRequire.DiffBuilder(j$);