From 1fc911e0fa0888ea9883d5313f8c5b710aa5f416 Mon Sep 17 00:00:00 2001 From: Nico Jansen Date: Fri, 1 Oct 2021 16:12:57 +0200 Subject: [PATCH 1/4] Support running jasmine multiple times Add support for running jasmine multiple times. ```js const Jasmine = require('jasmine'); async function main() { const jasmine = new Jasmine({ projectBaseDir: process.cwd() }); let specId = 'spec0'; jasmine.loadConfigFile('./spec/support/jasmine.json'); jasmine.env.configure({ specFilter(sp) { return sp.id === specId; }, autoCleanClosures: false }); jasmine.exit = () => {}; await jasmine.execute(); specId = 'spec2'; await jasmine.execute(); } main().catch((err) => { console.error(err); process.exitCode = 1; }); ``` With `jasmine.env.configure({ autoCleanClosures: false })` you disable Jasmine's feature to automatically clean closures (functions) during the test run. This is a requirement to be able to rerun. When `execute` is called more than once, the `topSuite.reset` is called, which will reset the state for the next run as well as reset any child suites. Add a function `exclude` to the `Suite` and `Spec` clases. This functions similar to `pend`, but will allow the "pending" state to persist over multiple runs. This is useful when `xit` is used. Revert changes to jasmine.js fix: make sure to call hooks during second run Remove jsdoc from private apis Fix elint issue Add new line --- spec/core/EnvSpec.js | 21 ++- spec/core/SuiteSpec.js | 82 +++++++++ spec/core/integration/SpecRunningSpec.js | 205 +++++++++++++++++++++++ src/core/Env.js | 44 +++-- src/core/Spec.js | 56 ++++++- src/core/Suite.js | 74 +++++--- 6 files changed, 438 insertions(+), 44 deletions(-) diff --git a/spec/core/EnvSpec.js b/spec/core/EnvSpec.js index 566be5eb..ffc39aa0 100644 --- a/spec/core/EnvSpec.js +++ b/spec/core/EnvSpec.js @@ -265,13 +265,13 @@ describe('Env', function() { }); describe('#xit', function() { - it('calls spec.pend with "Temporarily disabled with xit"', function() { - var pendSpy = jasmine.createSpy(); + it('calls spec.exclude with "Temporarily disabled with xit"', function() { + var excludeSpy = jasmine.createSpy(); spyOn(env, 'it').and.returnValue({ - pend: pendSpy + exclude: excludeSpy }); env.xit('foo', function() {}); - expect(pendSpy).toHaveBeenCalledWith('Temporarily disabled with xit'); + expect(excludeSpy).toHaveBeenCalledWith('Temporarily disabled with xit'); }); it('throws an error when it receives a non-fn argument', function() { @@ -521,5 +521,18 @@ describe('Env', function() { jasmine.getEnv().requireNoPromises(); expect(env.execute()).toBeUndefined(); }); + + it('should reset the topSuite when run twice', function() { + jasmine.getEnv().requirePromises(); + spyOn(env.topSuite(), 'reset'); + return env + .execute() // 1 + .then(function() { + return env.execute(); // 2 + }) + .then(function() { + expect(env.topSuite().reset).toHaveBeenCalledOnceWith(); + }); + }); }); }); diff --git a/spec/core/SuiteSpec.js b/spec/core/SuiteSpec.js index 2a1cd757..5e42c0cc 100644 --- a/spec/core/SuiteSpec.js +++ b/spec/core/SuiteSpec.js @@ -142,4 +142,86 @@ describe('Suite', function() { ); }); }); + + describe('attr.autoCleanClosures', function() { + function arrangeSuite(attrs) { + var suite = new jasmineUnderTest.Suite(attrs); + suite.beforeAll(function() {}); + suite.beforeEach(function() {}); + suite.afterEach(function() {}); + suite.afterAll(function() {}); + return suite; + } + + it('should clean closures when "attr.autoCleanClosures" is missing', function() { + var suite = arrangeSuite({}); + suite.cleanupBeforeAfter(); + expect(suite.beforeAllFns[0].fn).toBe(null); + expect(suite.beforeFns[0].fn).toBe(null); + expect(suite.afterFns[0].fn).toBe(null); + expect(suite.afterAllFns[0].fn).toBe(null); + }); + + it('should clean closures when "attr.autoCleanClosures" is true', function() { + var suite = arrangeSuite({ autoCleanClosures: true }); + suite.cleanupBeforeAfter(); + expect(suite.beforeAllFns[0].fn).toBe(null); + expect(suite.beforeFns[0].fn).toBe(null); + expect(suite.afterFns[0].fn).toBe(null); + expect(suite.afterAllFns[0].fn).toBe(null); + }); + + it('should NOT clean closures when "attr.autoCleanClosures" is false', function() { + var suite = arrangeSuite({ autoCleanClosures: false }); + suite.cleanupBeforeAfter(); + expect(suite.beforeAllFns[0].fn).not.toBe(null); + expect(suite.beforeFns[0].fn).not.toBe(null); + expect(suite.afterFns[0].fn).not.toBe(null); + expect(suite.afterAllFns[0].fn).not.toBe(null); + }); + }); + + describe('#reset', function() { + it('should reset the "pending" status', function() { + var suite = new jasmineUnderTest.Suite({}); + suite.pend(); + suite.reset(); + expect(suite.getResult().status).toBe('passed'); + }); + + it('should not reset the "pending" status when the suite was excluded', function() { + var suite = new jasmineUnderTest.Suite({}); + suite.exclude(); + suite.reset(); + expect(suite.getResult().status).toBe('pending'); + }); + + it('should also reset the children', function() { + var suite = new jasmineUnderTest.Suite({}); + var child1 = jasmine.createSpyObj(['reset']); + var child2 = jasmine.createSpyObj(['reset']); + suite.addChild(child1); + suite.addChild(child2); + + suite.reset(); + + expect(child1.reset).toHaveBeenCalled(); + expect(child2.reset).toHaveBeenCalled(); + }); + + it('should reset the failedExpectations', function() { + var suite = new jasmineUnderTest.Suite({ + expectationResultFactory: function(error) { + return error; + } + }); + suite.onException(new Error()); + + suite.reset(); + + var result = suite.getResult(); + expect(result.status).toBe('passed'); + expect(result.failedExpectations).toHaveSize(0); + }); + }); }); diff --git a/spec/core/integration/SpecRunningSpec.js b/spec/core/integration/SpecRunningSpec.js index d6dececd..a9f0ae60 100644 --- a/spec/core/integration/SpecRunningSpec.js +++ b/spec/core/integration/SpecRunningSpec.js @@ -1026,4 +1026,209 @@ describe('spec running', function() { env.stopOnSpecFailure(true); }); }); + + describe('run multiple times', function() { + beforeEach(function() { + env.configure({ autoCleanClosures: false, random: false }); + }); + + it('should be able to run multiple times', function() { + var actions = []; + + env.describe('Suite', function() { + env.it('spec1', function() { + actions.push('spec1'); + }); + env.describe('inner suite', function() { + env.it('spec2', function() { + actions.push('spec2'); + }); + }); + }); + + return env.execute().then(function() { + expect(actions).toEqual(['spec1', 'spec2']); + return env.execute().then(function() { + expect(actions).toEqual(['spec1', 'spec2', 'spec1', 'spec2']); + }); + }); + }); + + it('should reset results between runs', function() { + var specResults = {}; + var suiteResults = {}; + var firstExecution = true; + + env.addReporter({ + specDone: function(spec) { + specResults[spec.description] = spec.status; + }, + suiteDone: function(suite) { + suiteResults[suite.description] = suite.status; + }, + jasmineDone: function() { + firstExecution = false; + } + }); + + env.describe('suite0', function() { + env.it('spec1', function() { + if (firstExecution) { + env.expect(1).toBe(2); + } + }); + env.describe('suite1', function() { + env.it('spec2', function() { + if (firstExecution) { + env.pending(); + } + }); + env.xit('spec3', function() {}); // Always pending + }); + env.describe('suite2', function() { + env.it('spec4', function() { + if (firstExecution) { + throw new Error('spec 3 fails'); + } + }); + }); + env.describe('suite3', function() { + beforeEach(function() { + throw new Error('suite 3 fails'); + }); + env.it('spec5', function() {}); + }); + env.xdescribe('suite4', function() { + // Always pending + env.it('spec6', function() {}); + }); + env.describe('suite5', function() { + env.it('spec7'); + }); + }); + + return env.execute().then(function() { + expect(specResults).toEqual({ + spec1: 'failed', + spec2: 'pending', + spec3: 'pending', + spec4: 'failed', + spec6: 'pending', + spec7: 'pending' + }); + expect(suiteResults).toEqual({ + suite0: 'passed', + suite1: 'passed', + suite2: 'passed', + suite3: 'failed', + suite4: 'pending', + suite5: 'passed' + }); + return env.execute().then(function() { + expect(specResults).toEqual({ + spec1: 'passed', + spec2: 'passed', + spec3: 'pending', + spec4: 'passed', + spec6: 'pending', + spec7: 'pending' + }); + expect(suiteResults).toEqual({ + suite0: 'passed', + suite1: 'passed', + suite2: 'passed', + suite3: 'passed', + suite4: 'pending', + suite5: 'passed' + }); + }); + }); + }); + + it('should execute before and after hooks per run', function() { + var timeline = []; + var timelineFn = function(hookName) { + return function() { + timeline.push(hookName); + }; + }; + var expectedTimeLine = [ + 'beforeAll', + 'beforeEach', + 'spec1', + 'afterEach', + 'beforeEach', + 'spec2', + 'afterEach', + 'afterAll' + ]; + + env.describe('suite0', function() { + env.beforeAll(timelineFn('beforeAll')); + env.beforeEach(timelineFn('beforeEach')); + env.afterEach(timelineFn('afterEach')); + env.afterAll(timelineFn('afterAll')); + env.it('spec1', timelineFn('spec1')); + env.it('spec2', timelineFn('spec2')); + }); + return env.execute().then(function() { + expect(timeline).toEqual(expectedTimeLine); + timeline = []; + return env.execute().then(function() { + expect(timeline).toEqual(expectedTimeLine); + }); + }); + }); + + it('should be able to filter out different tests in subsequent runs', function() { + var specResults = {}; + var focussedSpec = 'spec1'; + + env.configure({ + specFilter: function(spec) { + return spec.description === focussedSpec; + } + }); + + env.addReporter({ + specDone: function(spec) { + specResults[spec.description] = spec.status; + } + }); + + env.describe('suite0', function() { + env.it('spec1', function() {}); + env.it('spec2', function() {}); + env.it('spec3', function() {}); + }); + + return env + .execute() + .then(function() { + expect(specResults).toEqual({ + spec1: 'passed', + spec2: 'excluded', + spec3: 'excluded' + }); + focussedSpec = 'spec2'; + return env.execute(); + }) + .then(function() { + expect(specResults).toEqual({ + spec1: 'excluded', + spec2: 'passed', + spec3: 'excluded' + }); + focussedSpec = 'spec3'; + return env.execute(); + }) + .then(function() { + expect(specResults).toEqual({ + spec1: 'excluded', + spec2: 'excluded', + spec3: 'passed' + }); + }); + }); + }); }); diff --git a/src/core/Env.js b/src/core/Env.js index 895579c1..c01e23e1 100644 --- a/src/core/Env.js +++ b/src/core/Env.js @@ -138,7 +138,16 @@ getJasmineRequireObj().Env = function(j$) { * @type function * @default undefined */ - Promise: undefined + Promise: undefined, + /** + * Clean closures when a suite is done running (done by clearing the stored function reference). + * This prevents memory leaks, but you won't be able to run jasmine multiple times. + * @name Configuration#autoCleanClosures + * @since 3.10.0 + * @type boolean + * @default true + */ + autoCleanClosures: true }; var currentSuite = function() { @@ -191,7 +200,8 @@ getJasmineRequireObj().Env = function(j$) { var booleanProps = [ 'random', 'failSpecWithNoExpectations', - 'hideDisabled' + 'hideDisabled', + 'autoCleanClosures' ]; booleanProps.forEach(function(prop) { @@ -497,10 +507,11 @@ getJasmineRequireObj().Env = function(j$) { delete runnableResources[id]; }; - var beforeAndAfterFns = function(suite) { + var beforeAndAfterFns = function(targetSuite) { return function() { var befores = [], - afters = []; + afters = [], + suite = targetSuite; while (suite) { befores = befores.concat(suite.beforeFns); @@ -701,9 +712,9 @@ getJasmineRequireObj().Env = function(j$) { description: 'Jasmine__TopLevel__Suite', expectationFactory: expectationFactory, asyncExpectationFactory: suiteAsyncExpectationFactory, - expectationResultFactory: expectationResultFactory + expectationResultFactory: expectationResultFactory, + autoCleanClosures: config.autoCleanClosures }); - defaultResourcesForRunnable(topSuite.id); currentDeclarationSuite = topSuite; /** @@ -821,6 +832,11 @@ getJasmineRequireObj().Env = function(j$) { * @return {Promise} */ this.execute = function(runnablesToRun, onComplete) { + if (this._executedBefore) { + topSuite.reset(); + } + this._executedBefore = true; + defaultResourcesForRunnable(topSuite.id); installGlobalErrors(); if (!runnablesToRun) { @@ -1107,7 +1123,8 @@ getJasmineRequireObj().Env = function(j$) { expectationFactory: expectationFactory, asyncExpectationFactory: suiteAsyncExpectationFactory, expectationResultFactory: expectationResultFactory, - throwOnExpectationFailure: config.oneFailurePerSpec + throwOnExpectationFailure: config.oneFailurePerSpec, + autoCleanClosures: config.autoCleanClosures }); return suite; @@ -1120,8 +1137,8 @@ getJasmineRequireObj().Env = function(j$) { if (specDefinitions.length > 0) { throw new Error('describe does not expect any arguments'); } - if (currentDeclarationSuite.markedPending) { - suite.pend(); + if (currentDeclarationSuite.markedExcluding) { + suite.exclude(); } addSpecsToSuite(suite, specDefinitions); return suite; @@ -1131,7 +1148,7 @@ getJasmineRequireObj().Env = function(j$) { ensureIsNotNested('xdescribe'); ensureIsFunction(specDefinitions, 'xdescribe'); var suite = suiteFactory(description); - suite.pend(); + suite.exclude(); addSpecsToSuite(suite, specDefinitions); return suite; }; @@ -1216,6 +1233,7 @@ getJasmineRequireObj().Env = function(j$) { timeout: timeout || 0 }, throwOnExpectationFailure: config.oneFailurePerSpec, + autoCleanClosures: config.autoCleanClosures, timer: new j$.Timer() }); return spec; @@ -1251,8 +1269,8 @@ getJasmineRequireObj().Env = function(j$) { } var spec = specFactory(description, fn, currentDeclarationSuite, timeout); - if (currentDeclarationSuite.markedPending) { - spec.pend(); + if (currentDeclarationSuite.markedExcluding) { + spec.exclude(); } currentDeclarationSuite.addChild(spec); return spec; @@ -1266,7 +1284,7 @@ getJasmineRequireObj().Env = function(j$) { ensureIsFunctionOrAsync(fn, 'xit'); } var spec = this.it.apply(this, arguments); - spec.pend('Temporarily disabled with xit'); + spec.exclude('Temporarily disabled with xit'); return spec; }; diff --git a/src/core/Spec.js b/src/core/Spec.js index 2691ce04..29669fa2 100644 --- a/src/core/Spec.js +++ b/src/core/Spec.js @@ -36,6 +36,8 @@ getJasmineRequireObj().Spec = function(j$) { return {}; }; this.onStart = attrs.onStart || function() {}; + this.autoCleanClosures = + attrs.autoCleanClosures === undefined ? true : !!attrs.autoCleanClosures; this.getSpecName = attrs.getSpecName || function() { @@ -53,7 +55,7 @@ getJasmineRequireObj().Spec = function(j$) { this.timer = attrs.timer || new j$.Timer(); if (!this.queueableFn.fn) { - this.pend(); + this.exclude(); } /** @@ -121,7 +123,9 @@ x */ var complete = { fn: function(done) { - self.queueableFn.fn = null; + if (self.autoCleanClosures) { + self.queueableFn.fn = null; + } self.result.status = self.status(excluded, failSpecWithNoExp); self.result.duration = self.timer.elapsed(); self.resultCallback(self.result, done); @@ -158,6 +162,37 @@ x */ this.queueRunnerFactory(runnerConfig); }; + Spec.prototype.reset = function() { + /** + * @typedef SpecResult + * @property {Int} id - The unique id of this spec. + * @property {String} description - The description passed to the {@link it} that created this spec. + * @property {String} fullName - The full description including all ancestors of this spec. + * @property {Expectation[]} failedExpectations - The list of expectations that failed during execution of this spec. + * @property {Expectation[]} passedExpectations - The list of expectations that passed during execution of this spec. + * @property {Expectation[]} deprecationWarnings - The list of deprecation warnings that occurred during execution this spec. + * @property {String} pendingReason - If the spec is {@link pending}, this will be the reason. + * @property {String} status - Once the spec has completed, this string represents the pass/fail status of this spec. + * @property {number} duration - The time in ms used by the spec execution, including any before/afterEach. + * @property {Object} properties - User-supplied properties, if any, that were set using {@link Env#setSpecProperty} + * @property {TraceEntry[]|null} trace - Trace messages, if any, that were logged using {@link Env#trace} during a failing spec. + * @since 2.0.0 + */ + this.result = { + id: this.id, + description: this.description, + fullName: this.getFullName(), + failedExpectations: [], + passedExpectations: [], + deprecationWarnings: [], + pendingReason: this.excludeMessage, + duration: null, + properties: null, + trace: null + }; + this.markedPending = this.markedExcluding; + }; + Spec.prototype.onException = function onException(e) { if (Spec.isPendingSpecException(e)) { this.pend(extractCustomPendingMessage(e)); @@ -181,6 +216,10 @@ x */ ); }; + /* + * Marks state as pending + * @param {string} [message] An optional reason message + */ Spec.prototype.pend = function(message) { this.markedPending = true; if (message) { @@ -188,6 +227,19 @@ x */ } }; + /* + * Like {@link Spec#pend}, but pending state will survive {@link Spec#reset} + * Useful for fit, xit, where pending state remains. + * @param {string} [message] An optional reason message + */ + Spec.prototype.exclude = function(message) { + this.markedExcluding = true; + if (this.message) { + this.excludeMessage = message; + } + this.pend(); + }; + Spec.prototype.getResult = function() { this.result.status = this.status(); return this.result; diff --git a/src/core/Suite.js b/src/core/Suite.js index 40f3e6d3..c0368409 100644 --- a/src/core/Suite.js +++ b/src/core/Suite.js @@ -33,6 +33,8 @@ getJasmineRequireObj().Suite = function(j$) { this.asyncExpectationFactory = attrs.asyncExpectationFactory; this.expectationResultFactory = attrs.expectationResultFactory; this.throwOnExpectationFailure = !!attrs.throwOnExpectationFailure; + this.autoCleanClosures = + attrs.autoCleanClosures === undefined ? true : !!attrs.autoCleanClosures; this.beforeFns = []; this.afterFns = []; @@ -49,27 +51,7 @@ getJasmineRequireObj().Suite = function(j$) { */ this.children = []; - /** - * @typedef SuiteResult - * @property {Int} id - The unique id of this suite. - * @property {String} description - The description text passed to the {@link describe} that made this suite. - * @property {String} fullName - The full description including all ancestors of this suite. - * @property {Expectation[]} failedExpectations - The list of expectations that failed in an {@link afterAll} for this suite. - * @property {Expectation[]} deprecationWarnings - The list of deprecation warnings that occurred on this suite. - * @property {String} status - Once the suite has completed, this string represents the pass/fail status of this suite. - * @property {number} duration - The time in ms for Suite execution, including any before/afterAll, before/afterEach. - * @property {Object} properties - User-supplied properties, if any, that were set using {@link Env#setSuiteProperty} - * @since 2.0.0 - */ - this.result = { - id: this.id, - description: this.description, - fullName: this.getFullName(), - failedExpectations: [], - deprecationWarnings: [], - duration: null, - properties: null - }; + this.reset(); } Suite.prototype.setSuiteProperty = function(key, value) { @@ -106,10 +88,22 @@ getJasmineRequireObj().Suite = function(j$) { return fullName.join(' '); }; + /* + * Mark the suite with "pending" status + */ Suite.prototype.pend = function() { this.markedPending = true; }; + /* + * Like {@link Suite#pend}, but pending state will survive {@link Spec#reset} + * Useful for fdescribe, xdescribe, where pending state should remain. + */ + Suite.prototype.exclude = function() { + this.pend(); + this.markedExcluding = true; + }; + Suite.prototype.beforeEach = function(fn) { this.beforeFns.unshift(fn); }; @@ -141,10 +135,40 @@ getJasmineRequireObj().Suite = function(j$) { } Suite.prototype.cleanupBeforeAfter = function() { - removeFns(this.beforeAllFns); - removeFns(this.afterAllFns); - removeFns(this.beforeFns); - removeFns(this.afterFns); + if (this.autoCleanClosures) { + removeFns(this.beforeAllFns); + removeFns(this.afterAllFns); + removeFns(this.beforeFns); + removeFns(this.afterFns); + } + }; + + Suite.prototype.reset = function() { + /** + * @typedef SuiteResult + * @property {Int} id - The unique id of this suite. + * @property {String} description - The description text passed to the {@link describe} that made this suite. + * @property {String} fullName - The full description including all ancestors of this suite. + * @property {Expectation[]} failedExpectations - The list of expectations that failed in an {@link afterAll} for this suite. + * @property {Expectation[]} deprecationWarnings - The list of deprecation warnings that occurred on this suite. + * @property {String} status - Once the suite has completed, this string represents the pass/fail status of this suite. + * @property {number} duration - The time in ms for Suite execution, including any before/afterAll, before/afterEach. + * @property {Object} properties - User-supplied properties, if any, that were set using {@link Env#setSuiteProperty} + * @since 2.0.0 + */ + this.result = { + id: this.id, + description: this.description, + fullName: this.getFullName(), + failedExpectations: [], + deprecationWarnings: [], + duration: null, + properties: null + }; + this.markedPending = this.markedExcluding; + this.children.forEach(function(child) { + child.reset(); + }); }; Suite.prototype.addChild = function(child) { From 4482355885d07c1b4794ce3cd60a1de74db5520e Mon Sep 17 00:00:00 2001 From: Steve Gravrock Date: Wed, 6 Oct 2021 11:29:59 -0700 Subject: [PATCH 2/4] Removed jsdoc entry for SpecResult#trace Deferred to 4.0. --- lib/jasmine-core/jasmine.js | 1 - src/core/Spec.js | 1 - 2 files changed, 2 deletions(-) diff --git a/lib/jasmine-core/jasmine.js b/lib/jasmine-core/jasmine.js index 72fb98cb..7e1fa857 100644 --- a/lib/jasmine-core/jasmine.js +++ b/lib/jasmine-core/jasmine.js @@ -930,7 +930,6 @@ x */ * @property {String} status - Once the spec has completed, this string represents the pass/fail status of this spec. * @property {number} duration - The time in ms used by the spec execution, including any before/afterEach. * @property {Object} properties - User-supplied properties, if any, that were set using {@link Env#setSpecProperty} - * @property {TraceEntry[]|null} trace - Trace messages, if any, that were logged using {@link Env#trace} during a failing spec. * @since 2.0.0 */ this.result = { diff --git a/src/core/Spec.js b/src/core/Spec.js index 29669fa2..1548583a 100644 --- a/src/core/Spec.js +++ b/src/core/Spec.js @@ -175,7 +175,6 @@ x */ * @property {String} status - Once the spec has completed, this string represents the pass/fail status of this spec. * @property {number} duration - The time in ms used by the spec execution, including any before/afterEach. * @property {Object} properties - User-supplied properties, if any, that were set using {@link Env#setSpecProperty} - * @property {TraceEntry[]|null} trace - Trace messages, if any, that were logged using {@link Env#trace} during a failing spec. * @since 2.0.0 */ this.result = { From 503715c275c4dce807db7ab66d64b697fec384e6 Mon Sep 17 00:00:00 2001 From: Nico Jansen Date: Thu, 7 Oct 2021 10:23:52 +0200 Subject: [PATCH 3/4] test(ie): refactor promises to callbacks --- spec/core/integration/SpecRunningSpec.js | 64 ++++++++++++------------ 1 file changed, 33 insertions(+), 31 deletions(-) diff --git a/spec/core/integration/SpecRunningSpec.js b/spec/core/integration/SpecRunningSpec.js index a9f0ae60..45f0bf7b 100644 --- a/spec/core/integration/SpecRunningSpec.js +++ b/spec/core/integration/SpecRunningSpec.js @@ -1032,7 +1032,7 @@ describe('spec running', function() { env.configure({ autoCleanClosures: false, random: false }); }); - it('should be able to run multiple times', function() { + it('should be able to run multiple times', function(done) { var actions = []; env.describe('Suite', function() { @@ -1046,15 +1046,16 @@ describe('spec running', function() { }); }); - return env.execute().then(function() { + env.execute(null, function() { expect(actions).toEqual(['spec1', 'spec2']); - return env.execute().then(function() { + env.execute(null, function() { expect(actions).toEqual(['spec1', 'spec2', 'spec1', 'spec2']); + done(); }); }); }); - it('should reset results between runs', function() { + it('should reset results between runs', function(done) { var specResults = {}; var suiteResults = {}; var firstExecution = true; @@ -1093,7 +1094,7 @@ describe('spec running', function() { }); }); env.describe('suite3', function() { - beforeEach(function() { + env.beforeEach(function() { throw new Error('suite 3 fails'); }); env.it('spec5', function() {}); @@ -1107,12 +1108,13 @@ describe('spec running', function() { }); }); - return env.execute().then(function() { + env.execute(null, function() { expect(specResults).toEqual({ spec1: 'failed', spec2: 'pending', spec3: 'pending', spec4: 'failed', + spec5: 'failed', spec6: 'pending', spec7: 'pending' }); @@ -1120,16 +1122,17 @@ describe('spec running', function() { suite0: 'passed', suite1: 'passed', suite2: 'passed', - suite3: 'failed', + suite3: 'passed', suite4: 'pending', suite5: 'passed' }); - return env.execute().then(function() { + env.execute(null, function() { expect(specResults).toEqual({ spec1: 'passed', spec2: 'passed', spec3: 'pending', spec4: 'passed', + spec5: 'failed', spec6: 'pending', spec7: 'pending' }); @@ -1141,11 +1144,12 @@ describe('spec running', function() { suite4: 'pending', suite5: 'passed' }); + done(); }); }); }); - it('should execute before and after hooks per run', function() { + it('should execute before and after hooks per run', function(done) { var timeline = []; var timelineFn = function(hookName) { return function() { @@ -1171,16 +1175,17 @@ describe('spec running', function() { env.it('spec1', timelineFn('spec1')); env.it('spec2', timelineFn('spec2')); }); - return env.execute().then(function() { + env.execute(null, function() { expect(timeline).toEqual(expectedTimeLine); timeline = []; - return env.execute().then(function() { + env.execute(null, function() { expect(timeline).toEqual(expectedTimeLine); + done(); }); }); }); - it('should be able to filter out different tests in subsequent runs', function() { + it('should be able to filter out different tests in subsequent runs', function(done) { var specResults = {}; var focussedSpec = 'spec1'; @@ -1202,33 +1207,30 @@ describe('spec running', function() { env.it('spec3', function() {}); }); - return env - .execute() - .then(function() { - expect(specResults).toEqual({ - spec1: 'passed', - spec2: 'excluded', - spec3: 'excluded' - }); - focussedSpec = 'spec2'; - return env.execute(); - }) - .then(function() { + env.execute(null, function() { + expect(specResults).toEqual({ + spec1: 'passed', + spec2: 'excluded', + spec3: 'excluded' + }); + focussedSpec = 'spec2'; + env.execute(null, function() { expect(specResults).toEqual({ spec1: 'excluded', spec2: 'passed', spec3: 'excluded' }); focussedSpec = 'spec3'; - return env.execute(); - }) - .then(function() { - expect(specResults).toEqual({ - spec1: 'excluded', - spec2: 'excluded', - spec3: 'passed' + env.execute(null, function() { + expect(specResults).toEqual({ + spec1: 'excluded', + spec2: 'excluded', + spec3: 'passed' + }); + done(); }); }); + }); }); }); }); From 8cadfbd829179621cd9eba415556a6f58b31c544 Mon Sep 17 00:00:00 2001 From: Steve Gravrock Date: Thu, 7 Oct 2021 10:51:06 -0700 Subject: [PATCH 4/4] Fixed deprecation warning in spec --- spec/core/EnvSpec.js | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/spec/core/EnvSpec.js b/spec/core/EnvSpec.js index f0a1f6ce..4825d4b1 100644 --- a/spec/core/EnvSpec.js +++ b/spec/core/EnvSpec.js @@ -832,14 +832,20 @@ describe('Env', function() { it('should reset the topSuite when run twice', function() { jasmine.getEnv().requirePromises(); - spyOn(env.topSuite(), 'reset'); + spyOn(jasmineUnderTest.Suite.prototype, 'reset'); return env .execute() // 1 .then(function() { return env.execute(); // 2 }) .then(function() { - expect(env.topSuite().reset).toHaveBeenCalledOnceWith(); + var id; + expect( + jasmineUnderTest.Suite.prototype.reset + ).toHaveBeenCalledOnceWith(); + id = jasmineUnderTest.Suite.prototype.reset.calls.thisFor(0).id; + expect(id).toBeTruthy(); + expect(id).toEqual(env.topSuite().id); }); }); });