diff --git a/lib/jasmine-core/jasmine.js b/lib/jasmine-core/jasmine.js index 23230e4c..f69e68f9 100644 --- a/lib/jasmine-core/jasmine.js +++ b/lib/jasmine-core/jasmine.js @@ -9407,6 +9407,7 @@ getJasmineRequireObj().Runner = function(j$) { #runableResources; #runQueue; #TreeProcessor; + #treeProcessor; #globalErrors; #reportDispatcher; #getConfig; @@ -9469,12 +9470,9 @@ getJasmineRequireObj().Runner = function(j$) { seed: j$.isNumber_(config.seed) ? config.seed + '' : config.seed }); - const processor = new this.#TreeProcessor({ + this.#treeProcessor = new this.#TreeProcessor({ tree: this.#topSuite, runnableIds: runablesToRun, - executeTopSuite: this.#executeTopSuite.bind(this), - executeSpec: this.#executeSpec.bind(this), - executeSuiteSegment: this.#executeSuiteSegment.bind(this), orderChildren: function(node) { return order.sort(node.children); }, @@ -9482,12 +9480,12 @@ getJasmineRequireObj().Runner = function(j$) { return !config.specFilter(spec); } }); - processor.processTree(); + this.#treeProcessor.processTree(); - return this.#execute2(runablesToRun, order, processor); + return this.#execute2(runablesToRun, order); } - async #execute2(runablesToRun, order, processor) { + async #execute2(runablesToRun, order) { const totalSpecsDefined = this.#getTotalSpecsDefined(); this.#runableResources.initForRunable(this.#topSuite.id); @@ -9511,7 +9509,7 @@ getJasmineRequireObj().Runner = function(j$) { }); this.#currentlyExecutingSuites.push(this.#topSuite); - await processor.execute(); + await this.#executeTopSuite(); if (this.#topSuite.hadBeforeAllFailure) { await this.#reportChildrenOfBeforeAllFailure(this.#topSuite); @@ -9565,28 +9563,34 @@ getJasmineRequireObj().Runner = function(j$) { return jasmineDoneInfo; } - // TreeProcessor callback. - #executeTopSuite(topSuite, wrappedChildren, done) { + async #executeTopSuite() { + const wrappedChildren = this.#wrapNodes( + this.#treeProcessor.childrenOfTopSuite() + ); const queueableFns = this.#addBeforeAndAfterAlls( - topSuite, - true, + this.#topSuite, wrappedChildren ); - this.#runQueueWithSkipPolicy({ - queueableFns, - userContext: topSuite.sharedUserContext(), - onException: function() { - topSuite.handleException.apply(topSuite, arguments); - }.bind(this), - onComplete: done, - onMultipleDone: topSuite.onMultipleDone - ? topSuite.onMultipleDone.bind(topSuite) - : null + + 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 + }); }); } - // TreeProcessor callback. Mutually recursive with TreeProcessor##executeNode. - #executeSuiteSegment(suite, excluded, wrappedChildren, done) { + #executeSuiteSegment(suite, segmentNumber, done) { + const wrappedChildren = this.#wrapNodes( + this.#treeProcessor.childrenOfSuiteSegment(suite, segmentNumber) + ); const onStart = { fn: next => { this.#suiteSegmentStart(suite, next); @@ -9594,7 +9598,7 @@ getJasmineRequireObj().Runner = function(j$) { }; const queueableFns = [ onStart, - ...this.#addBeforeAndAfterAlls(suite, excluded, wrappedChildren) + ...this.#addBeforeAndAfterAlls(suite, wrappedChildren) ]; this.#runQueueWithSkipPolicy({ @@ -9617,27 +9621,40 @@ getJasmineRequireObj().Runner = function(j$) { }); } - // TreeProcessor callback. - #executeSpec(spec, excluded, done) { + #executeSpec(spec, done) { const config = this.#getConfig(); spec.execute( this.#runQueueWithSkipPolicy.bind(this), this.#globalErrors, done, - excluded, + this.#treeProcessor.isExcluded(spec), config.failSpecWithNoExpectations, config.detectLateRejectionHandling ); } - #addBeforeAndAfterAlls(suite, willExecute, wrappedChildren) { - if (willExecute) { - return suite.beforeAllFns - .concat(wrappedChildren) - .concat(suite.afterAllFns); - } else { + #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.#treeProcessor.isExcluded(suite)) { return wrappedChildren; } + + return suite.beforeAllFns + .concat(wrappedChildren) + .concat(suite.afterAllFns); } #suiteSegmentStart(suite, next) { @@ -11386,9 +11403,6 @@ getJasmineRequireObj().TreeProcessor = function(j$) { class TreeProcessor { #tree; - #executeTopSuite; - #executeSpec; - #executeSuiteSegment; #runnableIds; #orderChildren; #excludeNode; @@ -11398,9 +11412,6 @@ getJasmineRequireObj().TreeProcessor = function(j$) { constructor(attrs) { this.#tree = attrs.tree; this.#runnableIds = attrs.runnableIds; - this.#executeTopSuite = attrs.executeTopSuite; - this.#executeSpec = attrs.executeSpec; - this.#executeSuiteSegment = attrs.executeSuiteSegment; this.#orderChildren = attrs.orderChildren || @@ -11412,7 +11423,7 @@ getJasmineRequireObj().TreeProcessor = function(j$) { function(node) { return false; }; - this.#stats = { valid: true }; + this.#stats = {}; this.#processed = false; } @@ -11422,22 +11433,27 @@ getJasmineRequireObj().TreeProcessor = function(j$) { return this.#stats; } - async execute() { - if (!this.#processed) { - this.processTree(); - } + childrenOfTopSuite() { + return this.childrenOfSuiteSegment(this.#tree, 0); + } - if (!this.#stats.valid) { - throw new Error('invalid order'); - } - - const wrappedChildren = this.#wrapChildren(this.#tree, 0); - - await new Promise(resolve => { - this.#executeTopSuite(this.#tree, wrappedChildren, resolve); + childrenOfSuiteSegment(suite, segmentNumber) { + const segmentChildren = this.#stats[suite.id].segments[segmentNumber] + .nodes; + return segmentChildren.map(function(child) { + if (child.owner.children) { + return { suite: child.owner, segmentNumber: child.index }; + } else { + return { spec: child.owner }; + } }); } + isExcluded(node) { + const nodeStats = this.#stats[node.id]; + return node.children ? !nodeStats.willExecute : nodeStats.excluded; + } + #runnableIndex(id) { for (let i = 0; i < this.#runnableIds.length; i++) { if (this.#runnableIds[i] === id) { @@ -11475,15 +11491,8 @@ getJasmineRequireObj().TreeProcessor = function(j$) { for (let i = 0; i < orderedChildren.length; i++) { const child = orderedChildren[i]; - this.#processNode(child, parentExcluded); - - if (!this.#stats.valid) { - return; - } - const childStats = this.#stats[child.id]; - hasExecutableChild = hasExecutableChild || childStats.willExecute; } @@ -11500,7 +11509,6 @@ getJasmineRequireObj().TreeProcessor = function(j$) { 'The specified spec/suite order splits up a suite, running unrelated specs in the middle of it. This will become an error in a future release.' ); } else { - this.#stats = { valid: false }; throw new Error( 'Invalid order: would cause a beforeAll or afterAll to be run multiple times' ); @@ -11508,37 +11516,6 @@ getJasmineRequireObj().TreeProcessor = function(j$) { } } } - - #wrapChildren(node, segmentNumber) { - const result = [], - segmentChildren = this.#stats[node.id].segments[segmentNumber].nodes; - - for (let i = 0; i < segmentChildren.length; i++) { - result.push( - this.#executeNode(segmentChildren[i].owner, segmentChildren[i].index) - ); - } - - return result; - } - - #executeNode(node, segmentNumber) { - if (node.children) { - return { - fn: done => { - const wrappedChildren = this.#wrapChildren(node, segmentNumber); - const willExecute = this.#stats[node.id].willExecute; - this.#executeSuiteSegment(node, willExecute, wrappedChildren, done); - } - }; - } else { - return { - fn: done => { - this.#executeSpec(node, this.#stats[node.id].excluded, done); - } - }; - } - } } function segmentChildren(node, orderedChildren, stats, executableIndex) { diff --git a/spec/core/TreeProcessorSpec.js b/spec/core/TreeProcessorSpec.js index 89d9c76b..e3d5f9e8 100644 --- a/spec/core/TreeProcessorSpec.js +++ b/spec/core/TreeProcessorSpec.js @@ -34,8 +34,6 @@ describe('TreeProcessor', function() { }), result = processor.processTree(); - expect(result.valid).toBe(true); - expect(result[leaf.id]).toEqual({ excluded: false, willExecute: true, @@ -51,8 +49,6 @@ describe('TreeProcessor', function() { }), result = processor.processTree(); - expect(result.valid).toBe(true); - expect(result[leaf.id]).toEqual({ excluded: false, willExecute: false, @@ -68,8 +64,6 @@ describe('TreeProcessor', function() { }), result = processor.processTree(); - expect(result.valid).toBe(true); - expect(result[leaf.id]).toEqual({ excluded: true, willExecute: false, @@ -88,8 +82,6 @@ describe('TreeProcessor', function() { }), result = processor.processTree(); - expect(result.valid).toBe(true); - expect(result[leaf.id]).toEqual({ excluded: true, willExecute: false, @@ -106,8 +98,6 @@ describe('TreeProcessor', function() { }), result = processor.processTree(); - expect(result.valid).toBe(true); - expect(result[parent.id]).toEqual({ excluded: false, willExecute: true, @@ -130,8 +120,6 @@ describe('TreeProcessor', function() { }), result = processor.processTree(); - expect(result.valid).toBe(true); - expect(result[parent.id]).toEqual({ excluded: false, willExecute: false, @@ -166,8 +154,6 @@ describe('TreeProcessor', function() { }), result = processor.processTree(); - expect(result.valid).toBe(true); - expect(result[root.id]).toEqual({ excluded: false, willExecute: true, @@ -217,7 +203,7 @@ describe('TreeProcessor', function() { }); }); - it('marks the run order invalid if it would re-enter a node that does not allow re-entry', async function() { + it('throws if the specified order would re-enter a node that does not allow re-entry', function() { const leaf1 = new Leaf(), leaf2 = new Leaf(), leaf3 = new Leaf(), @@ -233,14 +219,9 @@ describe('TreeProcessor', function() { }).toThrowError( 'Invalid order: would cause a beforeAll or afterAll to be run multiple times' ); - - // Subsequent attempts to execute should fail - await expectAsync(processor.execute()).toBeRejectedWithError( - 'invalid order' - ); }); - it('marks the run order valid if a node being re-entered allows re-entry', function() { + it('does not throw if a node being re-entered allows re-entry', function() { const leaf1 = new Leaf(); const leaf2 = new Leaf(); const leaf3 = new Leaf(); @@ -253,15 +234,14 @@ describe('TreeProcessor', function() { const env = jasmineUnderTest.getEnv(); spyOn(env, 'deprecated'); - const result = processor.processTree(); + processor.processTree(); - expect(result.valid).toBe(true); expect(env.deprecated).toHaveBeenCalledWith( 'The specified spec/suite order splits up a suite, running unrelated specs in the middle of it. This will become an error in a future release.' ); }); - it("marks the run order valid if a node which can't be re-entered is only entered once", function() { + it("does not throw if a node which can't be re-entered is only entered once", function() { const leaf1 = new Leaf(), leaf2 = new Leaf(), leaf3 = new Leaf(), @@ -270,22 +250,20 @@ describe('TreeProcessor', function() { processor = new jasmineUnderTest.TreeProcessor({ tree: root, runnableIds: [leaf2.id, leaf1.id, leaf3.id] - }), - result = processor.processTree(); + }); - expect(result.valid).toBe(true); + processor.processTree(); }); - it("marks the run order valid if a node which can't be re-entered is run directly", function() { + it("does not throw if a node which can't be re-entered is run directly", function() { const noReentry = new Node({ noReenter: true }), root = new Node({ children: [noReentry] }), processor = new jasmineUnderTest.TreeProcessor({ tree: root, runnableIds: [root.id] - }), - result = processor.processTree(); + }); - expect(result.valid).toBe(true); + processor.processTree(); }); // TODO: Replace these with corresponding unit tests elsewhere, once things stabilize diff --git a/src/core/Runner.js b/src/core/Runner.js index 250e167a..0abfc143 100644 --- a/src/core/Runner.js +++ b/src/core/Runner.js @@ -6,6 +6,7 @@ getJasmineRequireObj().Runner = function(j$) { #runableResources; #runQueue; #TreeProcessor; + #treeProcessor; #globalErrors; #reportDispatcher; #getConfig; @@ -68,12 +69,9 @@ getJasmineRequireObj().Runner = function(j$) { seed: j$.isNumber_(config.seed) ? config.seed + '' : config.seed }); - const processor = new this.#TreeProcessor({ + this.#treeProcessor = new this.#TreeProcessor({ tree: this.#topSuite, runnableIds: runablesToRun, - executeTopSuite: this.#executeTopSuite.bind(this), - executeSpec: this.#executeSpec.bind(this), - executeSuiteSegment: this.#executeSuiteSegment.bind(this), orderChildren: function(node) { return order.sort(node.children); }, @@ -81,12 +79,12 @@ getJasmineRequireObj().Runner = function(j$) { return !config.specFilter(spec); } }); - processor.processTree(); + this.#treeProcessor.processTree(); - return this.#execute2(runablesToRun, order, processor); + return this.#execute2(runablesToRun, order); } - async #execute2(runablesToRun, order, processor) { + async #execute2(runablesToRun, order) { const totalSpecsDefined = this.#getTotalSpecsDefined(); this.#runableResources.initForRunable(this.#topSuite.id); @@ -110,7 +108,7 @@ getJasmineRequireObj().Runner = function(j$) { }); this.#currentlyExecutingSuites.push(this.#topSuite); - await processor.execute(); + await this.#executeTopSuite(); if (this.#topSuite.hadBeforeAllFailure) { await this.#reportChildrenOfBeforeAllFailure(this.#topSuite); @@ -164,28 +162,34 @@ getJasmineRequireObj().Runner = function(j$) { return jasmineDoneInfo; } - // TreeProcessor callback. - #executeTopSuite(topSuite, wrappedChildren, done) { + async #executeTopSuite() { + const wrappedChildren = this.#wrapNodes( + this.#treeProcessor.childrenOfTopSuite() + ); const queueableFns = this.#addBeforeAndAfterAlls( - topSuite, - true, + this.#topSuite, wrappedChildren ); - this.#runQueueWithSkipPolicy({ - queueableFns, - userContext: topSuite.sharedUserContext(), - onException: function() { - topSuite.handleException.apply(topSuite, arguments); - }.bind(this), - onComplete: done, - onMultipleDone: topSuite.onMultipleDone - ? topSuite.onMultipleDone.bind(topSuite) - : null + + 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 + }); }); } - // TreeProcessor callback. Mutually recursive with TreeProcessor##executeNode. - #executeSuiteSegment(suite, excluded, wrappedChildren, done) { + #executeSuiteSegment(suite, segmentNumber, done) { + const wrappedChildren = this.#wrapNodes( + this.#treeProcessor.childrenOfSuiteSegment(suite, segmentNumber) + ); const onStart = { fn: next => { this.#suiteSegmentStart(suite, next); @@ -193,7 +197,7 @@ getJasmineRequireObj().Runner = function(j$) { }; const queueableFns = [ onStart, - ...this.#addBeforeAndAfterAlls(suite, excluded, wrappedChildren) + ...this.#addBeforeAndAfterAlls(suite, wrappedChildren) ]; this.#runQueueWithSkipPolicy({ @@ -216,27 +220,40 @@ getJasmineRequireObj().Runner = function(j$) { }); } - // TreeProcessor callback. - #executeSpec(spec, excluded, done) { + #executeSpec(spec, done) { const config = this.#getConfig(); spec.execute( this.#runQueueWithSkipPolicy.bind(this), this.#globalErrors, done, - excluded, + this.#treeProcessor.isExcluded(spec), config.failSpecWithNoExpectations, config.detectLateRejectionHandling ); } - #addBeforeAndAfterAlls(suite, willExecute, wrappedChildren) { - if (willExecute) { - return suite.beforeAllFns - .concat(wrappedChildren) - .concat(suite.afterAllFns); - } else { + #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.#treeProcessor.isExcluded(suite)) { return wrappedChildren; } + + return suite.beforeAllFns + .concat(wrappedChildren) + .concat(suite.afterAllFns); } #suiteSegmentStart(suite, next) { diff --git a/src/core/TreeProcessor.js b/src/core/TreeProcessor.js index 328c50e3..da46eadd 100644 --- a/src/core/TreeProcessor.js +++ b/src/core/TreeProcessor.js @@ -4,9 +4,6 @@ getJasmineRequireObj().TreeProcessor = function(j$) { class TreeProcessor { #tree; - #executeTopSuite; - #executeSpec; - #executeSuiteSegment; #runnableIds; #orderChildren; #excludeNode; @@ -16,9 +13,6 @@ getJasmineRequireObj().TreeProcessor = function(j$) { constructor(attrs) { this.#tree = attrs.tree; this.#runnableIds = attrs.runnableIds; - this.#executeTopSuite = attrs.executeTopSuite; - this.#executeSpec = attrs.executeSpec; - this.#executeSuiteSegment = attrs.executeSuiteSegment; this.#orderChildren = attrs.orderChildren || @@ -30,7 +24,7 @@ getJasmineRequireObj().TreeProcessor = function(j$) { function(node) { return false; }; - this.#stats = { valid: true }; + this.#stats = {}; this.#processed = false; } @@ -40,22 +34,27 @@ getJasmineRequireObj().TreeProcessor = function(j$) { return this.#stats; } - async execute() { - if (!this.#processed) { - this.processTree(); - } + childrenOfTopSuite() { + return this.childrenOfSuiteSegment(this.#tree, 0); + } - if (!this.#stats.valid) { - throw new Error('invalid order'); - } - - const wrappedChildren = this.#wrapChildren(this.#tree, 0); - - await new Promise(resolve => { - this.#executeTopSuite(this.#tree, wrappedChildren, resolve); + childrenOfSuiteSegment(suite, segmentNumber) { + const segmentChildren = this.#stats[suite.id].segments[segmentNumber] + .nodes; + return segmentChildren.map(function(child) { + if (child.owner.children) { + return { suite: child.owner, segmentNumber: child.index }; + } else { + return { spec: child.owner }; + } }); } + isExcluded(node) { + const nodeStats = this.#stats[node.id]; + return node.children ? !nodeStats.willExecute : nodeStats.excluded; + } + #runnableIndex(id) { for (let i = 0; i < this.#runnableIds.length; i++) { if (this.#runnableIds[i] === id) { @@ -93,15 +92,8 @@ getJasmineRequireObj().TreeProcessor = function(j$) { for (let i = 0; i < orderedChildren.length; i++) { const child = orderedChildren[i]; - this.#processNode(child, parentExcluded); - - if (!this.#stats.valid) { - return; - } - const childStats = this.#stats[child.id]; - hasExecutableChild = hasExecutableChild || childStats.willExecute; } @@ -118,7 +110,6 @@ getJasmineRequireObj().TreeProcessor = function(j$) { 'The specified spec/suite order splits up a suite, running unrelated specs in the middle of it. This will become an error in a future release.' ); } else { - this.#stats = { valid: false }; throw new Error( 'Invalid order: would cause a beforeAll or afterAll to be run multiple times' ); @@ -126,37 +117,6 @@ getJasmineRequireObj().TreeProcessor = function(j$) { } } } - - #wrapChildren(node, segmentNumber) { - const result = [], - segmentChildren = this.#stats[node.id].segments[segmentNumber].nodes; - - for (let i = 0; i < segmentChildren.length; i++) { - result.push( - this.#executeNode(segmentChildren[i].owner, segmentChildren[i].index) - ); - } - - return result; - } - - #executeNode(node, segmentNumber) { - if (node.children) { - return { - fn: done => { - const wrappedChildren = this.#wrapChildren(node, segmentNumber); - const willExecute = this.#stats[node.id].willExecute; - this.#executeSuiteSegment(node, willExecute, wrappedChildren, done); - } - }; - } else { - return { - fn: done => { - this.#executeSpec(node, this.#stats[node.id].excluded, done); - } - }; - } - } } function segmentChildren(node, orderedChildren, stats, executableIndex) {