Merge branch 'main' into 3.99

This commit is contained in:
Steve Gravrock
2021-10-07 10:38:35 -07:00
7 changed files with 570 additions and 82 deletions

View File

@@ -818,6 +818,8 @@ getJasmineRequireObj().Spec = function(j$) {
return {};
};
this.onStart = attrs.onStart || function() {};
this.autoCleanClosures =
attrs.autoCleanClosures === undefined ? true : !!attrs.autoCleanClosures;
this.getSpecName =
attrs.getSpecName ||
function() {
@@ -836,7 +838,7 @@ getJasmineRequireObj().Spec = function(j$) {
this.timer = attrs.timer || new j$.Timer();
if (!this.queueableFn.fn) {
this.pend();
this.exclude();
}
/**
@@ -904,7 +906,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);
@@ -958,6 +962,36 @@ 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}
* @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));
@@ -981,6 +1015,10 @@ x */
);
};
/*
* Marks state as pending
* @param {string} [message] An optional reason message
*/
Spec.prototype.pend = function(message) {
this.markedPending = true;
if (message) {
@@ -988,6 +1026,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;
@@ -1251,6 +1302,15 @@ getJasmineRequireObj().Env = function(j$) {
* property and always create native promises instead.
*/
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,
/**
* Whether or not to issue warnings for certain deprecated functionality
* every time it's used. If not set or set to false, deprecation warnings
@@ -1315,7 +1375,8 @@ getJasmineRequireObj().Env = function(j$) {
var booleanProps = [
'random',
'failSpecWithNoExpectations',
'hideDisabled'
'hideDisabled',
'autoCleanClosures'
];
booleanProps.forEach(function(prop) {
@@ -1683,10 +1744,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);
@@ -1912,10 +1974,10 @@ getJasmineRequireObj().Env = function(j$) {
description: 'Jasmine__TopLevel__Suite',
expectationFactory: expectationFactory,
asyncExpectationFactory: suiteAsyncExpectationFactory,
expectationResultFactory: expectationResultFactory
expectationResultFactory: expectationResultFactory,
autoCleanClosures: config.autoCleanClosures
});
var deprecator = new j$.Deprecator(topSuite);
defaultResourcesForRunnable(topSuite.id);
currentDeclarationSuite = topSuite;
/**
@@ -2034,6 +2096,11 @@ getJasmineRequireObj().Env = function(j$) {
* @return {Promise<undefined>}
*/
this.execute = function(runnablesToRun, onComplete) {
if (this._executedBefore) {
topSuite.reset();
}
this._executedBefore = true;
defaultResourcesForRunnable(topSuite.id);
installGlobalErrors();
if (!runnablesToRun) {
@@ -2320,7 +2387,8 @@ getJasmineRequireObj().Env = function(j$) {
expectationFactory: expectationFactory,
asyncExpectationFactory: suiteAsyncExpectationFactory,
expectationResultFactory: expectationResultFactory,
throwOnExpectationFailure: config.oneFailurePerSpec
throwOnExpectationFailure: config.oneFailurePerSpec,
autoCleanClosures: config.autoCleanClosures
});
return suite;
@@ -2333,8 +2401,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);
if (suite.parentSuite && !suite.children.length) {
@@ -2351,7 +2419,7 @@ getJasmineRequireObj().Env = function(j$) {
ensureIsNotNested('xdescribe');
ensureIsFunction(specDefinitions, 'xdescribe');
var suite = suiteFactory(description);
suite.pend();
suite.exclude();
addSpecsToSuite(suite, specDefinitions);
return j$.deprecatingSuiteProxy(suite, suite.parentSuite, this);
};
@@ -2437,6 +2505,7 @@ getJasmineRequireObj().Env = function(j$) {
timeout: timeout || 0
},
throwOnExpectationFailure: config.oneFailurePerSpec,
autoCleanClosures: config.autoCleanClosures,
timer: new j$.Timer()
});
return spec;
@@ -2472,8 +2541,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);
@@ -2493,7 +2562,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 j$.deprecatingSpecProxy(spec, this);
};
@@ -10088,6 +10157,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 = [];
@@ -10104,27 +10175,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) {
@@ -10161,10 +10212,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);
};
@@ -10196,10 +10259,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) {

View File

@@ -529,13 +529,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() {
@@ -829,5 +829,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();
});
});
});
});

View File

@@ -143,6 +143,88 @@ 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);
});
});
describe('#onMultipleDone', function() {
it('logs a special deprecation when it is the top suite', function() {
var env = jasmine.createSpyObj('env', ['deprecated']);

View File

@@ -1034,4 +1034,211 @@ 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(done) {
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');
});
});
});
env.execute(null, function() {
expect(actions).toEqual(['spec1', 'spec2']);
env.execute(null, function() {
expect(actions).toEqual(['spec1', 'spec2', 'spec1', 'spec2']);
done();
});
});
});
it('should reset results between runs', function(done) {
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() {
env.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');
});
});
env.execute(null, function() {
expect(specResults).toEqual({
spec1: 'failed',
spec2: 'pending',
spec3: 'pending',
spec4: 'failed',
spec5: 'failed',
spec6: 'pending',
spec7: 'pending'
});
expect(suiteResults).toEqual({
suite0: 'passed',
suite1: 'passed',
suite2: 'passed',
suite3: 'passed',
suite4: 'pending',
suite5: 'passed'
});
env.execute(null, function() {
expect(specResults).toEqual({
spec1: 'passed',
spec2: 'passed',
spec3: 'pending',
spec4: 'passed',
spec5: 'failed',
spec6: 'pending',
spec7: 'pending'
});
expect(suiteResults).toEqual({
suite0: 'passed',
suite1: 'passed',
suite2: 'passed',
suite3: 'passed',
suite4: 'pending',
suite5: 'passed'
});
done();
});
});
});
it('should execute before and after hooks per run', function(done) {
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'));
});
env.execute(null, function() {
expect(timeline).toEqual(expectedTimeLine);
timeline = [];
env.execute(null, function() {
expect(timeline).toEqual(expectedTimeLine);
done();
});
});
});
it('should be able to filter out different tests in subsequent runs', function(done) {
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() {});
});
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';
env.execute(null, function() {
expect(specResults).toEqual({
spec1: 'excluded',
spec2: 'excluded',
spec3: 'passed'
});
done();
});
});
});
});
});
});

View File

@@ -141,6 +141,15 @@ getJasmineRequireObj().Env = function(j$) {
* property and always create native promises instead.
*/
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,
/**
* Whether or not to issue warnings for certain deprecated functionality
* every time it's used. If not set or set to false, deprecation warnings
@@ -205,7 +214,8 @@ getJasmineRequireObj().Env = function(j$) {
var booleanProps = [
'random',
'failSpecWithNoExpectations',
'hideDisabled'
'hideDisabled',
'autoCleanClosures'
];
booleanProps.forEach(function(prop) {
@@ -573,10 +583,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);
@@ -802,10 +813,10 @@ getJasmineRequireObj().Env = function(j$) {
description: 'Jasmine__TopLevel__Suite',
expectationFactory: expectationFactory,
asyncExpectationFactory: suiteAsyncExpectationFactory,
expectationResultFactory: expectationResultFactory
expectationResultFactory: expectationResultFactory,
autoCleanClosures: config.autoCleanClosures
});
var deprecator = new j$.Deprecator(topSuite);
defaultResourcesForRunnable(topSuite.id);
currentDeclarationSuite = topSuite;
/**
@@ -924,6 +935,11 @@ getJasmineRequireObj().Env = function(j$) {
* @return {Promise<undefined>}
*/
this.execute = function(runnablesToRun, onComplete) {
if (this._executedBefore) {
topSuite.reset();
}
this._executedBefore = true;
defaultResourcesForRunnable(topSuite.id);
installGlobalErrors();
if (!runnablesToRun) {
@@ -1210,7 +1226,8 @@ getJasmineRequireObj().Env = function(j$) {
expectationFactory: expectationFactory,
asyncExpectationFactory: suiteAsyncExpectationFactory,
expectationResultFactory: expectationResultFactory,
throwOnExpectationFailure: config.oneFailurePerSpec
throwOnExpectationFailure: config.oneFailurePerSpec,
autoCleanClosures: config.autoCleanClosures
});
return suite;
@@ -1223,8 +1240,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);
if (suite.parentSuite && !suite.children.length) {
@@ -1241,7 +1258,7 @@ getJasmineRequireObj().Env = function(j$) {
ensureIsNotNested('xdescribe');
ensureIsFunction(specDefinitions, 'xdescribe');
var suite = suiteFactory(description);
suite.pend();
suite.exclude();
addSpecsToSuite(suite, specDefinitions);
return j$.deprecatingSuiteProxy(suite, suite.parentSuite, this);
};
@@ -1327,6 +1344,7 @@ getJasmineRequireObj().Env = function(j$) {
timeout: timeout || 0
},
throwOnExpectationFailure: config.oneFailurePerSpec,
autoCleanClosures: config.autoCleanClosures,
timer: new j$.Timer()
});
return spec;
@@ -1362,8 +1380,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);
@@ -1383,7 +1401,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 j$.deprecatingSpecProxy(spec, this);
};

View File

@@ -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() {
@@ -54,7 +56,7 @@ getJasmineRequireObj().Spec = function(j$) {
this.timer = attrs.timer || new j$.Timer();
if (!this.queueableFn.fn) {
this.pend();
this.exclude();
}
/**
@@ -122,7 +124,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);
@@ -176,6 +180,36 @@ 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}
* @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));
@@ -199,6 +233,10 @@ x */
);
};
/*
* Marks state as pending
* @param {string} [message] An optional reason message
*/
Spec.prototype.pend = function(message) {
this.markedPending = true;
if (message) {
@@ -206,6 +244,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;

View File

@@ -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) {