diff --git a/spec/core/RunnerSpec.js b/spec/core/RunnerSpec.js index 1758ebaf..57547975 100644 --- a/spec/core/RunnerSpec.js +++ b/spec/core/RunnerSpec.js @@ -1,229 +1,4 @@ describe('Runner', function() { - // TODO: Update and re-enable these once things stabilize - xdescribe('TreeProcessor suiteSegmentComplete callback', function() { - it('throws if the wrong suite is passed to suiteSegmentComplete', async function() { - const TreeProcessor = spyTreeProcessorCtor(); - const subject = new jasmineUnderTest.Runner({ - ...defaultCtorOptions(), - TreeProcessor - }); - - const promise = subject.execute(); - expect(TreeProcessor).toHaveBeenCalled(); - await checkImmediateRejection(promise); - - const treeProcessorOpts = TreeProcessor.calls.argsFor(0)[0]; - const suiteToRun = { - parentSuite: stubSuite(), - startTimer: () => {} - }; - await callWithNext(treeProcessorOpts.suiteSegmentStart, [suiteToRun]); - - expect(function() { - const someOtherSuite = {}; - treeProcessorOpts.suiteSegmentComplete(someOtherSuite, {}, () => {}); - }).toThrowError('Tried to complete the wrong suite'); - }); - - it("ends the suite's timer", async function() { - const TreeProcessor = spyTreeProcessorCtor(); - const subject = new jasmineUnderTest.Runner({ - ...defaultCtorOptions(), - TreeProcessor - }); - - const promise = subject.execute(); - expect(TreeProcessor).toHaveBeenCalled(); - await checkImmediateRejection(promise); - - const treeProcessorOpts = TreeProcessor.calls.argsFor(0)[0]; - const suiteToRun = { - parentSuite: stubSuite(), - startTimer: () => {}, - endTimer: jasmine.createSpy('suiteToRun.endTimer') - }; - await callWithNext(treeProcessorOpts.suiteSegmentStart, [suiteToRun]); - await callWithNext(treeProcessorOpts.suiteSegmentComplete, [ - suiteToRun, - {} - ]); - - expect(suiteToRun.endTimer).toHaveBeenCalled(); - }); - - it('sets hasFailures to true when the suite fails', async function() { - const TreeProcessor = spyTreeProcessorCtor(); - const subject = new jasmineUnderTest.Runner({ - ...defaultCtorOptions(), - TreeProcessor - }); - - const promise = subject.execute(); - expect(TreeProcessor).toHaveBeenCalled(); - await checkImmediateRejection(promise); - - const treeProcessorOpts = TreeProcessor.calls.argsFor(0)[0]; - const suiteToRun = { - parentSuite: stubSuite(), - startTimer: () => {}, - endTimer: jasmine.createSpy('suiteToRun.endTimer') - }; - await callWithNext(treeProcessorOpts.suiteSegmentStart, [suiteToRun]); - await callWithNext(treeProcessorOpts.suiteSegmentComplete, [ - suiteToRun, - { status: 'failed' } - ]); - - expect(subject.hasFailures).toBe(true); - }); - - it('does not set hasFailures to true when the suite passes', async function() { - const TreeProcessor = spyTreeProcessorCtor(); - const subject = new jasmineUnderTest.Runner({ - ...defaultCtorOptions(), - TreeProcessor - }); - - const promise = subject.execute(); - expect(TreeProcessor).toHaveBeenCalled(); - await checkImmediateRejection(promise); - - const treeProcessorOpts = TreeProcessor.calls.argsFor(0)[0]; - const suiteToRun = { - parentSuite: stubSuite(), - startTimer: () => {}, - endTimer: jasmine.createSpy('suiteToRun.endTimer') - }; - await callWithNext(treeProcessorOpts.suiteSegmentStart, [suiteToRun]); - await callWithNext(treeProcessorOpts.suiteSegmentComplete, [ - suiteToRun, - { status: 'passed' } - ]); - - expect(subject.hasFailures).toBe(false); - }); - - it('does not set hasFailures to false when the suite passes', async function() { - const TreeProcessor = spyTreeProcessorCtor(); - const subject = new jasmineUnderTest.Runner({ - ...defaultCtorOptions(), - TreeProcessor - }); - - const promise = subject.execute(); - expect(TreeProcessor).toHaveBeenCalled(); - await checkImmediateRejection(promise); - - const treeProcessorOpts = TreeProcessor.calls.argsFor(0)[0]; - subject.hasFailures = true; - const suiteToRun = { - parentSuite: stubSuite(), - startTimer: () => {}, - endTimer: jasmine.createSpy('suiteToRun.endTimer') - }; - await callWithNext(treeProcessorOpts.suiteSegmentStart, [suiteToRun]); - await callWithNext(treeProcessorOpts.suiteSegmentComplete, [ - suiteToRun, - { status: 'passed' } - ]); - - expect(subject.hasFailures).toBe(true); - }); - - describe('reporting', function() { - it('reports the suiteDone event', async function() { - const TreeProcessor = spyTreeProcessorCtor(); - const reportDispatcher = spyReporter(); - const subject = new jasmineUnderTest.Runner({ - ...defaultCtorOptions(), - TreeProcessor, - reportDispatcher - }); - - const promise = subject.execute(); - expect(TreeProcessor).toHaveBeenCalled(); - await checkImmediateRejection(promise); - - const treeProcessorOpts = TreeProcessor.calls.argsFor(0)[0]; - const suiteToRun = { - parentSuite: stubSuite(), - startTimer: () => {}, - endTimer: jasmine.createSpy('suiteToRun.endTimer') - }; - await callWithNext(treeProcessorOpts.suiteSegmentStart, [suiteToRun]); - await callWithNext(treeProcessorOpts.suiteSegmentComplete, [ - suiteToRun, - { status: 'passed' } - ]); - - expect(reportDispatcher.suiteDone).toHaveBeenCalled(); - }); - - describe('when the suite had a beforeAll failure', function() { - it('reports children before the suiteDone event', async function() { - const TreeProcessor = spyTreeProcessorCtor(); - const reportDispatcher = spyReporter(); - const reportSpecDone = jasmine - .createSpy('reportSpecDone') - .and.callFake(function(child, result, next) { - next(); - }); - const subject = new jasmineUnderTest.Runner({ - ...defaultCtorOptions(), - TreeProcessor, - reportDispatcher, - reportSpecDone - }); - - const promise = subject.execute(); - expect(TreeProcessor).toHaveBeenCalled(); - await checkImmediateRejection(promise); - - const treeProcessorOpts = TreeProcessor.calls.argsFor(0)[0]; - const suiteToRun = { - parentSuite: stubSuite(), - children: [ - { - result: { description: 'a spec' }, - addExpectationResult: jasmine.createSpy('addExpectationResult') - } - ], - startTimer: () => {}, - endTimer: jasmine.createSpy('suiteToRun.endTimer') - }; - await callWithNext(treeProcessorOpts.suiteSegmentStart, [suiteToRun]); - - suiteToRun.hadBeforeAllFailure = true; - await callWithNext(treeProcessorOpts.suiteSegmentComplete, [ - suiteToRun, - { status: 'passed' } - ]); - - expect( - suiteToRun.children[0].addExpectationResult - ).toHaveBeenCalledWith( - false, - { - passed: false, - message: - 'Not run because a beforeAll function failed. The beforeAll failure will be reported on the suite that caused it.' - }, - true - ); - expect( - suiteToRun.children[0].addExpectationResult - ).toHaveBeenCalledBefore(reportSpecDone); - expect(reportSpecDone).toHaveBeenCalledBefore( - reportDispatcher.suiteDone - ); - expect(reportDispatcher.specStarted).toHaveBeenCalledBefore( - reportSpecDone - ); - }); - }); - }); - }); - describe('Integration with TreeProcessor and TreeRunner', function() { let suiteNumber, specNumber, @@ -852,58 +627,4 @@ describe('Runner', function() { await expectAsync(promise).toBePending(); }); }); - - async function callWithNext(fn, args) { - let next; - const nextPromise = new Promise(function(resolve) { - next = resolve; - }); - fn.apply(null, [...args, next]); - await nextPromise; - } - - // Check whether promise has already been rejected - async function checkImmediateRejection(promise) { - await Promise.race([promise, Promise.resolve()]); - } - - function spyTreeProcessorCtor() { - return jasmine.createSpy('TreeProcessor ctor').and.returnValue({ - execute: () => {}, - processTree: () => ({ valid: true }) - }); - } - - function spyReporter() { - return jasmine.createSpyObj('reportDispatcher', { - jasmineStarted: Promise.resolve(), - jasmineDone: Promise.resolve(), - suiteStarted: Promise.resolve(), - suiteDone: Promise.resolve(), - specStarted: Promise.resolve(), - specDone: Promise.resolve() - }); - } - - function defaultCtorOptions() { - return { - topSuite: stubSuite(), - runableResources: { - initForRunable: () => {}, - clearForRunable: () => {} - }, - reportDispatcher: spyReporter(), - focusedRunables: () => [], - getConfig: () => ({}), - totalSpecsDefined: () => 1 - }; - } - - function stubSuite() { - return { - result: { - failedExpectations: [] - } - }; - } }); diff --git a/spec/core/TreeRunnerSpec.js b/spec/core/TreeRunnerSpec.js index 88b14074..b46c2c28 100644 --- a/spec/core/TreeRunnerSpec.js +++ b/spec/core/TreeRunnerSpec.js @@ -270,6 +270,247 @@ describe('TreeRunner', function() { } }); + describe('Suite execution', function() { + it('reports the duration of the suite', async function() { + const timer = jasmine.createSpyObj('timer', ['start', 'elapsed']); + const topSuite = new jasmineUnderTest.Suite({ id: 'topSuite' }); + const suite = new jasmineUnderTest.Suite({ + id: 'suite1', + parentSuite: topSuite, + timer + }); + topSuite.addChild(suite); + const executionTree = { + topSuite, + childrenOfTopSuite() { + return [{ suite }]; + }, + childrenOfSuiteSegment() { + return []; + }, + isExcluded() { + return false; + } + }; + const runQueue = jasmine.createSpy('runQueue'); + const reportDispatcher = mockReportDispatcher(); + const subject = new jasmineUnderTest.TreeRunner({ + executionTree, + runQueue, + globalErrors: mockGlobalErrors(), + runableResources: mockRunableResources(), + reportDispatcher, + currentRunableTracker: new jasmineUnderTest.CurrentRunableTracker(), + getConfig() { + return {}; + }, + reportChildrenOfBeforeAllFailure() {} + }); + + const executePromise = subject.execute(); + expect(runQueue).toHaveBeenCalledTimes(1); + const topSuiteRunQueueOpts = runQueue.calls.mostRecent().args[0]; + runQueue.calls.reset(); + topSuiteRunQueueOpts.queueableFns[0].fn(function() {}); + + expect(runQueue).toHaveBeenCalledTimes(1); + expect(timer.start).not.toHaveBeenCalled(); + const suiteRunQueueOpts = runQueue.calls.mostRecent().args[0]; + suiteRunQueueOpts.queueableFns[0].fn(); + expect(timer.start).toHaveBeenCalled(); + expect(timer.elapsed).not.toHaveBeenCalled(); + + timer.elapsed.and.returnValue('the duration'); + suiteRunQueueOpts.onComplete(); + expect(timer.elapsed).toHaveBeenCalled(); + const result = suite.getResult(); + expect(result.duration).toEqual('the duration'); + expect(reportDispatcher.suiteDone).toHaveBeenCalledWith(result); + + await expectAsync(executePromise).toBePending(); + }); + + it('returns false if a suite failed', async function() { + const topSuite = new jasmineUnderTest.Suite({ id: 'topSuite' }); + const failingSuite = new jasmineUnderTest.Suite({ + id: 'failingSuite', + parentSuite: topSuite + }); + const passingSuite = new jasmineUnderTest.Suite({ + id: 'passingSuite', + parentSuite: topSuite + }); + const executionTree = { + topSuite, + childrenOfTopSuite() { + return [{ suite: failingSuite }, { suite: passingSuite }]; + }, + childrenOfSuiteSegment() { + return []; + }, + isExcluded() { + return false; + } + }; + const runQueue = jasmine.createSpy('runQueue'); + const reportDispatcher = mockReportDispatcher(); + const subject = new jasmineUnderTest.TreeRunner({ + executionTree, + runQueue, + globalErrors: mockGlobalErrors(), + runableResources: mockRunableResources(), + reportDispatcher, + currentRunableTracker: new jasmineUnderTest.CurrentRunableTracker(), + getConfig() { + return {}; + }, + reportChildrenOfBeforeAllFailure() {} + }); + + const executePromise = subject.execute(); + expect(runQueue).toHaveBeenCalledTimes(1); + const topSuiteRunQueueOpts = runQueue.calls.mostRecent().args[0]; + runQueue.calls.reset(); + topSuiteRunQueueOpts.queueableFns[0].fn(function() {}); + + // Fail the first suite. + expect(runQueue).toHaveBeenCalledTimes(1); + const failingSuiteRunQueueOpts = runQueue.calls.mostRecent().args[0]; + runQueue.calls.reset(); + failingSuiteRunQueueOpts.queueableFns[0].fn(); + failingSuite.addExpectationResult(false, {}); + failingSuiteRunQueueOpts.onComplete(); + + // Passing the second suite should not reset the overall result. + topSuiteRunQueueOpts.queueableFns[1].fn(function() {}); + expect(runQueue).toHaveBeenCalledTimes(1); + const passingSuiteRunQueueOpts = runQueue.calls.mostRecent().args[0]; + passingSuiteRunQueueOpts.queueableFns[0].fn(); + passingSuiteRunQueueOpts.onComplete(); + + topSuiteRunQueueOpts.onComplete(); + + const result = await executePromise; + expect(result.hasFailures).toEqual(true); + }); + + it('reports children when there is a beforeAll failure', async function() { + const topSuite = new jasmineUnderTest.Suite({ id: 'topSuite' }); + const suite = new jasmineUnderTest.Suite({ + id: 'suite', + parentSuite: topSuite + }); + suite.beforeAll({ fn() {} }); + const spec = new jasmineUnderTest.Spec({ + id: 'spec', + parentSuite: suite, + queueableFn: { fn() {} } + }); + const executionTree = { + topSuite, + childrenOfTopSuite() { + return [{ suite }]; + }, + childrenOfSuiteSegment() { + return [{ spec }]; + }, + isExcluded() { + return false; + } + }; + const runQueue = jasmine.createSpy('runQueue'); + const reportDispatcher = mockReportDispatcher(); + const reportChildrenOfBeforeAllFailure = jasmine + .createSpy('reportChildrenOfBeforeAllFailure') + .and.returnValue(Promise.resolve()); + const subject = new jasmineUnderTest.TreeRunner({ + executionTree, + runQueue, + globalErrors: mockGlobalErrors(), + runableResources: mockRunableResources(), + reportDispatcher, + currentRunableTracker: new jasmineUnderTest.CurrentRunableTracker(), + reportChildrenOfBeforeAllFailure, + getConfig() { + return {}; + } + }); + + const executePromise = subject.execute(); + expect(runQueue).toHaveBeenCalledTimes(1); + const topSuiteRunQueueOpts = runQueue.calls.mostRecent().args[0]; + runQueue.calls.reset(); + topSuiteRunQueueOpts.queueableFns[0].fn(function() {}); + + expect(runQueue).toHaveBeenCalledTimes(1); + const suiteRunQueueOpts = runQueue.calls.mostRecent().args[0]; + suiteRunQueueOpts.queueableFns[0].fn(); + suite.hadBeforeAllFailure = true; + suiteRunQueueOpts.onComplete(); + await Promise.resolve(); + + expect(reportChildrenOfBeforeAllFailure).toHaveBeenCalledBefore( + reportDispatcher.suiteDone + ); + await expectAsync(executePromise).toBePending(); + }); + + it('throws if the wrong suite is completed', async function() { + const topSuite = new jasmineUnderTest.Suite({ id: 'topSuite' }); + const suite = new jasmineUnderTest.Suite({ + id: 'suite', + parentSuite: topSuite + }); + const spec = new jasmineUnderTest.Spec({ + id: 'spec', + parentSuite: suite, + queueableFn: { fn() {} } + }); + const executionTree = { + topSuite, + childrenOfTopSuite() { + return [{ suite }]; + }, + childrenOfSuiteSegment() { + return [{ spec }]; + }, + isExcluded() { + return false; + } + }; + const runQueue = jasmine.createSpy('runQueue'); + const reportDispatcher = mockReportDispatcher(); + const subject = new jasmineUnderTest.TreeRunner({ + executionTree, + runQueue, + globalErrors: mockGlobalErrors(), + runableResources: mockRunableResources(), + reportDispatcher, + currentRunableTracker: new jasmineUnderTest.CurrentRunableTracker(), + getConfig() { + return {}; + }, + reportChildrenOfBeforeAllFailure() {} + }); + + const executePromise = subject.execute(); + expect(runQueue).toHaveBeenCalledTimes(1); + const topSuiteRunQueueOpts = runQueue.calls.mostRecent().args[0]; + runQueue.calls.reset(); + topSuiteRunQueueOpts.queueableFns[0].fn(function() {}); + + expect(runQueue).toHaveBeenCalledTimes(1); + const suiteRunQueueOpts = runQueue.calls.mostRecent().args[0]; + + // Complete the suite without starting it + expect(function() { + suiteRunQueueOpts.onComplete(); + }).toThrowError('Tried to complete the wrong suite'); + + await expectAsync(executePromise).toBePending(); + }); + }); + function mockReportDispatcher() { const reportDispatcher = jasmine.createSpyObj( 'reportDispatcher',