Move spec execution from Spec to TreeRunner
This commit is contained in:
@@ -774,7 +774,6 @@ getJasmineRequireObj().Spec = function(j$) {
|
|||||||
function Spec(attrs) {
|
function Spec(attrs) {
|
||||||
this.expectationFactory = attrs.expectationFactory;
|
this.expectationFactory = attrs.expectationFactory;
|
||||||
this.asyncExpectationFactory = attrs.asyncExpectationFactory;
|
this.asyncExpectationFactory = attrs.asyncExpectationFactory;
|
||||||
this.setTimeout = attrs.setTimeout;
|
|
||||||
this.id = attrs.id;
|
this.id = attrs.id;
|
||||||
this.filename = attrs.filename;
|
this.filename = attrs.filename;
|
||||||
this.parentSuiteId = attrs.parentSuiteId;
|
this.parentSuiteId = attrs.parentSuiteId;
|
||||||
@@ -858,87 +857,6 @@ getJasmineRequireObj().Spec = function(j$) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Spec.prototype.execute = function(
|
|
||||||
runQueue,
|
|
||||||
globalErrors,
|
|
||||||
onStart,
|
|
||||||
// TODO: may be able to merge resultCallback into onComplete
|
|
||||||
resultCallback,
|
|
||||||
onComplete,
|
|
||||||
excluded,
|
|
||||||
failSpecWithNoExp,
|
|
||||||
detectLateRejectionHandling
|
|
||||||
) {
|
|
||||||
const start = {
|
|
||||||
fn: done => {
|
|
||||||
this.executionStarted();
|
|
||||||
onStart(done);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const complete = {
|
|
||||||
fn: done => {
|
|
||||||
this.executionFinished(excluded, failSpecWithNoExp);
|
|
||||||
resultCallback(this.result, done);
|
|
||||||
},
|
|
||||||
type: 'specCleanup'
|
|
||||||
};
|
|
||||||
|
|
||||||
const fns = this.beforeAndAfterFns();
|
|
||||||
|
|
||||||
const runnerConfig = {
|
|
||||||
isLeaf: true,
|
|
||||||
queueableFns: [...fns.befores, this.queueableFn, ...fns.afters],
|
|
||||||
onException: e => this.handleException(e),
|
|
||||||
onMultipleDone: () => {
|
|
||||||
// Issue a deprecation. Include the context ourselves and pass
|
|
||||||
// ignoreRunnable: true, since getting here always means that we've already
|
|
||||||
// moved on and the current runnable isn't the one that caused the problem.
|
|
||||||
this.onLateError(
|
|
||||||
new Error(
|
|
||||||
'An asynchronous spec, beforeEach, or afterEach function called its ' +
|
|
||||||
"'done' callback more than once.\n(in spec: " +
|
|
||||||
this.getFullName() +
|
|
||||||
')'
|
|
||||||
)
|
|
||||||
);
|
|
||||||
},
|
|
||||||
onComplete: () => {
|
|
||||||
if (this.result.status === 'failed') {
|
|
||||||
onComplete(new j$.StopExecutionError('spec failed'));
|
|
||||||
} else {
|
|
||||||
onComplete();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
userContext: this.userContext(),
|
|
||||||
runnableName: this.getFullName.bind(this)
|
|
||||||
};
|
|
||||||
|
|
||||||
if (this.markedPending || excluded === true) {
|
|
||||||
runnerConfig.queueableFns = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
runnerConfig.queueableFns.unshift(start);
|
|
||||||
|
|
||||||
if (detectLateRejectionHandling) {
|
|
||||||
// Conditional because the setTimeout imposes a significant performance
|
|
||||||
// penalty in suites with lots of fast specs.
|
|
||||||
runnerConfig.queueableFns.push({
|
|
||||||
fn: done => {
|
|
||||||
// setTimeout is necessary to trigger rejectionhandled events
|
|
||||||
// TODO: let clearStack know about this so it doesn't do redundant setTimeouts
|
|
||||||
this.setTimeout(function() {
|
|
||||||
globalErrors.reportUnhandledRejections();
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
runnerConfig.queueableFns.push(complete);
|
|
||||||
|
|
||||||
runQueue(runnerConfig);
|
|
||||||
};
|
|
||||||
|
|
||||||
Spec.prototype.reset = function() {
|
Spec.prototype.reset = function() {
|
||||||
/**
|
/**
|
||||||
* @typedef SpecResult
|
* @typedef SpecResult
|
||||||
@@ -1027,6 +945,8 @@ getJasmineRequireObj().Spec = function(j$) {
|
|||||||
this.pend(message);
|
this.pend(message);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// TODO: ensure that all access to result goes through .getResult()
|
||||||
|
// so that the status is correct.
|
||||||
Spec.prototype.getResult = function() {
|
Spec.prototype.getResult = function() {
|
||||||
this.result.status = this.status();
|
this.result.status = this.status();
|
||||||
return this.result;
|
return this.result;
|
||||||
@@ -11151,7 +11071,6 @@ getJasmineRequireObj().SuiteBuilder = function(j$) {
|
|||||||
const config = this.env_.configuration();
|
const config = this.env_.configuration();
|
||||||
const suite = this.currentDeclarationSuite_;
|
const suite = this.currentDeclarationSuite_;
|
||||||
const parentSuiteId = suite === this.topSuite ? null : suite.id;
|
const parentSuiteId = suite === this.topSuite ? null : suite.id;
|
||||||
const global = j$.getGlobal();
|
|
||||||
const spec = new j$.Spec({
|
const spec = new j$.Spec({
|
||||||
id: 'spec' + this.nextSpecId_++,
|
id: 'spec' + this.nextSpecId_++,
|
||||||
filename,
|
filename,
|
||||||
@@ -11159,7 +11078,6 @@ getJasmineRequireObj().SuiteBuilder = function(j$) {
|
|||||||
beforeAndAfterFns: beforeAndAfterFns(suite),
|
beforeAndAfterFns: beforeAndAfterFns(suite),
|
||||||
expectationFactory: this.expectationFactory_,
|
expectationFactory: this.expectationFactory_,
|
||||||
asyncExpectationFactory: this.specAsyncExpectationFactory_,
|
asyncExpectationFactory: this.specAsyncExpectationFactory_,
|
||||||
setTimeout: global.setTimeout.bind(global),
|
|
||||||
onLateError: this.onLateError_,
|
onLateError: this.onLateError_,
|
||||||
getPath: spec => this.getSpecPath_(spec, suite),
|
getPath: spec => this.getSpecPath_(spec, suite),
|
||||||
description: description,
|
description: description,
|
||||||
@@ -11518,6 +11436,7 @@ getJasmineRequireObj().TreeProcessor = function(j$) {
|
|||||||
getJasmineRequireObj().TreeRunner = function(j$) {
|
getJasmineRequireObj().TreeRunner = function(j$) {
|
||||||
class TreeRunner {
|
class TreeRunner {
|
||||||
#executionTree;
|
#executionTree;
|
||||||
|
#setTimeout;
|
||||||
#globalErrors;
|
#globalErrors;
|
||||||
#runableResources;
|
#runableResources;
|
||||||
#reportDispatcher;
|
#reportDispatcher;
|
||||||
@@ -11530,6 +11449,7 @@ getJasmineRequireObj().TreeRunner = function(j$) {
|
|||||||
constructor(attrs) {
|
constructor(attrs) {
|
||||||
this.#executionTree = attrs.executionTree;
|
this.#executionTree = attrs.executionTree;
|
||||||
this.#globalErrors = attrs.globalErrors;
|
this.#globalErrors = attrs.globalErrors;
|
||||||
|
this.#setTimeout = attrs.setTimeout || setTimeout.bind(globalThis);
|
||||||
this.#runableResources = attrs.runableResources;
|
this.#runableResources = attrs.runableResources;
|
||||||
this.#reportDispatcher = attrs.reportDispatcher;
|
this.#reportDispatcher = attrs.reportDispatcher;
|
||||||
this.#runQueue = attrs.runQueue;
|
this.#runQueue = attrs.runQueue;
|
||||||
@@ -11574,31 +11494,103 @@ getJasmineRequireObj().TreeRunner = function(j$) {
|
|||||||
if (node.suite) {
|
if (node.suite) {
|
||||||
this.#executeSuiteSegment(node.suite, node.segmentNumber, done);
|
this.#executeSuiteSegment(node.suite, node.segmentNumber, done);
|
||||||
} else {
|
} else {
|
||||||
this.#executeSpec(node.spec, done);
|
this._executeSpec(node.spec, done);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
#executeSpec(spec, done) {
|
// Only exposed for testing.
|
||||||
const config = this.#getConfig();
|
_executeSpec(spec, specOverallDone) {
|
||||||
spec.execute(
|
const onStart = next => {
|
||||||
this.#runQueueWithSkipPolicy.bind(this),
|
this.#currentRunableTracker.setCurrentSpec(spec);
|
||||||
this.#globalErrors,
|
this.#runableResources.initForRunable(spec.id, spec.parentSuiteId);
|
||||||
next => {
|
this.#reportDispatcher.specStarted(spec.result).then(next);
|
||||||
this.#currentRunableTracker.setCurrentSpec(spec);
|
};
|
||||||
this.#runableResources.initForRunable(spec.id, spec.parentSuiteId);
|
const resultCallback = (result, next) => {
|
||||||
this.#reportDispatcher.specStarted(spec.result).then(next);
|
this.#specComplete(spec).then(next);
|
||||||
},
|
};
|
||||||
(result, next) => {
|
const queueableFns = this.#specQueueableFns(
|
||||||
this.#specComplete(spec).then(next);
|
spec,
|
||||||
},
|
onStart,
|
||||||
done,
|
resultCallback
|
||||||
this.#executionTree.isExcluded(spec),
|
|
||||||
config.failSpecWithNoExpectations,
|
|
||||||
config.detectLateRejectionHandling
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
this.#runQueueWithSkipPolicy({
|
||||||
|
isLeaf: true,
|
||||||
|
queueableFns,
|
||||||
|
onException: e => spec.handleException(e),
|
||||||
|
onMultipleDone: () => {
|
||||||
|
// Issue an erorr. Include the context ourselves and pass
|
||||||
|
// ignoreRunnable: true, since getting here always means that we've already
|
||||||
|
// moved on and the current runnable isn't the one that caused the problem.
|
||||||
|
spec.onLateError(
|
||||||
|
new Error(
|
||||||
|
'An asynchronous spec, beforeEach, or afterEach function called its ' +
|
||||||
|
"'done' callback more than once.\n(in spec: " +
|
||||||
|
spec.getFullName() +
|
||||||
|
')'
|
||||||
|
)
|
||||||
|
);
|
||||||
|
},
|
||||||
|
onComplete: () => {
|
||||||
|
if (spec.result.status === 'failed') {
|
||||||
|
specOverallDone(new j$.StopExecutionError('spec failed'));
|
||||||
|
} else {
|
||||||
|
specOverallDone();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
userContext: spec.userContext(),
|
||||||
|
runnableName: spec.getFullName.bind(spec)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#specQueueableFns(spec, onStart, resultCallback) {
|
||||||
|
const config = this.#getConfig();
|
||||||
|
const excluded = this.#executionTree.isExcluded(spec);
|
||||||
|
const ba = spec.beforeAndAfterFns();
|
||||||
|
let fns = [...ba.befores, spec.queueableFn, ...ba.afters];
|
||||||
|
|
||||||
|
if (spec.markedPending || excluded === true) {
|
||||||
|
fns = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
const start = {
|
||||||
|
fn(done) {
|
||||||
|
spec.executionStarted();
|
||||||
|
onStart(done);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const complete = {
|
||||||
|
fn(done) {
|
||||||
|
spec.executionFinished(excluded, config.failSpecWithNoExpectations);
|
||||||
|
resultCallback(spec.result, done);
|
||||||
|
},
|
||||||
|
type: 'specCleanup'
|
||||||
|
};
|
||||||
|
|
||||||
|
fns.unshift(start);
|
||||||
|
|
||||||
|
if (config.detectLateRejectionHandling) {
|
||||||
|
// Conditional because the setTimeout imposes a significant performance
|
||||||
|
// penalty in suites with lots of fast specs.
|
||||||
|
const globalErrors = this.#globalErrors;
|
||||||
|
fns.push({
|
||||||
|
fn: done => {
|
||||||
|
// setTimeout is necessary to trigger rejectionhandled events
|
||||||
|
// TODO: let clearStack know about this so it doesn't do redundant setTimeouts
|
||||||
|
this.#setTimeout(function() {
|
||||||
|
globalErrors.reportUnhandledRejections();
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fns.push(complete);
|
||||||
|
return fns;
|
||||||
}
|
}
|
||||||
|
|
||||||
#executeSuiteSegment(suite, segmentNumber, done) {
|
#executeSuiteSegment(suite, segmentNumber, done) {
|
||||||
|
|||||||
@@ -224,7 +224,7 @@ describe('Runner', function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Integration with TreeProcessor', function() {
|
describe('Integration with TreeProcessor and TreeRunner', function() {
|
||||||
let suiteNumber,
|
let suiteNumber,
|
||||||
specNumber,
|
specNumber,
|
||||||
runQueue,
|
runQueue,
|
||||||
@@ -250,6 +250,8 @@ describe('Runner', function() {
|
|||||||
// Reasonable defaults, may be overridden in some cases
|
// Reasonable defaults, may be overridden in some cases
|
||||||
failSpecWithNoExpectations = false;
|
failSpecWithNoExpectations = false;
|
||||||
detectLateRejectionHandling = false;
|
detectLateRejectionHandling = false;
|
||||||
|
|
||||||
|
spyOn(jasmineUnderTest.TreeRunner.prototype, '_executeSpec');
|
||||||
});
|
});
|
||||||
|
|
||||||
function StubSuite(attrs) {
|
function StubSuite(attrs) {
|
||||||
@@ -280,6 +282,10 @@ describe('Runner', function() {
|
|||||||
this.id = 'spec' + specNumber++;
|
this.id = 'spec' + specNumber++;
|
||||||
this.markedPending = attrs.markedPending || false;
|
this.markedPending = attrs.markedPending || false;
|
||||||
this.execute = jasmine.createSpy(this.id + '#execute');
|
this.execute = jasmine.createSpy(this.id + '#execute');
|
||||||
|
this.beforeAndAfterFns = () => ({ befores: [], afters: [] });
|
||||||
|
this.userContext = () => ({});
|
||||||
|
this.getFullName = () => '';
|
||||||
|
this.queueableFn = () => {};
|
||||||
}
|
}
|
||||||
|
|
||||||
function makeRunner(topSuite) {
|
function makeRunner(topSuite) {
|
||||||
@@ -306,24 +312,41 @@ describe('Runner', function() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function arrayNotContaining(item) {
|
||||||
|
return {
|
||||||
|
asymmetricMatch(other, matchersUtil) {
|
||||||
|
if (!jasmine.isArray_(other)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const x of other) {
|
||||||
|
if (matchersUtil.equals(x, item)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Precondition: jasmineUnderTest.TreeRunner.prototype._executeSpec is a spy
|
||||||
function verifyAndFinishSpec(spec, queueableFn, shouldBeExcluded) {
|
function verifyAndFinishSpec(spec, queueableFn, shouldBeExcluded) {
|
||||||
|
const ex = jasmineUnderTest.TreeRunner.prototype._executeSpec;
|
||||||
|
ex.withArgs(spec, 'onComplete').and.callThrough();
|
||||||
|
|
||||||
queueableFn.fn('onComplete');
|
queueableFn.fn('onComplete');
|
||||||
expect(spec.execute).toHaveBeenCalledWith(
|
expect(ex).toHaveBeenCalledWith(spec, 'onComplete');
|
||||||
jasmine.any(Function),
|
|
||||||
globalErrors,
|
expect(runQueue).toHaveBeenCalledWith(
|
||||||
jasmine.any(Function),
|
jasmine.objectContaining({
|
||||||
jasmine.any(Function),
|
isLeaf: true,
|
||||||
'onComplete',
|
SkipPolicy: jasmineUnderTest.CompleteOnFirstErrorSkipPolicy,
|
||||||
shouldBeExcluded,
|
queueableFns: shouldBeExcluded
|
||||||
failSpecWithNoExpectations,
|
? arrayNotContaining(spec.queueableFn)
|
||||||
detectLateRejectionHandling
|
: jasmine.arrayContaining([spec.queueableFn])
|
||||||
|
})
|
||||||
);
|
);
|
||||||
spec.execute.calls.mostRecent().args[0]({ for: spec.id, isLeaf: true });
|
|
||||||
expect(runQueue).toHaveBeenCalledWith({
|
|
||||||
for: spec.id,
|
|
||||||
isLeaf: true,
|
|
||||||
SkipPolicy: jasmineUnderTest.CompleteOnFirstErrorSkipPolicy
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
it('runs a single spec', async function() {
|
it('runs a single spec', async function() {
|
||||||
@@ -467,16 +490,9 @@ describe('Runner', function() {
|
|||||||
expect(queueableFns.length).toBe(2);
|
expect(queueableFns.length).toBe(2);
|
||||||
|
|
||||||
queueableFns[1].fn('foo');
|
queueableFns[1].fn('foo');
|
||||||
expect(spec.execute).toHaveBeenCalledWith(
|
expect(
|
||||||
jasmine.any(Function),
|
jasmineUnderTest.TreeRunner.prototype._executeSpec
|
||||||
globalErrors,
|
).toHaveBeenCalledWith(spec, 'foo');
|
||||||
jasmine.any(Function),
|
|
||||||
jasmine.any(Function),
|
|
||||||
'foo',
|
|
||||||
false,
|
|
||||||
true,
|
|
||||||
detectLateRejectionHandling
|
|
||||||
);
|
|
||||||
|
|
||||||
await expectAsync(promise).toBePending();
|
await expectAsync(promise).toBePending();
|
||||||
});
|
});
|
||||||
@@ -568,14 +584,20 @@ describe('Runner', function() {
|
|||||||
await Promise.resolve();
|
await Promise.resolve();
|
||||||
expect(runQueue).toHaveBeenCalledTimes(1);
|
expect(runQueue).toHaveBeenCalledTimes(1);
|
||||||
const queueableFns = runQueue.calls.mostRecent().args[0].queueableFns;
|
const queueableFns = runQueue.calls.mostRecent().args[0].queueableFns;
|
||||||
queueableFns[0].fn();
|
queueableFns[0].fn('done');
|
||||||
|
|
||||||
expect(specs[0].execute).not.toHaveBeenCalled();
|
expect(
|
||||||
expect(specs[1].execute).toHaveBeenCalled();
|
jasmineUnderTest.TreeRunner.prototype._executeSpec
|
||||||
|
).not.toHaveBeenCalledWith(specs[0], jasmine.anything());
|
||||||
|
expect(
|
||||||
|
jasmineUnderTest.TreeRunner.prototype._executeSpec
|
||||||
|
).toHaveBeenCalledWith(specs[1], 'done');
|
||||||
|
|
||||||
queueableFns[1].fn();
|
queueableFns[1].fn('done');
|
||||||
|
|
||||||
expect(specs[0].execute).toHaveBeenCalled();
|
expect(
|
||||||
|
jasmineUnderTest.TreeRunner.prototype._executeSpec
|
||||||
|
).toHaveBeenCalledWith(specs[0], 'done');
|
||||||
|
|
||||||
await expectAsync(promise).toBePending();
|
await expectAsync(promise).toBePending();
|
||||||
});
|
});
|
||||||
@@ -590,32 +612,20 @@ describe('Runner', function() {
|
|||||||
await Promise.resolve();
|
await Promise.resolve();
|
||||||
expect(runQueue).toHaveBeenCalledTimes(1);
|
expect(runQueue).toHaveBeenCalledTimes(1);
|
||||||
const queueableFns = runQueue.calls.mostRecent().args[0].queueableFns;
|
const queueableFns = runQueue.calls.mostRecent().args[0].queueableFns;
|
||||||
queueableFns[0].fn();
|
queueableFns[0].fn('done');
|
||||||
|
|
||||||
expect(nonSpecified.execute).not.toHaveBeenCalled();
|
expect(
|
||||||
expect(specified.execute).toHaveBeenCalledWith(
|
jasmineUnderTest.TreeRunner.prototype._executeSpec
|
||||||
jasmine.any(Function),
|
).not.toHaveBeenCalledWith(nonSpecified, jasmine.anything());
|
||||||
globalErrors,
|
expect(
|
||||||
jasmine.any(Function),
|
jasmineUnderTest.TreeRunner.prototype._executeSpec
|
||||||
jasmine.any(Function),
|
).toHaveBeenCalledWith(specified, 'done');
|
||||||
undefined,
|
|
||||||
false,
|
|
||||||
false,
|
|
||||||
detectLateRejectionHandling
|
|
||||||
);
|
|
||||||
|
|
||||||
queueableFns[1].fn();
|
queueableFns[1].fn('done');
|
||||||
|
|
||||||
expect(nonSpecified.execute).toHaveBeenCalledWith(
|
expect(
|
||||||
jasmine.any(Function),
|
jasmineUnderTest.TreeRunner.prototype._executeSpec
|
||||||
globalErrors,
|
).toHaveBeenCalledWith(nonSpecified, 'done');
|
||||||
jasmine.any(Function),
|
|
||||||
jasmine.any(Function),
|
|
||||||
undefined,
|
|
||||||
true,
|
|
||||||
false,
|
|
||||||
detectLateRejectionHandling
|
|
||||||
);
|
|
||||||
|
|
||||||
await expectAsync(promise).toBePending();
|
await expectAsync(promise).toBePending();
|
||||||
});
|
});
|
||||||
@@ -637,11 +647,15 @@ describe('Runner', function() {
|
|||||||
|
|
||||||
expect(specifiedSpec.execute).not.toHaveBeenCalled();
|
expect(specifiedSpec.execute).not.toHaveBeenCalled();
|
||||||
const nodeQueueableFns = runQueue.calls.mostRecent().args[0].queueableFns;
|
const nodeQueueableFns = runQueue.calls.mostRecent().args[0].queueableFns;
|
||||||
nodeQueueableFns[1].fn();
|
nodeQueueableFns[1].fn('done');
|
||||||
expect(nonSpecifiedSpec.execute).toHaveBeenCalled();
|
expect(
|
||||||
|
jasmineUnderTest.TreeRunner.prototype._executeSpec
|
||||||
|
).toHaveBeenCalledWith(nonSpecifiedSpec, 'done');
|
||||||
|
|
||||||
queueableFns[1].fn();
|
queueableFns[1].fn('done');
|
||||||
expect(specifiedSpec.execute).toHaveBeenCalled();
|
expect(
|
||||||
|
jasmineUnderTest.TreeRunner.prototype._executeSpec
|
||||||
|
).toHaveBeenCalledWith(specifiedSpec, 'done');
|
||||||
|
|
||||||
await expectAsync(promise).toBePending();
|
await expectAsync(promise).toBePending();
|
||||||
});
|
});
|
||||||
@@ -660,17 +674,23 @@ describe('Runner', function() {
|
|||||||
const queueableFns = runQueue.calls.mostRecent().args[0].queueableFns;
|
const queueableFns = runQueue.calls.mostRecent().args[0].queueableFns;
|
||||||
expect(queueableFns.length).toBe(2);
|
expect(queueableFns.length).toBe(2);
|
||||||
|
|
||||||
queueableFns[0].fn();
|
queueableFns[0].fn('done');
|
||||||
expect(spec1.execute).toHaveBeenCalled();
|
expect(
|
||||||
|
jasmineUnderTest.TreeRunner.prototype._executeSpec
|
||||||
|
).toHaveBeenCalledWith(spec1, 'done');
|
||||||
|
|
||||||
queueableFns[1].fn();
|
queueableFns[1].fn();
|
||||||
const childFns = runQueue.calls.mostRecent().args[0].queueableFns;
|
const childFns = runQueue.calls.mostRecent().args[0].queueableFns;
|
||||||
expect(childFns.length).toBe(3);
|
expect(childFns.length).toBe(3);
|
||||||
childFns[1].fn();
|
childFns[1].fn('done');
|
||||||
expect(spec2.execute).toHaveBeenCalled();
|
expect(
|
||||||
|
jasmineUnderTest.TreeRunner.prototype._executeSpec
|
||||||
|
).toHaveBeenCalledWith(spec2, 'done');
|
||||||
|
|
||||||
childFns[2].fn();
|
childFns[2].fn('done');
|
||||||
expect(spec3.execute).toHaveBeenCalled();
|
expect(
|
||||||
|
jasmineUnderTest.TreeRunner.prototype._executeSpec
|
||||||
|
).toHaveBeenCalledWith(spec3, 'done');
|
||||||
|
|
||||||
await expectAsync(promise).toBePending();
|
await expectAsync(promise).toBePending();
|
||||||
});
|
});
|
||||||
@@ -699,26 +719,36 @@ describe('Runner', function() {
|
|||||||
|
|
||||||
queueableFns[0].fn();
|
queueableFns[0].fn();
|
||||||
expect(runQueue.calls.mostRecent().args[0].queueableFns.length).toBe(2);
|
expect(runQueue.calls.mostRecent().args[0].queueableFns.length).toBe(2);
|
||||||
runQueue.calls.mostRecent().args[0].queueableFns[1].fn();
|
runQueue.calls.mostRecent().args[0].queueableFns[1].fn('done');
|
||||||
expect(spec1.execute).toHaveBeenCalled();
|
expect(
|
||||||
|
jasmineUnderTest.TreeRunner.prototype._executeSpec
|
||||||
|
).toHaveBeenCalledWith(spec1, 'done');
|
||||||
|
|
||||||
queueableFns[1].fn();
|
queueableFns[1].fn('done');
|
||||||
expect(spec4.execute).toHaveBeenCalled();
|
expect(
|
||||||
|
jasmineUnderTest.TreeRunner.prototype._executeSpec
|
||||||
|
).toHaveBeenCalledWith(spec4, 'done');
|
||||||
|
|
||||||
queueableFns[2].fn();
|
queueableFns[2].fn();
|
||||||
expect(runQueue.calls.count()).toBe(3);
|
expect(runQueue.calls.count()).toBe(3);
|
||||||
expect(runQueue.calls.mostRecent().args[0].queueableFns.length).toBe(2);
|
expect(runQueue.calls.mostRecent().args[0].queueableFns.length).toBe(2);
|
||||||
runQueue.calls.mostRecent().args[0].queueableFns[1].fn();
|
runQueue.calls.mostRecent().args[0].queueableFns[1].fn('done');
|
||||||
expect(spec2.execute).toHaveBeenCalled();
|
expect(
|
||||||
|
jasmineUnderTest.TreeRunner.prototype._executeSpec
|
||||||
|
).toHaveBeenCalledWith(spec2, 'done');
|
||||||
|
|
||||||
queueableFns[3].fn();
|
queueableFns[3].fn('done');
|
||||||
expect(spec5.execute).toHaveBeenCalled();
|
expect(
|
||||||
|
jasmineUnderTest.TreeRunner.prototype._executeSpec
|
||||||
|
).toHaveBeenCalledWith(spec5, 'done');
|
||||||
|
|
||||||
queueableFns[4].fn();
|
queueableFns[4].fn();
|
||||||
expect(runQueue.calls.count()).toBe(4);
|
expect(runQueue.calls.count()).toBe(4);
|
||||||
expect(runQueue.calls.mostRecent().args[0].queueableFns.length).toBe(2);
|
expect(runQueue.calls.mostRecent().args[0].queueableFns.length).toBe(2);
|
||||||
runQueue.calls.mostRecent().args[0].queueableFns[1].fn();
|
runQueue.calls.mostRecent().args[0].queueableFns[1].fn('done');
|
||||||
expect(spec3.execute).toHaveBeenCalled();
|
expect(
|
||||||
|
jasmineUnderTest.TreeRunner.prototype._executeSpec
|
||||||
|
).toHaveBeenCalledWith(spec3, 'done');
|
||||||
|
|
||||||
await expectAsync(promise).toBePending();
|
await expectAsync(promise).toBePending();
|
||||||
});
|
});
|
||||||
@@ -754,11 +784,15 @@ describe('Runner', function() {
|
|||||||
runQueue.calls.mostRecent().args[0].queueableFns[1].fn();
|
runQueue.calls.mostRecent().args[0].queueableFns[1].fn();
|
||||||
expect(runQueue.calls.count()).toBe(3);
|
expect(runQueue.calls.count()).toBe(3);
|
||||||
|
|
||||||
runQueue.calls.mostRecent().args[0].queueableFns[1].fn();
|
runQueue.calls.mostRecent().args[0].queueableFns[1].fn('done');
|
||||||
expect(spec1.execute).toHaveBeenCalled();
|
expect(
|
||||||
|
jasmineUnderTest.TreeRunner.prototype._executeSpec
|
||||||
|
).toHaveBeenCalledWith(spec1, 'done');
|
||||||
|
|
||||||
queueableFns[1].fn();
|
queueableFns[1].fn('done');
|
||||||
expect(spec4.execute).toHaveBeenCalled();
|
expect(
|
||||||
|
jasmineUnderTest.TreeRunner.prototype._executeSpec
|
||||||
|
).toHaveBeenCalledWith(spec4, 'done');
|
||||||
|
|
||||||
queueableFns[2].fn();
|
queueableFns[2].fn();
|
||||||
expect(runQueue.calls.count()).toBe(4);
|
expect(runQueue.calls.count()).toBe(4);
|
||||||
@@ -767,11 +801,15 @@ describe('Runner', function() {
|
|||||||
runQueue.calls.mostRecent().args[0].queueableFns[1].fn();
|
runQueue.calls.mostRecent().args[0].queueableFns[1].fn();
|
||||||
expect(runQueue.calls.count()).toBe(5);
|
expect(runQueue.calls.count()).toBe(5);
|
||||||
|
|
||||||
runQueue.calls.mostRecent().args[0].queueableFns[1].fn();
|
runQueue.calls.mostRecent().args[0].queueableFns[1].fn('done');
|
||||||
expect(spec2.execute).toHaveBeenCalled();
|
expect(
|
||||||
|
jasmineUnderTest.TreeRunner.prototype._executeSpec
|
||||||
|
).toHaveBeenCalledWith(spec2, 'done');
|
||||||
|
|
||||||
queueableFns[3].fn();
|
queueableFns[3].fn('done');
|
||||||
expect(spec5.execute).toHaveBeenCalled();
|
expect(
|
||||||
|
jasmineUnderTest.TreeRunner.prototype._executeSpec
|
||||||
|
).toHaveBeenCalledWith(spec5, 'done');
|
||||||
|
|
||||||
queueableFns[4].fn();
|
queueableFns[4].fn();
|
||||||
expect(runQueue.calls.count()).toBe(6);
|
expect(runQueue.calls.count()).toBe(6);
|
||||||
@@ -780,8 +818,10 @@ describe('Runner', function() {
|
|||||||
runQueue.calls.mostRecent().args[0].queueableFns[1].fn();
|
runQueue.calls.mostRecent().args[0].queueableFns[1].fn();
|
||||||
expect(runQueue.calls.count()).toBe(7);
|
expect(runQueue.calls.count()).toBe(7);
|
||||||
|
|
||||||
runQueue.calls.mostRecent().args[0].queueableFns[1].fn();
|
runQueue.calls.mostRecent().args[0].queueableFns[1].fn('done');
|
||||||
expect(spec3.execute).toHaveBeenCalled();
|
expect(
|
||||||
|
jasmineUnderTest.TreeRunner.prototype._executeSpec
|
||||||
|
).toHaveBeenCalledWith(spec3, 'done');
|
||||||
|
|
||||||
await expectAsync(promise).toBePending();
|
await expectAsync(promise).toBePending();
|
||||||
});
|
});
|
||||||
@@ -803,8 +843,10 @@ describe('Runner', function() {
|
|||||||
expect(queueableFns.length).toBe(11);
|
expect(queueableFns.length).toBe(11);
|
||||||
|
|
||||||
for (let i = 0; i < 11; i++) {
|
for (let i = 0; i < 11; i++) {
|
||||||
queueableFns[i].fn();
|
queueableFns[i].fn('done');
|
||||||
expect(specs[i].execute).toHaveBeenCalled();
|
expect(
|
||||||
|
jasmineUnderTest.TreeRunner.prototype._executeSpec
|
||||||
|
).toHaveBeenCalledWith(specs[i], 'done');
|
||||||
}
|
}
|
||||||
|
|
||||||
await expectAsync(promise).toBePending();
|
await expectAsync(promise).toBePending();
|
||||||
|
|||||||
@@ -33,170 +33,6 @@ describe('Spec', function() {
|
|||||||
expect(jasmineUnderTest.Spec.isPendingSpecException(void 0)).toBe(false);
|
expect(jasmineUnderTest.Spec.isPendingSpecException(void 0)).toBe(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('delegates execution to a QueueRunner', function() {
|
|
||||||
const fakeQueueRunner = jasmine.createSpy('fakeQueueRunner'),
|
|
||||||
spec = new jasmineUnderTest.Spec({
|
|
||||||
description: 'my test',
|
|
||||||
id: 'some-id',
|
|
||||||
queueableFn: { fn: function() {} }
|
|
||||||
});
|
|
||||||
|
|
||||||
spec.execute(fakeQueueRunner);
|
|
||||||
|
|
||||||
expect(fakeQueueRunner).toHaveBeenCalled();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should call the start callback on execution', function() {
|
|
||||||
const fakeQueueRunner = jasmine.createSpy('fakeQueueRunner'),
|
|
||||||
startCallback = jasmine.createSpy('startCallback'),
|
|
||||||
spec = new jasmineUnderTest.Spec({
|
|
||||||
id: 123,
|
|
||||||
description: 'foo bar',
|
|
||||||
queueableFn: { fn: function() {} }
|
|
||||||
});
|
|
||||||
|
|
||||||
spec.execute(fakeQueueRunner, null, startCallback);
|
|
||||||
|
|
||||||
fakeQueueRunner.calls.mostRecent().args[0].queueableFns[0].fn();
|
|
||||||
expect(startCallback).toHaveBeenCalled();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should call the start callback on execution but before any befores are called', function() {
|
|
||||||
const fakeQueueRunner = jasmine.createSpy('fakeQueueRunner');
|
|
||||||
let beforesWereCalled = false;
|
|
||||||
const startCallback = jasmine
|
|
||||||
.createSpy('start-callback')
|
|
||||||
.and.callFake(function() {
|
|
||||||
expect(beforesWereCalled).toBe(false);
|
|
||||||
});
|
|
||||||
const spec = new jasmineUnderTest.Spec({
|
|
||||||
queueableFn: { fn: function() {} },
|
|
||||||
beforeFns: function() {
|
|
||||||
return [
|
|
||||||
function() {
|
|
||||||
beforesWereCalled = true;
|
|
||||||
}
|
|
||||||
];
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
spec.execute(fakeQueueRunner, null, startCallback);
|
|
||||||
|
|
||||||
fakeQueueRunner.calls.mostRecent().args[0].queueableFns[0].fn();
|
|
||||||
expect(startCallback).toHaveBeenCalled();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('provides all before fns and after fns to be run', function() {
|
|
||||||
const fakeQueueRunner = jasmine.createSpy('fakeQueueRunner'),
|
|
||||||
before = jasmine.createSpy('before'),
|
|
||||||
after = jasmine.createSpy('after'),
|
|
||||||
queueableFn = {
|
|
||||||
fn: jasmine.createSpy('test body').and.callFake(function() {
|
|
||||||
expect(before).toHaveBeenCalled();
|
|
||||||
expect(after).not.toHaveBeenCalled();
|
|
||||||
})
|
|
||||||
},
|
|
||||||
spec = new jasmineUnderTest.Spec({
|
|
||||||
queueableFn: queueableFn,
|
|
||||||
beforeAndAfterFns: function() {
|
|
||||||
return { befores: [before], afters: [after] };
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
spec.execute(fakeQueueRunner, null, null);
|
|
||||||
|
|
||||||
const options = fakeQueueRunner.calls.mostRecent().args[0];
|
|
||||||
expect(options.queueableFns).toEqual([
|
|
||||||
{ fn: jasmine.any(Function) },
|
|
||||||
before,
|
|
||||||
queueableFn,
|
|
||||||
after,
|
|
||||||
{
|
|
||||||
fn: jasmine.any(Function),
|
|
||||||
type: 'specCleanup'
|
|
||||||
}
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('Late promise rejection handling', function() {
|
|
||||||
it('is enabled when the detectLateRejectionHandling param is true', function() {
|
|
||||||
const fakeQueueRunner = jasmine.createSpy('fakeQueueRunner');
|
|
||||||
const globalErrors = jasmine.createSpyObj('globalErrors', [
|
|
||||||
'reportUnhandledRejections'
|
|
||||||
]);
|
|
||||||
const setTimeout = jasmine.createSpy('setTimeout');
|
|
||||||
const before = jasmine.createSpy('before');
|
|
||||||
const after = jasmine.createSpy('after');
|
|
||||||
const queueableFn = {
|
|
||||||
fn: jasmine.createSpy('test body').and.callFake(function() {
|
|
||||||
expect(before).toHaveBeenCalled();
|
|
||||||
expect(after).not.toHaveBeenCalled();
|
|
||||||
})
|
|
||||||
};
|
|
||||||
const spec = new jasmineUnderTest.Spec({
|
|
||||||
queueableFn,
|
|
||||||
setTimeout,
|
|
||||||
beforeAndAfterFns: function() {
|
|
||||||
return { befores: [before], afters: [after] };
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
spec.execute(
|
|
||||||
fakeQueueRunner,
|
|
||||||
globalErrors,
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
false,
|
|
||||||
false,
|
|
||||||
true
|
|
||||||
);
|
|
||||||
|
|
||||||
const options = fakeQueueRunner.calls.mostRecent().args[0];
|
|
||||||
expect(options.queueableFns).toEqual([
|
|
||||||
{ fn: jasmine.any(Function) },
|
|
||||||
before,
|
|
||||||
queueableFn,
|
|
||||||
after,
|
|
||||||
{ fn: jasmine.any(Function) },
|
|
||||||
{
|
|
||||||
fn: jasmine.any(Function),
|
|
||||||
type: 'specCleanup'
|
|
||||||
}
|
|
||||||
]);
|
|
||||||
|
|
||||||
const done = jasmine.createSpy('done');
|
|
||||||
options.queueableFns[4].fn(done);
|
|
||||||
expect(globalErrors.reportUnhandledRejections).not.toHaveBeenCalled();
|
|
||||||
expect(done).not.toHaveBeenCalled();
|
|
||||||
|
|
||||||
expect(setTimeout).toHaveBeenCalledOnceWith(jasmine.any(Function));
|
|
||||||
setTimeout.calls.argsFor(0)[0]();
|
|
||||||
expect(globalErrors.reportUnhandledRejections).toHaveBeenCalled();
|
|
||||||
expect(globalErrors.reportUnhandledRejections).toHaveBeenCalledBefore(
|
|
||||||
done
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it("tells the queue runner that it's a leaf node", function() {
|
|
||||||
const fakeQueueRunner = jasmine.createSpy('fakeQueueRunner'),
|
|
||||||
spec = new jasmineUnderTest.Spec({
|
|
||||||
queueableFn: { fn: function() {} },
|
|
||||||
beforeAndAfterFns: function() {
|
|
||||||
return { befores: [], afters: [] };
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
spec.execute(fakeQueueRunner);
|
|
||||||
|
|
||||||
expect(fakeQueueRunner).toHaveBeenCalledWith(
|
|
||||||
jasmine.objectContaining({
|
|
||||||
isLeaf: true
|
|
||||||
})
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('is marked pending if created without a function body', function() {
|
it('is marked pending if created without a function body', function() {
|
||||||
const startCallback = jasmine.createSpy('startCallback'),
|
const startCallback = jasmine.createSpy('startCallback'),
|
||||||
resultCallback = jasmine.createSpy('resultCallback'),
|
resultCallback = jasmine.createSpy('resultCallback'),
|
||||||
@@ -209,234 +45,49 @@ describe('Spec', function() {
|
|||||||
expect(spec.status()).toBe('pending');
|
expect(spec.status()).toBe('pending');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('can be excluded at execution time by a parent', function() {
|
describe('#executionFinished', function() {
|
||||||
const fakeQueueRunner = jasmine.createSpy('fakeQueueRunner'),
|
it('removes the fn if autoCleanClosures is true', function() {
|
||||||
startCallback = jasmine.createSpy('startCallback'),
|
const spec = new jasmineUnderTest.Spec({
|
||||||
specBody = jasmine.createSpy('specBody'),
|
queueableFn: { fn: () => {} },
|
||||||
resultCallback = jasmine.createSpy('resultCallback'),
|
autoCleanClosures: true
|
||||||
spec = new jasmineUnderTest.Spec({
|
|
||||||
queueableFn: { fn: specBody }
|
|
||||||
});
|
});
|
||||||
|
|
||||||
spec.execute(
|
spec.executionFinished();
|
||||||
fakeQueueRunner,
|
expect(spec.queueableFn.fn).toBeFalsy();
|
||||||
null,
|
|
||||||
startCallback,
|
|
||||||
resultCallback,
|
|
||||||
'onComplete',
|
|
||||||
true
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(fakeQueueRunner).toHaveBeenCalledWith(
|
|
||||||
jasmine.objectContaining({
|
|
||||||
onComplete: jasmine.any(Function),
|
|
||||||
queueableFns: [
|
|
||||||
{ fn: jasmine.any(Function) },
|
|
||||||
{
|
|
||||||
fn: jasmine.any(Function),
|
|
||||||
type: 'specCleanup'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
})
|
|
||||||
);
|
|
||||||
expect(specBody).not.toHaveBeenCalled();
|
|
||||||
|
|
||||||
const args = fakeQueueRunner.calls.mostRecent().args[0];
|
|
||||||
args.queueableFns[0].fn();
|
|
||||||
expect(startCallback).toHaveBeenCalled();
|
|
||||||
args.queueableFns[args.queueableFns.length - 1].fn();
|
|
||||||
expect(resultCallback).toHaveBeenCalled();
|
|
||||||
|
|
||||||
expect(spec.result.status).toBe('excluded');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('can be marked pending, but still calls callbacks when executed', function() {
|
|
||||||
const fakeQueueRunner = jasmine.createSpy('fakeQueueRunner'),
|
|
||||||
startCallback = jasmine.createSpy('startCallback'),
|
|
||||||
resultCallback = jasmine.createSpy('resultCallback'),
|
|
||||||
spec = new jasmineUnderTest.Spec({
|
|
||||||
description: 'with a spec',
|
|
||||||
parentSuiteId: 'suite1',
|
|
||||||
filename: 'someSpecFile.js',
|
|
||||||
getPath: function() {
|
|
||||||
return ['a suite', 'with a spec'];
|
|
||||||
},
|
|
||||||
queueableFn: { fn: null }
|
|
||||||
});
|
|
||||||
|
|
||||||
spec.pend();
|
|
||||||
|
|
||||||
expect(spec.status()).toBe('pending');
|
|
||||||
|
|
||||||
spec.execute(fakeQueueRunner, null, startCallback, resultCallback);
|
|
||||||
|
|
||||||
expect(fakeQueueRunner).toHaveBeenCalled();
|
|
||||||
|
|
||||||
const args = fakeQueueRunner.calls.mostRecent().args[0];
|
|
||||||
args.queueableFns[0].fn();
|
|
||||||
expect(startCallback).toHaveBeenCalled();
|
|
||||||
args.queueableFns[1].fn('things');
|
|
||||||
expect(resultCallback).toHaveBeenCalledWith(
|
|
||||||
{
|
|
||||||
id: spec.id,
|
|
||||||
status: 'pending',
|
|
||||||
description: 'with a spec',
|
|
||||||
fullName: 'a suite with a spec',
|
|
||||||
parentSuiteId: 'suite1',
|
|
||||||
filename: 'someSpecFile.js',
|
|
||||||
failedExpectations: [],
|
|
||||||
passedExpectations: [],
|
|
||||||
deprecationWarnings: [],
|
|
||||||
pendingReason: '',
|
|
||||||
duration: jasmine.any(Number),
|
|
||||||
properties: null,
|
|
||||||
debugLogs: null
|
|
||||||
},
|
|
||||||
'things'
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should call the done callback on execution complete', function() {
|
|
||||||
const done = jasmine.createSpy('done callback'),
|
|
||||||
spec = new jasmineUnderTest.Spec({
|
|
||||||
queueableFn: { fn: function() {} },
|
|
||||||
catchExceptions: function() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
spec.execute(
|
|
||||||
attrs => attrs.onComplete(),
|
|
||||||
null,
|
|
||||||
function() {},
|
|
||||||
function() {},
|
|
||||||
done
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(done).toHaveBeenCalled();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should call the done callback with an error if the spec is failed', function() {
|
|
||||||
const done = jasmine.createSpy('done callback'),
|
|
||||||
spec = new jasmineUnderTest.Spec({
|
|
||||||
queueableFn: { fn: function() {} },
|
|
||||||
catchExceptions: function() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
function runQueue(attrs) {
|
|
||||||
spec.result.status = 'failed';
|
|
||||||
attrs.onComplete();
|
|
||||||
}
|
|
||||||
spec.execute(runQueue, null, function() {}, function() {}, done);
|
|
||||||
|
|
||||||
expect(done).toHaveBeenCalledWith(
|
|
||||||
jasmine.any(jasmineUnderTest.StopExecutionError)
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should report the duration of the test', function() {
|
|
||||||
const timer = jasmine.createSpyObj('timer', {
|
|
||||||
start: null,
|
|
||||||
elapsed: 77000
|
|
||||||
});
|
|
||||||
const resultCallback = jasmine.createSpy('resultCallback');
|
|
||||||
const spec = new jasmineUnderTest.Spec({
|
|
||||||
queueableFn: { fn: jasmine.createSpy('spec body') },
|
|
||||||
catchExceptions: function() {
|
|
||||||
return false;
|
|
||||||
},
|
|
||||||
timer: timer
|
|
||||||
});
|
});
|
||||||
|
|
||||||
function runQueue(config) {
|
it('removes the fn after execution if autoCleanClosures is undefined', function() {
|
||||||
config.queueableFns.forEach(function(qf) {
|
const spec = new jasmineUnderTest.Spec({
|
||||||
qf.fn();
|
queueableFn: { fn: () => {} },
|
||||||
|
autoCleanClosures: undefined
|
||||||
});
|
});
|
||||||
config.onComplete();
|
|
||||||
}
|
|
||||||
|
|
||||||
spec.execute(runQueue, null, function() {}, resultCallback, function() {});
|
spec.executionFinished();
|
||||||
expect(resultCallback).toHaveBeenCalled();
|
expect(spec.queueableFn.fn).toBeFalsy();
|
||||||
expect(resultCallback.calls.argsFor(0)[0].duration).toEqual(77000);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('removes the fn after execution if autoCleanClosures is true', function() {
|
|
||||||
const done = jasmine.createSpy('done callback');
|
|
||||||
const spec = new jasmineUnderTest.Spec({
|
|
||||||
queueableFn: { fn() {} },
|
|
||||||
autoCleanClosures: true
|
|
||||||
});
|
});
|
||||||
|
|
||||||
function runQueue(config) {
|
it('does not remove the fn after execution if autoCleanClosures is false', function() {
|
||||||
config.queueableFns.forEach(function(qf) {
|
function originalFn() {}
|
||||||
qf.fn();
|
const spec = new jasmineUnderTest.Spec({
|
||||||
|
queueableFn: { fn: originalFn },
|
||||||
|
autoCleanClosures: false
|
||||||
});
|
});
|
||||||
config.onComplete();
|
|
||||||
}
|
|
||||||
|
|
||||||
spec.execute(runQueue, null, function() {}, function() {}, done);
|
spec.executionFinished();
|
||||||
expect(done).toHaveBeenCalled();
|
expect(spec.queueableFn.fn).toBe(originalFn);
|
||||||
expect(spec.queueableFn.fn).toBeFalsy();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('removes the fn after execution if autoCleanClosures is undefined', function() {
|
|
||||||
const done = jasmine.createSpy('done callback');
|
|
||||||
const spec = new jasmineUnderTest.Spec({
|
|
||||||
queueableFn: { fn() {} },
|
|
||||||
autoCleanClosures: undefined
|
|
||||||
});
|
});
|
||||||
|
|
||||||
function runQueue(config) {
|
|
||||||
config.queueableFns.forEach(function(qf) {
|
|
||||||
qf.fn();
|
|
||||||
});
|
|
||||||
config.onComplete();
|
|
||||||
}
|
|
||||||
|
|
||||||
spec.execute(runQueue, null, function() {}, function() {}, done);
|
|
||||||
expect(done).toHaveBeenCalled();
|
|
||||||
expect(spec.queueableFn.fn).toBeFalsy();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('does not remove the fn after execution if autoCleanClosures is false', function() {
|
describe('#setSpecProperty', function() {
|
||||||
const done = jasmine.createSpy('done callback');
|
it('adds the property to the result', function() {
|
||||||
function originalFn() {}
|
const spec = new jasmineUnderTest.Spec({
|
||||||
const spec = new jasmineUnderTest.Spec({
|
queueableFn: { fn: () => {} }
|
||||||
queueableFn: { fn: originalFn },
|
});
|
||||||
autoCleanClosures: false
|
|
||||||
|
spec.setSpecProperty('a', 4);
|
||||||
|
|
||||||
|
expect(spec.result.properties).toEqual({ a: 4 });
|
||||||
});
|
});
|
||||||
|
|
||||||
function runQueue(config) {
|
|
||||||
config.queueableFns.forEach(function(qf) {
|
|
||||||
qf.fn();
|
|
||||||
});
|
|
||||||
config.onComplete();
|
|
||||||
}
|
|
||||||
|
|
||||||
spec.execute(runQueue, null, function() {}, function() {}, done);
|
|
||||||
expect(done).toHaveBeenCalled();
|
|
||||||
expect(spec.queueableFn.fn).toBe(originalFn);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should report properties set during the test', function() {
|
|
||||||
const done = jasmine.createSpy('done callback'),
|
|
||||||
spec = new jasmineUnderTest.Spec({
|
|
||||||
queueableFn: { fn: jasmine.createSpy('spec body') },
|
|
||||||
catchExceptions: function() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
spec.setSpecProperty('a', 4);
|
|
||||||
spec.execute(
|
|
||||||
attrs => attrs.onComplete(),
|
|
||||||
null,
|
|
||||||
function() {},
|
|
||||||
function() {},
|
|
||||||
done
|
|
||||||
);
|
|
||||||
expect(spec.result.properties).toEqual({ a: 4 });
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('#status returns passing by default', function() {
|
it('#status returns passing by default', function() {
|
||||||
@@ -446,69 +97,84 @@ describe('Spec', function() {
|
|||||||
expect(spec.status()).toBe('passed');
|
expect(spec.status()).toBe('passed');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('#status returns passed if all expectations in the spec have passed', function() {
|
describe('#status', function() {
|
||||||
const spec = new jasmineUnderTest.Spec({
|
it('returns "passed"" by default', function() {
|
||||||
queueableFn: { fn: jasmine.createSpy('spec body') }
|
const spec = new jasmineUnderTest.Spec({
|
||||||
});
|
queueableFn: { fn: () => {} }
|
||||||
spec.addExpectationResult(true, {});
|
|
||||||
expect(spec.status()).toBe('passed');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('#status returns failed if any expectations in the spec have failed', function() {
|
|
||||||
const spec = new jasmineUnderTest.Spec({
|
|
||||||
queueableFn: { fn: jasmine.createSpy('spec body') }
|
|
||||||
});
|
|
||||||
spec.addExpectationResult(true, {});
|
|
||||||
spec.addExpectationResult(false, {});
|
|
||||||
expect(spec.status()).toBe('failed');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('keeps track of passed and failed expectations', function() {
|
|
||||||
const fakeQueueRunner = jasmine.createSpy('queueRunner'),
|
|
||||||
resultCallback = jasmine.createSpy('resultCallback'),
|
|
||||||
spec = new jasmineUnderTest.Spec({
|
|
||||||
queueableFn: { fn: jasmine.createSpy('spec body') }
|
|
||||||
});
|
});
|
||||||
spec.addExpectationResult(true, { message: 'expectation1' });
|
expect(spec.status()).toBe('passed');
|
||||||
spec.addExpectationResult(false, { message: 'expectation2' });
|
});
|
||||||
|
|
||||||
spec.execute(fakeQueueRunner, null, function() {}, resultCallback);
|
it('returns "passed"" if all expectations passed', function() {
|
||||||
|
const spec = new jasmineUnderTest.Spec({
|
||||||
const fns = fakeQueueRunner.calls.mostRecent().args[0].queueableFns;
|
queueableFn: { fn: () => {} }
|
||||||
fns[fns.length - 1].fn();
|
|
||||||
|
|
||||||
expect(resultCallback.calls.first().args[0].passedExpectations).toEqual([
|
|
||||||
jasmine.objectContaining({ message: 'expectation1' })
|
|
||||||
]);
|
|
||||||
expect(resultCallback.calls.first().args[0].failedExpectations).toEqual([
|
|
||||||
jasmine.objectContaining({ message: 'expectation2' })
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("throws an ExpectationFailed error upon receiving a failed expectation when 'throwOnExpectationFailure' is set", function() {
|
|
||||||
const fakeQueueRunner = jasmine.createSpy('queueRunner'),
|
|
||||||
resultCallback = jasmine.createSpy('resultCallback'),
|
|
||||||
spec = new jasmineUnderTest.Spec({
|
|
||||||
queueableFn: { fn: function() {} },
|
|
||||||
resultCallback: resultCallback,
|
|
||||||
throwOnExpectationFailure: true
|
|
||||||
});
|
});
|
||||||
|
|
||||||
spec.addExpectationResult(true, { message: 'passed' });
|
spec.addExpectationResult(true, {});
|
||||||
expect(function() {
|
|
||||||
spec.addExpectationResult(false, { message: 'failed' });
|
|
||||||
}).toThrowError(jasmineUnderTest.errors.ExpectationFailed);
|
|
||||||
|
|
||||||
spec.execute(fakeQueueRunner, null, function() {}, resultCallback);
|
expect(spec.status()).toBe('passed');
|
||||||
|
});
|
||||||
|
|
||||||
const fns = fakeQueueRunner.calls.mostRecent().args[0].queueableFns;
|
it('returns "failed" if any expectation failed', function() {
|
||||||
fns[fns.length - 1].fn();
|
const spec = new jasmineUnderTest.Spec({
|
||||||
expect(resultCallback.calls.first().args[0].passedExpectations).toEqual([
|
queueableFn: { fn: () => {} }
|
||||||
jasmine.objectContaining({ message: 'passed' })
|
});
|
||||||
]);
|
|
||||||
expect(resultCallback.calls.first().args[0].failedExpectations).toEqual([
|
spec.addExpectationResult(true, {});
|
||||||
jasmine.objectContaining({ message: 'failed' })
|
spec.addExpectationResult(false, {});
|
||||||
]);
|
|
||||||
|
expect(spec.status()).toBe('failed');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('#addExpectationResult', function() {
|
||||||
|
it('keeps track of passed and failed expectations', function() {
|
||||||
|
const spec = new jasmineUnderTest.Spec({
|
||||||
|
queueableFn: { fn: () => {} }
|
||||||
|
});
|
||||||
|
|
||||||
|
spec.addExpectationResult(true, { message: 'expectation1' });
|
||||||
|
spec.addExpectationResult(false, { message: 'expectation2' });
|
||||||
|
|
||||||
|
expect(spec.result.passedExpectations).toEqual([
|
||||||
|
jasmine.objectContaining({ message: 'expectation1' })
|
||||||
|
]);
|
||||||
|
expect(spec.result.failedExpectations).toEqual([
|
||||||
|
jasmine.objectContaining({ message: 'expectation2' })
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("when 'throwOnExpectationFailure' is set", function() {
|
||||||
|
it('throws an ExpectationFailed error', function() {
|
||||||
|
const spec = new jasmineUnderTest.Spec({
|
||||||
|
queueableFn: { fn: () => {} },
|
||||||
|
throwOnExpectationFailure: true
|
||||||
|
});
|
||||||
|
|
||||||
|
spec.addExpectationResult(true, { message: 'passed' });
|
||||||
|
expect(function() {
|
||||||
|
spec.addExpectationResult(false, { message: 'failed' });
|
||||||
|
}).toThrowError(jasmineUnderTest.errors.ExpectationFailed);
|
||||||
|
|
||||||
|
expect(spec.result.failedExpectations).toEqual([
|
||||||
|
jasmine.objectContaining({ message: 'failed' })
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("when 'throwOnExpectationFailure' is not set", function() {
|
||||||
|
it('does not throw', function() {
|
||||||
|
const spec = new jasmineUnderTest.Spec({
|
||||||
|
queueableFn: { fn: () => {} }
|
||||||
|
});
|
||||||
|
|
||||||
|
spec.addExpectationResult(false, { message: 'failed' });
|
||||||
|
|
||||||
|
expect(spec.result.failedExpectations).toEqual([
|
||||||
|
jasmine.objectContaining({ message: 'failed' })
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('forwards late expectation failures to onLateError', function() {
|
it('forwards late expectation failures to onLateError', function() {
|
||||||
@@ -639,123 +305,47 @@ describe('Spec', function() {
|
|||||||
expect(spec.metadata.getPath()).toEqual(['expected val']);
|
expect(spec.metadata.getPath()).toEqual(['expected val']);
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('when a spec is marked pending during execution', function() {
|
describe('#handleException', function() {
|
||||||
it('should mark the spec as pending', function() {
|
it('records a failure', function() {
|
||||||
const fakeQueueRunner = function(opts) {
|
const spec = new jasmineUnderTest.Spec({
|
||||||
opts.onException(
|
queueableFn: {}
|
||||||
new Error(jasmineUnderTest.Spec.pendingSpecExceptionMessage)
|
|
||||||
);
|
|
||||||
},
|
|
||||||
spec = new jasmineUnderTest.Spec({
|
|
||||||
description: 'my test',
|
|
||||||
id: 'some-id',
|
|
||||||
queueableFn: { fn: function() {} }
|
|
||||||
});
|
|
||||||
|
|
||||||
spec.execute(fakeQueueRunner);
|
|
||||||
|
|
||||||
expect(spec.status()).toEqual('pending');
|
|
||||||
expect(spec.result.pendingReason).toEqual('');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should set the pendingReason', function() {
|
|
||||||
const fakeQueueRunner = function(opts) {
|
|
||||||
opts.onException(
|
|
||||||
new Error(
|
|
||||||
jasmineUnderTest.Spec.pendingSpecExceptionMessage +
|
|
||||||
'custom message'
|
|
||||||
)
|
|
||||||
);
|
|
||||||
},
|
|
||||||
spec = new jasmineUnderTest.Spec({
|
|
||||||
description: 'my test',
|
|
||||||
id: 'some-id',
|
|
||||||
queueableFn: { fn: function() {} }
|
|
||||||
});
|
|
||||||
|
|
||||||
spec.execute(fakeQueueRunner);
|
|
||||||
|
|
||||||
expect(spec.status()).toEqual('pending');
|
|
||||||
expect(spec.result.pendingReason).toEqual('custom message');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should log a failure when handling an exception', function() {
|
|
||||||
const fakeQueueRunner = jasmine.createSpy('queueRunner'),
|
|
||||||
resultCallback = jasmine.createSpy('resultCallback'),
|
|
||||||
spec = new jasmineUnderTest.Spec({
|
|
||||||
queueableFn: { fn: function() {} }
|
|
||||||
});
|
});
|
||||||
|
|
||||||
spec.handleException('foo');
|
spec.handleException('foo');
|
||||||
spec.execute(fakeQueueRunner, null, function() {}, resultCallback);
|
|
||||||
|
|
||||||
const args = fakeQueueRunner.calls.mostRecent().args[0];
|
expect(spec.result.failedExpectations).toEqual([
|
||||||
args.queueableFns[args.queueableFns.length - 1].fn();
|
{
|
||||||
expect(resultCallback.calls.first().args[0].failedExpectations).toEqual([
|
message: 'foo thrown',
|
||||||
{
|
matcherName: '',
|
||||||
message: 'foo thrown',
|
passed: false,
|
||||||
matcherName: '',
|
expected: '',
|
||||||
passed: false,
|
actual: '',
|
||||||
expected: '',
|
stack: null
|
||||||
actual: '',
|
|
||||||
stack: null
|
|
||||||
}
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should not log an additional failure when handling an ExpectationFailed error', function() {
|
|
||||||
const fakeQueueRunner = jasmine.createSpy('queueRunner'),
|
|
||||||
resultCallback = jasmine.createSpy('resultCallback'),
|
|
||||||
spec = new jasmineUnderTest.Spec({
|
|
||||||
queueableFn: { fn: function() {} }
|
|
||||||
});
|
|
||||||
|
|
||||||
spec.handleException(new jasmineUnderTest.errors.ExpectationFailed());
|
|
||||||
spec.execute(fakeQueueRunner, null, function() {}, resultCallback);
|
|
||||||
|
|
||||||
const args = fakeQueueRunner.calls.mostRecent().args[0];
|
|
||||||
args.queueableFns[args.queueableFns.length - 1].fn();
|
|
||||||
expect(resultCallback.calls.first().args[0].failedExpectations).toEqual([]);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('treats multiple done calls as late errors', function() {
|
|
||||||
const runQueue = jasmine.createSpy('runQueue'),
|
|
||||||
onLateError = jasmine.createSpy('onLateError'),
|
|
||||||
spec = new jasmineUnderTest.Spec({
|
|
||||||
onLateError: onLateError,
|
|
||||||
queueableFn: { fn: function() {} },
|
|
||||||
getPath: function() {
|
|
||||||
return ['a spec'];
|
|
||||||
}
|
}
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does not record an additional failure when the error is ExpectationFailed', function() {
|
||||||
|
const spec = new jasmineUnderTest.Spec({
|
||||||
|
queueableFn: {}
|
||||||
});
|
});
|
||||||
|
|
||||||
spec.execute(runQueue);
|
spec.handleException(new jasmineUnderTest.errors.ExpectationFailed());
|
||||||
|
|
||||||
expect(runQueue).toHaveBeenCalled();
|
expect(spec.result.failedExpectations).toEqual([]);
|
||||||
runQueue.calls.argsFor(0)[0].onMultipleDone();
|
});
|
||||||
|
|
||||||
expect(onLateError).toHaveBeenCalledTimes(1);
|
|
||||||
expect(onLateError.calls.argsFor(0)[0]).toBeInstanceOf(Error);
|
|
||||||
expect(onLateError.calls.argsFor(0)[0].message).toEqual(
|
|
||||||
'An asynchronous spec, beforeEach, or afterEach function called its ' +
|
|
||||||
"'done' callback more than once.\n(in spec: a spec)"
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('#trace', function() {
|
describe('#debugLog', function() {
|
||||||
it('adds the messages to the result', function() {
|
it('adds the messages to the result', function() {
|
||||||
const timer = jasmine.createSpyObj('timer', ['start', 'elapsed']),
|
const timer = jasmine.createSpyObj('timer', ['start', 'elapsed']);
|
||||||
spec = new jasmineUnderTest.Spec({
|
const spec = new jasmineUnderTest.Spec({
|
||||||
queueableFn: {
|
queueableFn: { fn: () => {} },
|
||||||
fn: function() {}
|
timer: timer
|
||||||
},
|
});
|
||||||
timer: timer
|
const t1 = 123;
|
||||||
}),
|
const t2 = 456;
|
||||||
t1 = 123,
|
|
||||||
t2 = 456;
|
|
||||||
|
|
||||||
spec.execute(() => {});
|
|
||||||
expect(spec.result.debugLogs).toBeNull();
|
expect(spec.result.debugLogs).toBeNull();
|
||||||
timer.elapsed.and.returnValue(t1);
|
timer.elapsed.and.returnValue(t1);
|
||||||
spec.debugLog('msg 1');
|
spec.debugLog('msg 1');
|
||||||
@@ -771,99 +361,36 @@ describe('Spec', function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('When the spec passes', function() {
|
describe('When the spec passes', function() {
|
||||||
it('omits the messages from the reported result', function() {
|
it('removes the logs from the result', function() {
|
||||||
const resultCallback = jasmine.createSpy('resultCallback'),
|
const spec = new jasmineUnderTest.Spec({
|
||||||
spec = new jasmineUnderTest.Spec({
|
queueableFn: { fn: () => {} }
|
||||||
queueableFn: {
|
});
|
||||||
fn: function() {}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
function runQueue(config) {
|
spec.debugLog('msg');
|
||||||
spec.debugLog('msg');
|
spec.executionFinished();
|
||||||
for (const fn of config.queueableFns) {
|
|
||||||
fn.fn();
|
|
||||||
}
|
|
||||||
config.onComplete(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
spec.execute(
|
|
||||||
runQueue,
|
|
||||||
null,
|
|
||||||
function() {},
|
|
||||||
resultCallback,
|
|
||||||
function() {}
|
|
||||||
);
|
|
||||||
expect(resultCallback).toHaveBeenCalledWith(
|
|
||||||
jasmine.objectContaining({ debugLogs: null }),
|
|
||||||
undefined
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('removes the messages to save memory', function() {
|
|
||||||
const resultCallback = jasmine.createSpy('resultCallback'),
|
|
||||||
spec = new jasmineUnderTest.Spec({
|
|
||||||
queueableFn: {
|
|
||||||
fn: function() {}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
function runQueue(config) {
|
|
||||||
spec.debugLog('msg');
|
|
||||||
for (const fn of config.queueableFns) {
|
|
||||||
fn.fn();
|
|
||||||
}
|
|
||||||
config.onComplete(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
spec.execute(
|
|
||||||
runQueue,
|
|
||||||
null,
|
|
||||||
function() {},
|
|
||||||
resultCallback,
|
|
||||||
function() {}
|
|
||||||
);
|
|
||||||
expect(resultCallback).toHaveBeenCalled();
|
|
||||||
expect(spec.result.debugLogs).toBeNull();
|
expect(spec.result.debugLogs).toBeNull();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('When the spec fails', function() {
|
describe('When the spec fails', function() {
|
||||||
it('includes the messages in the reported result', function() {
|
it('includes the messages in the result', function() {
|
||||||
const resultCallback = jasmine.createSpy('resultCallback'),
|
const timer = jasmine.createSpyObj('timer', ['start', 'elapsed']);
|
||||||
timer = jasmine.createSpyObj('timer', ['start', 'elapsed']),
|
const spec = new jasmineUnderTest.Spec({
|
||||||
spec = new jasmineUnderTest.Spec({
|
queueableFn: { fn: () => {} },
|
||||||
queueableFn: {
|
timer: timer
|
||||||
fn: function() {}
|
});
|
||||||
},
|
const timestamp = 12345;
|
||||||
timer: timer
|
|
||||||
}),
|
|
||||||
timestamp = 12345;
|
|
||||||
|
|
||||||
timer.elapsed.and.returnValue(timestamp);
|
timer.elapsed.and.returnValue(timestamp);
|
||||||
|
|
||||||
function runQueue(config) {
|
spec.debugLog('msg');
|
||||||
spec.debugLog('msg');
|
spec.handleException(new Error('nope'));
|
||||||
spec.handleException(new Error('nope'));
|
spec.executionFinished();
|
||||||
for (const fn of config.queueableFns) {
|
|
||||||
fn.fn();
|
|
||||||
}
|
|
||||||
config.onComplete(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
spec.execute(
|
expect(spec.result.debugLogs).toEqual([
|
||||||
runQueue,
|
{ message: 'msg', timestamp: timestamp }
|
||||||
null,
|
]);
|
||||||
function() {},
|
|
||||||
resultCallback,
|
|
||||||
function() {}
|
|
||||||
);
|
|
||||||
expect(resultCallback).toHaveBeenCalledWith(
|
|
||||||
jasmine.objectContaining({
|
|
||||||
debugLogs: [{ message: 'msg', timestamp: timestamp }]
|
|
||||||
}),
|
|
||||||
undefined
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -71,7 +71,131 @@ describe('TreeRunner', function() {
|
|||||||
await expectAsync(executePromise).toBePending();
|
await expectAsync(executePromise).toBePending();
|
||||||
});
|
});
|
||||||
|
|
||||||
function runSingleSpecSuite(spec) {
|
it('runs before and after fns', function() {
|
||||||
|
const before = { fn: jasmine.createSpy('before') };
|
||||||
|
const after = { fn: jasmine.createSpy('after') };
|
||||||
|
const queueableFn = {
|
||||||
|
fn: jasmine.createSpy('test body').and.callFake(function() {
|
||||||
|
expect(before).toHaveBeenCalled();
|
||||||
|
expect(after).not.toHaveBeenCalled();
|
||||||
|
})
|
||||||
|
};
|
||||||
|
const spec = new jasmineUnderTest.Spec({
|
||||||
|
queueableFn: queueableFn,
|
||||||
|
beforeAndAfterFns: function() {
|
||||||
|
return { befores: [before], afters: [after] };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const { runQueue, suiteRunQueueArgs } = runSingleSpecSuite(spec);
|
||||||
|
suiteRunQueueArgs.queueableFns[0].fn();
|
||||||
|
expect(runQueue).toHaveBeenCalledTimes(1);
|
||||||
|
const specRunQueueArgs = runQueue.calls.mostRecent().args[0];
|
||||||
|
|
||||||
|
expect(specRunQueueArgs.queueableFns[1]).toEqual(before);
|
||||||
|
expect(specRunQueueArgs.queueableFns[2]).toEqual(queueableFn);
|
||||||
|
expect(specRunQueueArgs.queueableFns[3]).toEqual(after);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('marks specs pending at runtime', function() {
|
||||||
|
let spec;
|
||||||
|
const queueableFn = {
|
||||||
|
fn() {
|
||||||
|
spec.pend();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
spec = new jasmineUnderTest.Spec({ queueableFn });
|
||||||
|
|
||||||
|
const { runQueue, suiteRunQueueArgs } = runSingleSpecSuite(spec);
|
||||||
|
suiteRunQueueArgs.queueableFns[0].fn();
|
||||||
|
expect(runQueue).toHaveBeenCalledTimes(1);
|
||||||
|
const specRunQueueArgs = runQueue.calls.mostRecent().args[0];
|
||||||
|
|
||||||
|
expect(specRunQueueArgs.queueableFns[1]).toEqual(queueableFn);
|
||||||
|
queueableFn.fn();
|
||||||
|
|
||||||
|
expect(spec.status()).toEqual('pending');
|
||||||
|
expect(spec.getResult().status).toEqual('pending');
|
||||||
|
expect(spec.getResult().pendingReason).toEqual('');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('marks specs pending at runtime with a message', function() {
|
||||||
|
let spec;
|
||||||
|
const queueableFn = {
|
||||||
|
fn() {
|
||||||
|
spec.pend('some reason');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
spec = new jasmineUnderTest.Spec({ queueableFn });
|
||||||
|
|
||||||
|
const { runQueue, suiteRunQueueArgs } = runSingleSpecSuite(spec);
|
||||||
|
suiteRunQueueArgs.queueableFns[0].fn();
|
||||||
|
expect(runQueue).toHaveBeenCalledTimes(1);
|
||||||
|
const specRunQueueArgs = runQueue.calls.mostRecent().args[0];
|
||||||
|
|
||||||
|
expect(specRunQueueArgs.queueableFns[1]).toEqual(queueableFn);
|
||||||
|
queueableFn.fn();
|
||||||
|
|
||||||
|
expect(spec.status()).toEqual('pending');
|
||||||
|
expect(spec.getResult().status).toEqual('pending');
|
||||||
|
expect(spec.getResult().pendingReason).toEqual('some reason');
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Late promise rejection handling', function() {
|
||||||
|
it('is enabled when the detectLateRejectionHandling param is true', function() {
|
||||||
|
const before = jasmine.createSpy('before');
|
||||||
|
const after = jasmine.createSpy('after');
|
||||||
|
const queueableFn = {
|
||||||
|
fn: jasmine.createSpy('test body').and.callFake(function() {
|
||||||
|
expect(before).toHaveBeenCalled();
|
||||||
|
expect(after).not.toHaveBeenCalled();
|
||||||
|
})
|
||||||
|
};
|
||||||
|
const spec = new jasmineUnderTest.Spec({
|
||||||
|
queueableFn,
|
||||||
|
beforeAndAfterFns: function() {
|
||||||
|
return { befores: [before], afters: [after] };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const {
|
||||||
|
runQueue,
|
||||||
|
setTimeout,
|
||||||
|
suiteRunQueueArgs,
|
||||||
|
globalErrors
|
||||||
|
} = runSingleSpecSuite(spec, { detectLateRejectionHandling: true });
|
||||||
|
|
||||||
|
suiteRunQueueArgs.queueableFns[0].fn();
|
||||||
|
expect(runQueue).toHaveBeenCalledTimes(1);
|
||||||
|
const specRunQueueOpts = runQueue.calls.mostRecent().args[0];
|
||||||
|
|
||||||
|
expect(specRunQueueOpts.queueableFns).toEqual([
|
||||||
|
{ fn: jasmine.any(Function) },
|
||||||
|
before,
|
||||||
|
queueableFn,
|
||||||
|
after,
|
||||||
|
{ fn: jasmine.any(Function) },
|
||||||
|
{
|
||||||
|
fn: jasmine.any(Function),
|
||||||
|
type: 'specCleanup'
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
|
||||||
|
const done = jasmine.createSpy('done');
|
||||||
|
specRunQueueOpts.queueableFns[4].fn(done);
|
||||||
|
expect(globalErrors.reportUnhandledRejections).not.toHaveBeenCalled();
|
||||||
|
expect(done).not.toHaveBeenCalled();
|
||||||
|
|
||||||
|
expect(setTimeout).toHaveBeenCalledOnceWith(jasmine.any(Function));
|
||||||
|
setTimeout.calls.argsFor(0)[0]();
|
||||||
|
expect(globalErrors.reportUnhandledRejections).toHaveBeenCalled();
|
||||||
|
expect(globalErrors.reportUnhandledRejections).toHaveBeenCalledBefore(
|
||||||
|
done
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
function runSingleSpecSuite(spec, optionalConfig) {
|
||||||
const topSuiteId = 'suite1';
|
const topSuiteId = 'suite1';
|
||||||
spec.parentSuiteId = topSuiteId;
|
spec.parentSuiteId = topSuiteId;
|
||||||
const topSuite = new jasmineUnderTest.Suite({ id: topSuiteId });
|
const topSuite = new jasmineUnderTest.Suite({ id: topSuiteId });
|
||||||
@@ -88,15 +212,19 @@ describe('TreeRunner', function() {
|
|||||||
const runQueue = jasmine.createSpy('runQueue');
|
const runQueue = jasmine.createSpy('runQueue');
|
||||||
const reportDispatcher = mockReportDispatcher();
|
const reportDispatcher = mockReportDispatcher();
|
||||||
const runableResources = mockRunableResources();
|
const runableResources = mockRunableResources();
|
||||||
|
const globalErrors = mockGlobalErrors();
|
||||||
|
const setTimeout = jasmine.createSpy('setTimeout');
|
||||||
const currentRunableTracker = new jasmineUnderTest.CurrentRunableTracker();
|
const currentRunableTracker = new jasmineUnderTest.CurrentRunableTracker();
|
||||||
const subject = new jasmineUnderTest.TreeRunner({
|
const subject = new jasmineUnderTest.TreeRunner({
|
||||||
executionTree,
|
executionTree,
|
||||||
runQueue,
|
runQueue,
|
||||||
|
globalErrors,
|
||||||
|
setTimeout,
|
||||||
runableResources,
|
runableResources,
|
||||||
reportDispatcher,
|
reportDispatcher,
|
||||||
currentRunableTracker,
|
currentRunableTracker,
|
||||||
getConfig() {
|
getConfig() {
|
||||||
return {};
|
return optionalConfig || {};
|
||||||
},
|
},
|
||||||
reportChildrenOfBeforeAllFailure() {}
|
reportChildrenOfBeforeAllFailure() {}
|
||||||
});
|
});
|
||||||
@@ -108,6 +236,8 @@ describe('TreeRunner', function() {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
runQueue,
|
runQueue,
|
||||||
|
globalErrors,
|
||||||
|
setTimeout,
|
||||||
currentRunableTracker,
|
currentRunableTracker,
|
||||||
runableResources,
|
runableResources,
|
||||||
reportDispatcher,
|
reportDispatcher,
|
||||||
@@ -136,4 +266,8 @@ describe('TreeRunner', function() {
|
|||||||
'clearForRunable'
|
'clearForRunable'
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function mockGlobalErrors() {
|
||||||
|
return jasmine.createSpyObj('globalErrors', ['reportUnhandledRejections']);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ getJasmineRequireObj().Spec = function(j$) {
|
|||||||
function Spec(attrs) {
|
function Spec(attrs) {
|
||||||
this.expectationFactory = attrs.expectationFactory;
|
this.expectationFactory = attrs.expectationFactory;
|
||||||
this.asyncExpectationFactory = attrs.asyncExpectationFactory;
|
this.asyncExpectationFactory = attrs.asyncExpectationFactory;
|
||||||
this.setTimeout = attrs.setTimeout;
|
|
||||||
this.id = attrs.id;
|
this.id = attrs.id;
|
||||||
this.filename = attrs.filename;
|
this.filename = attrs.filename;
|
||||||
this.parentSuiteId = attrs.parentSuiteId;
|
this.parentSuiteId = attrs.parentSuiteId;
|
||||||
@@ -86,87 +85,6 @@ getJasmineRequireObj().Spec = function(j$) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Spec.prototype.execute = function(
|
|
||||||
runQueue,
|
|
||||||
globalErrors,
|
|
||||||
onStart,
|
|
||||||
// TODO: may be able to merge resultCallback into onComplete
|
|
||||||
resultCallback,
|
|
||||||
onComplete,
|
|
||||||
excluded,
|
|
||||||
failSpecWithNoExp,
|
|
||||||
detectLateRejectionHandling
|
|
||||||
) {
|
|
||||||
const start = {
|
|
||||||
fn: done => {
|
|
||||||
this.executionStarted();
|
|
||||||
onStart(done);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const complete = {
|
|
||||||
fn: done => {
|
|
||||||
this.executionFinished(excluded, failSpecWithNoExp);
|
|
||||||
resultCallback(this.result, done);
|
|
||||||
},
|
|
||||||
type: 'specCleanup'
|
|
||||||
};
|
|
||||||
|
|
||||||
const fns = this.beforeAndAfterFns();
|
|
||||||
|
|
||||||
const runnerConfig = {
|
|
||||||
isLeaf: true,
|
|
||||||
queueableFns: [...fns.befores, this.queueableFn, ...fns.afters],
|
|
||||||
onException: e => this.handleException(e),
|
|
||||||
onMultipleDone: () => {
|
|
||||||
// Issue a deprecation. Include the context ourselves and pass
|
|
||||||
// ignoreRunnable: true, since getting here always means that we've already
|
|
||||||
// moved on and the current runnable isn't the one that caused the problem.
|
|
||||||
this.onLateError(
|
|
||||||
new Error(
|
|
||||||
'An asynchronous spec, beforeEach, or afterEach function called its ' +
|
|
||||||
"'done' callback more than once.\n(in spec: " +
|
|
||||||
this.getFullName() +
|
|
||||||
')'
|
|
||||||
)
|
|
||||||
);
|
|
||||||
},
|
|
||||||
onComplete: () => {
|
|
||||||
if (this.result.status === 'failed') {
|
|
||||||
onComplete(new j$.StopExecutionError('spec failed'));
|
|
||||||
} else {
|
|
||||||
onComplete();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
userContext: this.userContext(),
|
|
||||||
runnableName: this.getFullName.bind(this)
|
|
||||||
};
|
|
||||||
|
|
||||||
if (this.markedPending || excluded === true) {
|
|
||||||
runnerConfig.queueableFns = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
runnerConfig.queueableFns.unshift(start);
|
|
||||||
|
|
||||||
if (detectLateRejectionHandling) {
|
|
||||||
// Conditional because the setTimeout imposes a significant performance
|
|
||||||
// penalty in suites with lots of fast specs.
|
|
||||||
runnerConfig.queueableFns.push({
|
|
||||||
fn: done => {
|
|
||||||
// setTimeout is necessary to trigger rejectionhandled events
|
|
||||||
// TODO: let clearStack know about this so it doesn't do redundant setTimeouts
|
|
||||||
this.setTimeout(function() {
|
|
||||||
globalErrors.reportUnhandledRejections();
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
runnerConfig.queueableFns.push(complete);
|
|
||||||
|
|
||||||
runQueue(runnerConfig);
|
|
||||||
};
|
|
||||||
|
|
||||||
Spec.prototype.reset = function() {
|
Spec.prototype.reset = function() {
|
||||||
/**
|
/**
|
||||||
* @typedef SpecResult
|
* @typedef SpecResult
|
||||||
@@ -255,6 +173,8 @@ getJasmineRequireObj().Spec = function(j$) {
|
|||||||
this.pend(message);
|
this.pend(message);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// TODO: ensure that all access to result goes through .getResult()
|
||||||
|
// so that the status is correct.
|
||||||
Spec.prototype.getResult = function() {
|
Spec.prototype.getResult = function() {
|
||||||
this.result.status = this.status();
|
this.result.status = this.status();
|
||||||
return this.result;
|
return this.result;
|
||||||
|
|||||||
@@ -237,7 +237,6 @@ getJasmineRequireObj().SuiteBuilder = function(j$) {
|
|||||||
const config = this.env_.configuration();
|
const config = this.env_.configuration();
|
||||||
const suite = this.currentDeclarationSuite_;
|
const suite = this.currentDeclarationSuite_;
|
||||||
const parentSuiteId = suite === this.topSuite ? null : suite.id;
|
const parentSuiteId = suite === this.topSuite ? null : suite.id;
|
||||||
const global = j$.getGlobal();
|
|
||||||
const spec = new j$.Spec({
|
const spec = new j$.Spec({
|
||||||
id: 'spec' + this.nextSpecId_++,
|
id: 'spec' + this.nextSpecId_++,
|
||||||
filename,
|
filename,
|
||||||
@@ -245,7 +244,6 @@ getJasmineRequireObj().SuiteBuilder = function(j$) {
|
|||||||
beforeAndAfterFns: beforeAndAfterFns(suite),
|
beforeAndAfterFns: beforeAndAfterFns(suite),
|
||||||
expectationFactory: this.expectationFactory_,
|
expectationFactory: this.expectationFactory_,
|
||||||
asyncExpectationFactory: this.specAsyncExpectationFactory_,
|
asyncExpectationFactory: this.specAsyncExpectationFactory_,
|
||||||
setTimeout: global.setTimeout.bind(global),
|
|
||||||
onLateError: this.onLateError_,
|
onLateError: this.onLateError_,
|
||||||
getPath: spec => this.getSpecPath_(spec, suite),
|
getPath: spec => this.getSpecPath_(spec, suite),
|
||||||
description: description,
|
description: description,
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
getJasmineRequireObj().TreeRunner = function(j$) {
|
getJasmineRequireObj().TreeRunner = function(j$) {
|
||||||
class TreeRunner {
|
class TreeRunner {
|
||||||
#executionTree;
|
#executionTree;
|
||||||
|
#setTimeout;
|
||||||
#globalErrors;
|
#globalErrors;
|
||||||
#runableResources;
|
#runableResources;
|
||||||
#reportDispatcher;
|
#reportDispatcher;
|
||||||
@@ -13,6 +14,7 @@ getJasmineRequireObj().TreeRunner = function(j$) {
|
|||||||
constructor(attrs) {
|
constructor(attrs) {
|
||||||
this.#executionTree = attrs.executionTree;
|
this.#executionTree = attrs.executionTree;
|
||||||
this.#globalErrors = attrs.globalErrors;
|
this.#globalErrors = attrs.globalErrors;
|
||||||
|
this.#setTimeout = attrs.setTimeout || setTimeout.bind(globalThis);
|
||||||
this.#runableResources = attrs.runableResources;
|
this.#runableResources = attrs.runableResources;
|
||||||
this.#reportDispatcher = attrs.reportDispatcher;
|
this.#reportDispatcher = attrs.reportDispatcher;
|
||||||
this.#runQueue = attrs.runQueue;
|
this.#runQueue = attrs.runQueue;
|
||||||
@@ -57,31 +59,103 @@ getJasmineRequireObj().TreeRunner = function(j$) {
|
|||||||
if (node.suite) {
|
if (node.suite) {
|
||||||
this.#executeSuiteSegment(node.suite, node.segmentNumber, done);
|
this.#executeSuiteSegment(node.suite, node.segmentNumber, done);
|
||||||
} else {
|
} else {
|
||||||
this.#executeSpec(node.spec, done);
|
this._executeSpec(node.spec, done);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
#executeSpec(spec, done) {
|
// Only exposed for testing.
|
||||||
const config = this.#getConfig();
|
_executeSpec(spec, specOverallDone) {
|
||||||
spec.execute(
|
const onStart = next => {
|
||||||
this.#runQueueWithSkipPolicy.bind(this),
|
this.#currentRunableTracker.setCurrentSpec(spec);
|
||||||
this.#globalErrors,
|
this.#runableResources.initForRunable(spec.id, spec.parentSuiteId);
|
||||||
next => {
|
this.#reportDispatcher.specStarted(spec.result).then(next);
|
||||||
this.#currentRunableTracker.setCurrentSpec(spec);
|
};
|
||||||
this.#runableResources.initForRunable(spec.id, spec.parentSuiteId);
|
const resultCallback = (result, next) => {
|
||||||
this.#reportDispatcher.specStarted(spec.result).then(next);
|
this.#specComplete(spec).then(next);
|
||||||
},
|
};
|
||||||
(result, next) => {
|
const queueableFns = this.#specQueueableFns(
|
||||||
this.#specComplete(spec).then(next);
|
spec,
|
||||||
},
|
onStart,
|
||||||
done,
|
resultCallback
|
||||||
this.#executionTree.isExcluded(spec),
|
|
||||||
config.failSpecWithNoExpectations,
|
|
||||||
config.detectLateRejectionHandling
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
this.#runQueueWithSkipPolicy({
|
||||||
|
isLeaf: true,
|
||||||
|
queueableFns,
|
||||||
|
onException: e => spec.handleException(e),
|
||||||
|
onMultipleDone: () => {
|
||||||
|
// Issue an erorr. Include the context ourselves and pass
|
||||||
|
// ignoreRunnable: true, since getting here always means that we've already
|
||||||
|
// moved on and the current runnable isn't the one that caused the problem.
|
||||||
|
spec.onLateError(
|
||||||
|
new Error(
|
||||||
|
'An asynchronous spec, beforeEach, or afterEach function called its ' +
|
||||||
|
"'done' callback more than once.\n(in spec: " +
|
||||||
|
spec.getFullName() +
|
||||||
|
')'
|
||||||
|
)
|
||||||
|
);
|
||||||
|
},
|
||||||
|
onComplete: () => {
|
||||||
|
if (spec.result.status === 'failed') {
|
||||||
|
specOverallDone(new j$.StopExecutionError('spec failed'));
|
||||||
|
} else {
|
||||||
|
specOverallDone();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
userContext: spec.userContext(),
|
||||||
|
runnableName: spec.getFullName.bind(spec)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#specQueueableFns(spec, onStart, resultCallback) {
|
||||||
|
const config = this.#getConfig();
|
||||||
|
const excluded = this.#executionTree.isExcluded(spec);
|
||||||
|
const ba = spec.beforeAndAfterFns();
|
||||||
|
let fns = [...ba.befores, spec.queueableFn, ...ba.afters];
|
||||||
|
|
||||||
|
if (spec.markedPending || excluded === true) {
|
||||||
|
fns = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
const start = {
|
||||||
|
fn(done) {
|
||||||
|
spec.executionStarted();
|
||||||
|
onStart(done);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const complete = {
|
||||||
|
fn(done) {
|
||||||
|
spec.executionFinished(excluded, config.failSpecWithNoExpectations);
|
||||||
|
resultCallback(spec.result, done);
|
||||||
|
},
|
||||||
|
type: 'specCleanup'
|
||||||
|
};
|
||||||
|
|
||||||
|
fns.unshift(start);
|
||||||
|
|
||||||
|
if (config.detectLateRejectionHandling) {
|
||||||
|
// Conditional because the setTimeout imposes a significant performance
|
||||||
|
// penalty in suites with lots of fast specs.
|
||||||
|
const globalErrors = this.#globalErrors;
|
||||||
|
fns.push({
|
||||||
|
fn: done => {
|
||||||
|
// setTimeout is necessary to trigger rejectionhandled events
|
||||||
|
// TODO: let clearStack know about this so it doesn't do redundant setTimeouts
|
||||||
|
this.#setTimeout(function() {
|
||||||
|
globalErrors.reportUnhandledRejections();
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fns.push(complete);
|
||||||
|
return fns;
|
||||||
}
|
}
|
||||||
|
|
||||||
#executeSuiteSegment(suite, segmentNumber, done) {
|
#executeSuiteSegment(suite, segmentNumber, done) {
|
||||||
|
|||||||
Reference in New Issue
Block a user