Move spec execution from Spec to TreeRunner
This commit is contained in:
@@ -774,7 +774,6 @@ getJasmineRequireObj().Spec = function(j$) {
|
||||
function Spec(attrs) {
|
||||
this.expectationFactory = attrs.expectationFactory;
|
||||
this.asyncExpectationFactory = attrs.asyncExpectationFactory;
|
||||
this.setTimeout = attrs.setTimeout;
|
||||
this.id = attrs.id;
|
||||
this.filename = attrs.filename;
|
||||
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() {
|
||||
/**
|
||||
* @typedef SpecResult
|
||||
@@ -1027,6 +945,8 @@ getJasmineRequireObj().Spec = function(j$) {
|
||||
this.pend(message);
|
||||
};
|
||||
|
||||
// TODO: ensure that all access to result goes through .getResult()
|
||||
// so that the status is correct.
|
||||
Spec.prototype.getResult = function() {
|
||||
this.result.status = this.status();
|
||||
return this.result;
|
||||
@@ -11151,7 +11071,6 @@ getJasmineRequireObj().SuiteBuilder = function(j$) {
|
||||
const config = this.env_.configuration();
|
||||
const suite = this.currentDeclarationSuite_;
|
||||
const parentSuiteId = suite === this.topSuite ? null : suite.id;
|
||||
const global = j$.getGlobal();
|
||||
const spec = new j$.Spec({
|
||||
id: 'spec' + this.nextSpecId_++,
|
||||
filename,
|
||||
@@ -11159,7 +11078,6 @@ getJasmineRequireObj().SuiteBuilder = function(j$) {
|
||||
beforeAndAfterFns: beforeAndAfterFns(suite),
|
||||
expectationFactory: this.expectationFactory_,
|
||||
asyncExpectationFactory: this.specAsyncExpectationFactory_,
|
||||
setTimeout: global.setTimeout.bind(global),
|
||||
onLateError: this.onLateError_,
|
||||
getPath: spec => this.getSpecPath_(spec, suite),
|
||||
description: description,
|
||||
@@ -11518,6 +11436,7 @@ getJasmineRequireObj().TreeProcessor = function(j$) {
|
||||
getJasmineRequireObj().TreeRunner = function(j$) {
|
||||
class TreeRunner {
|
||||
#executionTree;
|
||||
#setTimeout;
|
||||
#globalErrors;
|
||||
#runableResources;
|
||||
#reportDispatcher;
|
||||
@@ -11530,6 +11449,7 @@ getJasmineRequireObj().TreeRunner = function(j$) {
|
||||
constructor(attrs) {
|
||||
this.#executionTree = attrs.executionTree;
|
||||
this.#globalErrors = attrs.globalErrors;
|
||||
this.#setTimeout = attrs.setTimeout || setTimeout.bind(globalThis);
|
||||
this.#runableResources = attrs.runableResources;
|
||||
this.#reportDispatcher = attrs.reportDispatcher;
|
||||
this.#runQueue = attrs.runQueue;
|
||||
@@ -11574,31 +11494,103 @@ getJasmineRequireObj().TreeRunner = function(j$) {
|
||||
if (node.suite) {
|
||||
this.#executeSuiteSegment(node.suite, node.segmentNumber, done);
|
||||
} else {
|
||||
this.#executeSpec(node.spec, done);
|
||||
this._executeSpec(node.spec, done);
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
#executeSpec(spec, done) {
|
||||
const config = this.#getConfig();
|
||||
spec.execute(
|
||||
this.#runQueueWithSkipPolicy.bind(this),
|
||||
this.#globalErrors,
|
||||
next => {
|
||||
this.#currentRunableTracker.setCurrentSpec(spec);
|
||||
this.#runableResources.initForRunable(spec.id, spec.parentSuiteId);
|
||||
this.#reportDispatcher.specStarted(spec.result).then(next);
|
||||
},
|
||||
(result, next) => {
|
||||
this.#specComplete(spec).then(next);
|
||||
},
|
||||
done,
|
||||
this.#executionTree.isExcluded(spec),
|
||||
config.failSpecWithNoExpectations,
|
||||
config.detectLateRejectionHandling
|
||||
// Only exposed for testing.
|
||||
_executeSpec(spec, specOverallDone) {
|
||||
const onStart = next => {
|
||||
this.#currentRunableTracker.setCurrentSpec(spec);
|
||||
this.#runableResources.initForRunable(spec.id, spec.parentSuiteId);
|
||||
this.#reportDispatcher.specStarted(spec.result).then(next);
|
||||
};
|
||||
const resultCallback = (result, next) => {
|
||||
this.#specComplete(spec).then(next);
|
||||
};
|
||||
const queueableFns = this.#specQueueableFns(
|
||||
spec,
|
||||
onStart,
|
||||
resultCallback
|
||||
);
|
||||
|
||||
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) {
|
||||
|
||||
@@ -224,7 +224,7 @@ describe('Runner', function() {
|
||||
});
|
||||
});
|
||||
|
||||
describe('Integration with TreeProcessor', function() {
|
||||
describe('Integration with TreeProcessor and TreeRunner', function() {
|
||||
let suiteNumber,
|
||||
specNumber,
|
||||
runQueue,
|
||||
@@ -250,6 +250,8 @@ describe('Runner', function() {
|
||||
// Reasonable defaults, may be overridden in some cases
|
||||
failSpecWithNoExpectations = false;
|
||||
detectLateRejectionHandling = false;
|
||||
|
||||
spyOn(jasmineUnderTest.TreeRunner.prototype, '_executeSpec');
|
||||
});
|
||||
|
||||
function StubSuite(attrs) {
|
||||
@@ -280,6 +282,10 @@ describe('Runner', function() {
|
||||
this.id = 'spec' + specNumber++;
|
||||
this.markedPending = attrs.markedPending || false;
|
||||
this.execute = jasmine.createSpy(this.id + '#execute');
|
||||
this.beforeAndAfterFns = () => ({ befores: [], afters: [] });
|
||||
this.userContext = () => ({});
|
||||
this.getFullName = () => '';
|
||||
this.queueableFn = () => {};
|
||||
}
|
||||
|
||||
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) {
|
||||
const ex = jasmineUnderTest.TreeRunner.prototype._executeSpec;
|
||||
ex.withArgs(spec, 'onComplete').and.callThrough();
|
||||
|
||||
queueableFn.fn('onComplete');
|
||||
expect(spec.execute).toHaveBeenCalledWith(
|
||||
jasmine.any(Function),
|
||||
globalErrors,
|
||||
jasmine.any(Function),
|
||||
jasmine.any(Function),
|
||||
'onComplete',
|
||||
shouldBeExcluded,
|
||||
failSpecWithNoExpectations,
|
||||
detectLateRejectionHandling
|
||||
expect(ex).toHaveBeenCalledWith(spec, 'onComplete');
|
||||
|
||||
expect(runQueue).toHaveBeenCalledWith(
|
||||
jasmine.objectContaining({
|
||||
isLeaf: true,
|
||||
SkipPolicy: jasmineUnderTest.CompleteOnFirstErrorSkipPolicy,
|
||||
queueableFns: shouldBeExcluded
|
||||
? arrayNotContaining(spec.queueableFn)
|
||||
: 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() {
|
||||
@@ -467,16 +490,9 @@ describe('Runner', function() {
|
||||
expect(queueableFns.length).toBe(2);
|
||||
|
||||
queueableFns[1].fn('foo');
|
||||
expect(spec.execute).toHaveBeenCalledWith(
|
||||
jasmine.any(Function),
|
||||
globalErrors,
|
||||
jasmine.any(Function),
|
||||
jasmine.any(Function),
|
||||
'foo',
|
||||
false,
|
||||
true,
|
||||
detectLateRejectionHandling
|
||||
);
|
||||
expect(
|
||||
jasmineUnderTest.TreeRunner.prototype._executeSpec
|
||||
).toHaveBeenCalledWith(spec, 'foo');
|
||||
|
||||
await expectAsync(promise).toBePending();
|
||||
});
|
||||
@@ -568,14 +584,20 @@ describe('Runner', function() {
|
||||
await Promise.resolve();
|
||||
expect(runQueue).toHaveBeenCalledTimes(1);
|
||||
const queueableFns = runQueue.calls.mostRecent().args[0].queueableFns;
|
||||
queueableFns[0].fn();
|
||||
queueableFns[0].fn('done');
|
||||
|
||||
expect(specs[0].execute).not.toHaveBeenCalled();
|
||||
expect(specs[1].execute).toHaveBeenCalled();
|
||||
expect(
|
||||
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();
|
||||
});
|
||||
@@ -590,32 +612,20 @@ describe('Runner', function() {
|
||||
await Promise.resolve();
|
||||
expect(runQueue).toHaveBeenCalledTimes(1);
|
||||
const queueableFns = runQueue.calls.mostRecent().args[0].queueableFns;
|
||||
queueableFns[0].fn();
|
||||
queueableFns[0].fn('done');
|
||||
|
||||
expect(nonSpecified.execute).not.toHaveBeenCalled();
|
||||
expect(specified.execute).toHaveBeenCalledWith(
|
||||
jasmine.any(Function),
|
||||
globalErrors,
|
||||
jasmine.any(Function),
|
||||
jasmine.any(Function),
|
||||
undefined,
|
||||
false,
|
||||
false,
|
||||
detectLateRejectionHandling
|
||||
);
|
||||
expect(
|
||||
jasmineUnderTest.TreeRunner.prototype._executeSpec
|
||||
).not.toHaveBeenCalledWith(nonSpecified, jasmine.anything());
|
||||
expect(
|
||||
jasmineUnderTest.TreeRunner.prototype._executeSpec
|
||||
).toHaveBeenCalledWith(specified, 'done');
|
||||
|
||||
queueableFns[1].fn();
|
||||
queueableFns[1].fn('done');
|
||||
|
||||
expect(nonSpecified.execute).toHaveBeenCalledWith(
|
||||
jasmine.any(Function),
|
||||
globalErrors,
|
||||
jasmine.any(Function),
|
||||
jasmine.any(Function),
|
||||
undefined,
|
||||
true,
|
||||
false,
|
||||
detectLateRejectionHandling
|
||||
);
|
||||
expect(
|
||||
jasmineUnderTest.TreeRunner.prototype._executeSpec
|
||||
).toHaveBeenCalledWith(nonSpecified, 'done');
|
||||
|
||||
await expectAsync(promise).toBePending();
|
||||
});
|
||||
@@ -637,11 +647,15 @@ describe('Runner', function() {
|
||||
|
||||
expect(specifiedSpec.execute).not.toHaveBeenCalled();
|
||||
const nodeQueueableFns = runQueue.calls.mostRecent().args[0].queueableFns;
|
||||
nodeQueueableFns[1].fn();
|
||||
expect(nonSpecifiedSpec.execute).toHaveBeenCalled();
|
||||
nodeQueueableFns[1].fn('done');
|
||||
expect(
|
||||
jasmineUnderTest.TreeRunner.prototype._executeSpec
|
||||
).toHaveBeenCalledWith(nonSpecifiedSpec, 'done');
|
||||
|
||||
queueableFns[1].fn();
|
||||
expect(specifiedSpec.execute).toHaveBeenCalled();
|
||||
queueableFns[1].fn('done');
|
||||
expect(
|
||||
jasmineUnderTest.TreeRunner.prototype._executeSpec
|
||||
).toHaveBeenCalledWith(specifiedSpec, 'done');
|
||||
|
||||
await expectAsync(promise).toBePending();
|
||||
});
|
||||
@@ -660,17 +674,23 @@ describe('Runner', function() {
|
||||
const queueableFns = runQueue.calls.mostRecent().args[0].queueableFns;
|
||||
expect(queueableFns.length).toBe(2);
|
||||
|
||||
queueableFns[0].fn();
|
||||
expect(spec1.execute).toHaveBeenCalled();
|
||||
queueableFns[0].fn('done');
|
||||
expect(
|
||||
jasmineUnderTest.TreeRunner.prototype._executeSpec
|
||||
).toHaveBeenCalledWith(spec1, 'done');
|
||||
|
||||
queueableFns[1].fn();
|
||||
const childFns = runQueue.calls.mostRecent().args[0].queueableFns;
|
||||
expect(childFns.length).toBe(3);
|
||||
childFns[1].fn();
|
||||
expect(spec2.execute).toHaveBeenCalled();
|
||||
childFns[1].fn('done');
|
||||
expect(
|
||||
jasmineUnderTest.TreeRunner.prototype._executeSpec
|
||||
).toHaveBeenCalledWith(spec2, 'done');
|
||||
|
||||
childFns[2].fn();
|
||||
expect(spec3.execute).toHaveBeenCalled();
|
||||
childFns[2].fn('done');
|
||||
expect(
|
||||
jasmineUnderTest.TreeRunner.prototype._executeSpec
|
||||
).toHaveBeenCalledWith(spec3, 'done');
|
||||
|
||||
await expectAsync(promise).toBePending();
|
||||
});
|
||||
@@ -699,26 +719,36 @@ describe('Runner', function() {
|
||||
|
||||
queueableFns[0].fn();
|
||||
expect(runQueue.calls.mostRecent().args[0].queueableFns.length).toBe(2);
|
||||
runQueue.calls.mostRecent().args[0].queueableFns[1].fn();
|
||||
expect(spec1.execute).toHaveBeenCalled();
|
||||
runQueue.calls.mostRecent().args[0].queueableFns[1].fn('done');
|
||||
expect(
|
||||
jasmineUnderTest.TreeRunner.prototype._executeSpec
|
||||
).toHaveBeenCalledWith(spec1, 'done');
|
||||
|
||||
queueableFns[1].fn();
|
||||
expect(spec4.execute).toHaveBeenCalled();
|
||||
queueableFns[1].fn('done');
|
||||
expect(
|
||||
jasmineUnderTest.TreeRunner.prototype._executeSpec
|
||||
).toHaveBeenCalledWith(spec4, 'done');
|
||||
|
||||
queueableFns[2].fn();
|
||||
expect(runQueue.calls.count()).toBe(3);
|
||||
expect(runQueue.calls.mostRecent().args[0].queueableFns.length).toBe(2);
|
||||
runQueue.calls.mostRecent().args[0].queueableFns[1].fn();
|
||||
expect(spec2.execute).toHaveBeenCalled();
|
||||
runQueue.calls.mostRecent().args[0].queueableFns[1].fn('done');
|
||||
expect(
|
||||
jasmineUnderTest.TreeRunner.prototype._executeSpec
|
||||
).toHaveBeenCalledWith(spec2, 'done');
|
||||
|
||||
queueableFns[3].fn();
|
||||
expect(spec5.execute).toHaveBeenCalled();
|
||||
queueableFns[3].fn('done');
|
||||
expect(
|
||||
jasmineUnderTest.TreeRunner.prototype._executeSpec
|
||||
).toHaveBeenCalledWith(spec5, 'done');
|
||||
|
||||
queueableFns[4].fn();
|
||||
expect(runQueue.calls.count()).toBe(4);
|
||||
expect(runQueue.calls.mostRecent().args[0].queueableFns.length).toBe(2);
|
||||
runQueue.calls.mostRecent().args[0].queueableFns[1].fn();
|
||||
expect(spec3.execute).toHaveBeenCalled();
|
||||
runQueue.calls.mostRecent().args[0].queueableFns[1].fn('done');
|
||||
expect(
|
||||
jasmineUnderTest.TreeRunner.prototype._executeSpec
|
||||
).toHaveBeenCalledWith(spec3, 'done');
|
||||
|
||||
await expectAsync(promise).toBePending();
|
||||
});
|
||||
@@ -754,11 +784,15 @@ describe('Runner', function() {
|
||||
runQueue.calls.mostRecent().args[0].queueableFns[1].fn();
|
||||
expect(runQueue.calls.count()).toBe(3);
|
||||
|
||||
runQueue.calls.mostRecent().args[0].queueableFns[1].fn();
|
||||
expect(spec1.execute).toHaveBeenCalled();
|
||||
runQueue.calls.mostRecent().args[0].queueableFns[1].fn('done');
|
||||
expect(
|
||||
jasmineUnderTest.TreeRunner.prototype._executeSpec
|
||||
).toHaveBeenCalledWith(spec1, 'done');
|
||||
|
||||
queueableFns[1].fn();
|
||||
expect(spec4.execute).toHaveBeenCalled();
|
||||
queueableFns[1].fn('done');
|
||||
expect(
|
||||
jasmineUnderTest.TreeRunner.prototype._executeSpec
|
||||
).toHaveBeenCalledWith(spec4, 'done');
|
||||
|
||||
queueableFns[2].fn();
|
||||
expect(runQueue.calls.count()).toBe(4);
|
||||
@@ -767,11 +801,15 @@ describe('Runner', function() {
|
||||
runQueue.calls.mostRecent().args[0].queueableFns[1].fn();
|
||||
expect(runQueue.calls.count()).toBe(5);
|
||||
|
||||
runQueue.calls.mostRecent().args[0].queueableFns[1].fn();
|
||||
expect(spec2.execute).toHaveBeenCalled();
|
||||
runQueue.calls.mostRecent().args[0].queueableFns[1].fn('done');
|
||||
expect(
|
||||
jasmineUnderTest.TreeRunner.prototype._executeSpec
|
||||
).toHaveBeenCalledWith(spec2, 'done');
|
||||
|
||||
queueableFns[3].fn();
|
||||
expect(spec5.execute).toHaveBeenCalled();
|
||||
queueableFns[3].fn('done');
|
||||
expect(
|
||||
jasmineUnderTest.TreeRunner.prototype._executeSpec
|
||||
).toHaveBeenCalledWith(spec5, 'done');
|
||||
|
||||
queueableFns[4].fn();
|
||||
expect(runQueue.calls.count()).toBe(6);
|
||||
@@ -780,8 +818,10 @@ describe('Runner', function() {
|
||||
runQueue.calls.mostRecent().args[0].queueableFns[1].fn();
|
||||
expect(runQueue.calls.count()).toBe(7);
|
||||
|
||||
runQueue.calls.mostRecent().args[0].queueableFns[1].fn();
|
||||
expect(spec3.execute).toHaveBeenCalled();
|
||||
runQueue.calls.mostRecent().args[0].queueableFns[1].fn('done');
|
||||
expect(
|
||||
jasmineUnderTest.TreeRunner.prototype._executeSpec
|
||||
).toHaveBeenCalledWith(spec3, 'done');
|
||||
|
||||
await expectAsync(promise).toBePending();
|
||||
});
|
||||
@@ -803,8 +843,10 @@ describe('Runner', function() {
|
||||
expect(queueableFns.length).toBe(11);
|
||||
|
||||
for (let i = 0; i < 11; i++) {
|
||||
queueableFns[i].fn();
|
||||
expect(specs[i].execute).toHaveBeenCalled();
|
||||
queueableFns[i].fn('done');
|
||||
expect(
|
||||
jasmineUnderTest.TreeRunner.prototype._executeSpec
|
||||
).toHaveBeenCalledWith(specs[i], 'done');
|
||||
}
|
||||
|
||||
await expectAsync(promise).toBePending();
|
||||
|
||||
@@ -33,170 +33,6 @@ describe('Spec', function() {
|
||||
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() {
|
||||
const startCallback = jasmine.createSpy('startCallback'),
|
||||
resultCallback = jasmine.createSpy('resultCallback'),
|
||||
@@ -209,234 +45,49 @@ describe('Spec', function() {
|
||||
expect(spec.status()).toBe('pending');
|
||||
});
|
||||
|
||||
it('can be excluded at execution time by a parent', function() {
|
||||
const fakeQueueRunner = jasmine.createSpy('fakeQueueRunner'),
|
||||
startCallback = jasmine.createSpy('startCallback'),
|
||||
specBody = jasmine.createSpy('specBody'),
|
||||
resultCallback = jasmine.createSpy('resultCallback'),
|
||||
spec = new jasmineUnderTest.Spec({
|
||||
queueableFn: { fn: specBody }
|
||||
describe('#executionFinished', function() {
|
||||
it('removes the fn if autoCleanClosures is true', function() {
|
||||
const spec = new jasmineUnderTest.Spec({
|
||||
queueableFn: { fn: () => {} },
|
||||
autoCleanClosures: true
|
||||
});
|
||||
|
||||
spec.execute(
|
||||
fakeQueueRunner,
|
||||
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
|
||||
spec.executionFinished();
|
||||
expect(spec.queueableFn.fn).toBeFalsy();
|
||||
});
|
||||
|
||||
function runQueue(config) {
|
||||
config.queueableFns.forEach(function(qf) {
|
||||
qf.fn();
|
||||
it('removes the fn after execution if autoCleanClosures is undefined', function() {
|
||||
const spec = new jasmineUnderTest.Spec({
|
||||
queueableFn: { fn: () => {} },
|
||||
autoCleanClosures: undefined
|
||||
});
|
||||
config.onComplete();
|
||||
}
|
||||
|
||||
spec.execute(runQueue, null, function() {}, resultCallback, function() {});
|
||||
expect(resultCallback).toHaveBeenCalled();
|
||||
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
|
||||
spec.executionFinished();
|
||||
expect(spec.queueableFn.fn).toBeFalsy();
|
||||
});
|
||||
|
||||
function runQueue(config) {
|
||||
config.queueableFns.forEach(function(qf) {
|
||||
qf.fn();
|
||||
it('does not remove the fn after execution if autoCleanClosures is false', function() {
|
||||
function originalFn() {}
|
||||
const spec = new jasmineUnderTest.Spec({
|
||||
queueableFn: { fn: originalFn },
|
||||
autoCleanClosures: false
|
||||
});
|
||||
config.onComplete();
|
||||
}
|
||||
|
||||
spec.execute(runQueue, null, function() {}, function() {}, done);
|
||||
expect(done).toHaveBeenCalled();
|
||||
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
|
||||
spec.executionFinished();
|
||||
expect(spec.queueableFn.fn).toBe(originalFn);
|
||||
});
|
||||
|
||||
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() {
|
||||
const done = jasmine.createSpy('done callback');
|
||||
function originalFn() {}
|
||||
const spec = new jasmineUnderTest.Spec({
|
||||
queueableFn: { fn: originalFn },
|
||||
autoCleanClosures: false
|
||||
describe('#setSpecProperty', function() {
|
||||
it('adds the property to the result', function() {
|
||||
const spec = new jasmineUnderTest.Spec({
|
||||
queueableFn: { fn: () => {} }
|
||||
});
|
||||
|
||||
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() {
|
||||
@@ -446,69 +97,84 @@ describe('Spec', function() {
|
||||
expect(spec.status()).toBe('passed');
|
||||
});
|
||||
|
||||
it('#status returns passed if all expectations in the spec have passed', function() {
|
||||
const spec = new jasmineUnderTest.Spec({
|
||||
queueableFn: { fn: jasmine.createSpy('spec body') }
|
||||
});
|
||||
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') }
|
||||
describe('#status', function() {
|
||||
it('returns "passed"" by default', function() {
|
||||
const spec = new jasmineUnderTest.Spec({
|
||||
queueableFn: { fn: () => {} }
|
||||
});
|
||||
spec.addExpectationResult(true, { message: 'expectation1' });
|
||||
spec.addExpectationResult(false, { message: 'expectation2' });
|
||||
expect(spec.status()).toBe('passed');
|
||||
});
|
||||
|
||||
spec.execute(fakeQueueRunner, null, function() {}, resultCallback);
|
||||
|
||||
const fns = fakeQueueRunner.calls.mostRecent().args[0].queueableFns;
|
||||
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
|
||||
it('returns "passed"" if all expectations passed', function() {
|
||||
const spec = new jasmineUnderTest.Spec({
|
||||
queueableFn: { fn: () => {} }
|
||||
});
|
||||
|
||||
spec.addExpectationResult(true, { message: 'passed' });
|
||||
expect(function() {
|
||||
spec.addExpectationResult(false, { message: 'failed' });
|
||||
}).toThrowError(jasmineUnderTest.errors.ExpectationFailed);
|
||||
spec.addExpectationResult(true, {});
|
||||
|
||||
spec.execute(fakeQueueRunner, null, function() {}, resultCallback);
|
||||
expect(spec.status()).toBe('passed');
|
||||
});
|
||||
|
||||
const fns = fakeQueueRunner.calls.mostRecent().args[0].queueableFns;
|
||||
fns[fns.length - 1].fn();
|
||||
expect(resultCallback.calls.first().args[0].passedExpectations).toEqual([
|
||||
jasmine.objectContaining({ message: 'passed' })
|
||||
]);
|
||||
expect(resultCallback.calls.first().args[0].failedExpectations).toEqual([
|
||||
jasmine.objectContaining({ message: 'failed' })
|
||||
]);
|
||||
it('returns "failed" if any expectation failed', function() {
|
||||
const spec = new jasmineUnderTest.Spec({
|
||||
queueableFn: { fn: () => {} }
|
||||
});
|
||||
|
||||
spec.addExpectationResult(true, {});
|
||||
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() {
|
||||
@@ -639,123 +305,47 @@ describe('Spec', function() {
|
||||
expect(spec.metadata.getPath()).toEqual(['expected val']);
|
||||
});
|
||||
|
||||
describe('when a spec is marked pending during execution', function() {
|
||||
it('should mark the spec as pending', function() {
|
||||
const fakeQueueRunner = function(opts) {
|
||||
opts.onException(
|
||||
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() {} }
|
||||
describe('#handleException', function() {
|
||||
it('records a failure', function() {
|
||||
const spec = new jasmineUnderTest.Spec({
|
||||
queueableFn: {}
|
||||
});
|
||||
|
||||
spec.handleException('foo');
|
||||
spec.execute(fakeQueueRunner, null, function() {}, resultCallback);
|
||||
spec.handleException('foo');
|
||||
|
||||
const args = fakeQueueRunner.calls.mostRecent().args[0];
|
||||
args.queueableFns[args.queueableFns.length - 1].fn();
|
||||
expect(resultCallback.calls.first().args[0].failedExpectations).toEqual([
|
||||
{
|
||||
message: 'foo thrown',
|
||||
matcherName: '',
|
||||
passed: false,
|
||||
expected: '',
|
||||
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'];
|
||||
expect(spec.result.failedExpectations).toEqual([
|
||||
{
|
||||
message: 'foo thrown',
|
||||
matcherName: '',
|
||||
passed: false,
|
||||
expected: '',
|
||||
actual: '',
|
||||
stack: null
|
||||
}
|
||||
]);
|
||||
});
|
||||
|
||||
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();
|
||||
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)"
|
||||
);
|
||||
expect(spec.result.failedExpectations).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#trace', function() {
|
||||
describe('#debugLog', function() {
|
||||
it('adds the messages to the result', function() {
|
||||
const timer = jasmine.createSpyObj('timer', ['start', 'elapsed']),
|
||||
spec = new jasmineUnderTest.Spec({
|
||||
queueableFn: {
|
||||
fn: function() {}
|
||||
},
|
||||
timer: timer
|
||||
}),
|
||||
t1 = 123,
|
||||
t2 = 456;
|
||||
const timer = jasmine.createSpyObj('timer', ['start', 'elapsed']);
|
||||
const spec = new jasmineUnderTest.Spec({
|
||||
queueableFn: { fn: () => {} },
|
||||
timer: timer
|
||||
});
|
||||
const t1 = 123;
|
||||
const t2 = 456;
|
||||
|
||||
spec.execute(() => {});
|
||||
expect(spec.result.debugLogs).toBeNull();
|
||||
timer.elapsed.and.returnValue(t1);
|
||||
spec.debugLog('msg 1');
|
||||
@@ -771,99 +361,36 @@ describe('Spec', function() {
|
||||
});
|
||||
|
||||
describe('When the spec passes', function() {
|
||||
it('omits the messages from the reported result', function() {
|
||||
const resultCallback = jasmine.createSpy('resultCallback'),
|
||||
spec = new jasmineUnderTest.Spec({
|
||||
queueableFn: {
|
||||
fn: function() {}
|
||||
}
|
||||
});
|
||||
it('removes the logs from the result', function() {
|
||||
const spec = new jasmineUnderTest.Spec({
|
||||
queueableFn: { fn: () => {} }
|
||||
});
|
||||
|
||||
function runQueue(config) {
|
||||
spec.debugLog('msg');
|
||||
for (const fn of config.queueableFns) {
|
||||
fn.fn();
|
||||
}
|
||||
config.onComplete(false);
|
||||
}
|
||||
spec.debugLog('msg');
|
||||
spec.executionFinished();
|
||||
|
||||
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();
|
||||
});
|
||||
});
|
||||
|
||||
describe('When the spec fails', function() {
|
||||
it('includes the messages in the reported result', function() {
|
||||
const resultCallback = jasmine.createSpy('resultCallback'),
|
||||
timer = jasmine.createSpyObj('timer', ['start', 'elapsed']),
|
||||
spec = new jasmineUnderTest.Spec({
|
||||
queueableFn: {
|
||||
fn: function() {}
|
||||
},
|
||||
timer: timer
|
||||
}),
|
||||
timestamp = 12345;
|
||||
it('includes the messages in the result', function() {
|
||||
const timer = jasmine.createSpyObj('timer', ['start', 'elapsed']);
|
||||
const spec = new jasmineUnderTest.Spec({
|
||||
queueableFn: { fn: () => {} },
|
||||
timer: timer
|
||||
});
|
||||
const timestamp = 12345;
|
||||
|
||||
timer.elapsed.and.returnValue(timestamp);
|
||||
|
||||
function runQueue(config) {
|
||||
spec.debugLog('msg');
|
||||
spec.handleException(new Error('nope'));
|
||||
for (const fn of config.queueableFns) {
|
||||
fn.fn();
|
||||
}
|
||||
config.onComplete(true);
|
||||
}
|
||||
spec.debugLog('msg');
|
||||
spec.handleException(new Error('nope'));
|
||||
spec.executionFinished();
|
||||
|
||||
spec.execute(
|
||||
runQueue,
|
||||
null,
|
||||
function() {},
|
||||
resultCallback,
|
||||
function() {}
|
||||
);
|
||||
expect(resultCallback).toHaveBeenCalledWith(
|
||||
jasmine.objectContaining({
|
||||
debugLogs: [{ message: 'msg', timestamp: timestamp }]
|
||||
}),
|
||||
undefined
|
||||
);
|
||||
expect(spec.result.debugLogs).toEqual([
|
||||
{ message: 'msg', timestamp: timestamp }
|
||||
]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -71,7 +71,131 @@ describe('TreeRunner', function() {
|
||||
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';
|
||||
spec.parentSuiteId = topSuiteId;
|
||||
const topSuite = new jasmineUnderTest.Suite({ id: topSuiteId });
|
||||
@@ -88,15 +212,19 @@ describe('TreeRunner', function() {
|
||||
const runQueue = jasmine.createSpy('runQueue');
|
||||
const reportDispatcher = mockReportDispatcher();
|
||||
const runableResources = mockRunableResources();
|
||||
const globalErrors = mockGlobalErrors();
|
||||
const setTimeout = jasmine.createSpy('setTimeout');
|
||||
const currentRunableTracker = new jasmineUnderTest.CurrentRunableTracker();
|
||||
const subject = new jasmineUnderTest.TreeRunner({
|
||||
executionTree,
|
||||
runQueue,
|
||||
globalErrors,
|
||||
setTimeout,
|
||||
runableResources,
|
||||
reportDispatcher,
|
||||
currentRunableTracker,
|
||||
getConfig() {
|
||||
return {};
|
||||
return optionalConfig || {};
|
||||
},
|
||||
reportChildrenOfBeforeAllFailure() {}
|
||||
});
|
||||
@@ -108,6 +236,8 @@ describe('TreeRunner', function() {
|
||||
|
||||
return {
|
||||
runQueue,
|
||||
globalErrors,
|
||||
setTimeout,
|
||||
currentRunableTracker,
|
||||
runableResources,
|
||||
reportDispatcher,
|
||||
@@ -136,4 +266,8 @@ describe('TreeRunner', function() {
|
||||
'clearForRunable'
|
||||
]);
|
||||
}
|
||||
|
||||
function mockGlobalErrors() {
|
||||
return jasmine.createSpyObj('globalErrors', ['reportUnhandledRejections']);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -2,7 +2,6 @@ getJasmineRequireObj().Spec = function(j$) {
|
||||
function Spec(attrs) {
|
||||
this.expectationFactory = attrs.expectationFactory;
|
||||
this.asyncExpectationFactory = attrs.asyncExpectationFactory;
|
||||
this.setTimeout = attrs.setTimeout;
|
||||
this.id = attrs.id;
|
||||
this.filename = attrs.filename;
|
||||
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() {
|
||||
/**
|
||||
* @typedef SpecResult
|
||||
@@ -255,6 +173,8 @@ getJasmineRequireObj().Spec = function(j$) {
|
||||
this.pend(message);
|
||||
};
|
||||
|
||||
// TODO: ensure that all access to result goes through .getResult()
|
||||
// so that the status is correct.
|
||||
Spec.prototype.getResult = function() {
|
||||
this.result.status = this.status();
|
||||
return this.result;
|
||||
|
||||
@@ -237,7 +237,6 @@ getJasmineRequireObj().SuiteBuilder = function(j$) {
|
||||
const config = this.env_.configuration();
|
||||
const suite = this.currentDeclarationSuite_;
|
||||
const parentSuiteId = suite === this.topSuite ? null : suite.id;
|
||||
const global = j$.getGlobal();
|
||||
const spec = new j$.Spec({
|
||||
id: 'spec' + this.nextSpecId_++,
|
||||
filename,
|
||||
@@ -245,7 +244,6 @@ getJasmineRequireObj().SuiteBuilder = function(j$) {
|
||||
beforeAndAfterFns: beforeAndAfterFns(suite),
|
||||
expectationFactory: this.expectationFactory_,
|
||||
asyncExpectationFactory: this.specAsyncExpectationFactory_,
|
||||
setTimeout: global.setTimeout.bind(global),
|
||||
onLateError: this.onLateError_,
|
||||
getPath: spec => this.getSpecPath_(spec, suite),
|
||||
description: description,
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
getJasmineRequireObj().TreeRunner = function(j$) {
|
||||
class TreeRunner {
|
||||
#executionTree;
|
||||
#setTimeout;
|
||||
#globalErrors;
|
||||
#runableResources;
|
||||
#reportDispatcher;
|
||||
@@ -13,6 +14,7 @@ getJasmineRequireObj().TreeRunner = function(j$) {
|
||||
constructor(attrs) {
|
||||
this.#executionTree = attrs.executionTree;
|
||||
this.#globalErrors = attrs.globalErrors;
|
||||
this.#setTimeout = attrs.setTimeout || setTimeout.bind(globalThis);
|
||||
this.#runableResources = attrs.runableResources;
|
||||
this.#reportDispatcher = attrs.reportDispatcher;
|
||||
this.#runQueue = attrs.runQueue;
|
||||
@@ -57,31 +59,103 @@ getJasmineRequireObj().TreeRunner = function(j$) {
|
||||
if (node.suite) {
|
||||
this.#executeSuiteSegment(node.suite, node.segmentNumber, done);
|
||||
} else {
|
||||
this.#executeSpec(node.spec, done);
|
||||
this._executeSpec(node.spec, done);
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
#executeSpec(spec, done) {
|
||||
const config = this.#getConfig();
|
||||
spec.execute(
|
||||
this.#runQueueWithSkipPolicy.bind(this),
|
||||
this.#globalErrors,
|
||||
next => {
|
||||
this.#currentRunableTracker.setCurrentSpec(spec);
|
||||
this.#runableResources.initForRunable(spec.id, spec.parentSuiteId);
|
||||
this.#reportDispatcher.specStarted(spec.result).then(next);
|
||||
},
|
||||
(result, next) => {
|
||||
this.#specComplete(spec).then(next);
|
||||
},
|
||||
done,
|
||||
this.#executionTree.isExcluded(spec),
|
||||
config.failSpecWithNoExpectations,
|
||||
config.detectLateRejectionHandling
|
||||
// Only exposed for testing.
|
||||
_executeSpec(spec, specOverallDone) {
|
||||
const onStart = next => {
|
||||
this.#currentRunableTracker.setCurrentSpec(spec);
|
||||
this.#runableResources.initForRunable(spec.id, spec.parentSuiteId);
|
||||
this.#reportDispatcher.specStarted(spec.result).then(next);
|
||||
};
|
||||
const resultCallback = (result, next) => {
|
||||
this.#specComplete(spec).then(next);
|
||||
};
|
||||
const queueableFns = this.#specQueueableFns(
|
||||
spec,
|
||||
onStart,
|
||||
resultCallback
|
||||
);
|
||||
|
||||
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) {
|
||||
|
||||
Reference in New Issue
Block a user