507 lines
17 KiB
JavaScript
507 lines
17 KiB
JavaScript
describe('Runner', function() {
|
|
describe('Integration with TreeProcessor and TreeRunner', function() {
|
|
let suiteNumber,
|
|
specNumber,
|
|
runQueue,
|
|
globalErrors,
|
|
reportDispatcher,
|
|
failSpecWithNoExpectations,
|
|
detectLateRejectionHandling;
|
|
|
|
beforeEach(function() {
|
|
suiteNumber = 0;
|
|
specNumber = 0;
|
|
runQueue = jasmine.createSpy('runQueue');
|
|
globalErrors = 'the global errors instance';
|
|
reportDispatcher = jasmine.createSpyObj(
|
|
'reportDispatcher',
|
|
privateUnderTest.reporterEvents
|
|
);
|
|
|
|
for (const k of privateUnderTest.reporterEvents) {
|
|
reportDispatcher[k].and.returnValue(Promise.resolve());
|
|
}
|
|
|
|
// Reasonable defaults, may be overridden in some cases
|
|
failSpecWithNoExpectations = false;
|
|
detectLateRejectionHandling = false;
|
|
|
|
spyOn(privateUnderTest.TreeRunner.prototype, '_executeSpec');
|
|
});
|
|
|
|
function StubSuite(attrs) {
|
|
attrs = attrs || {};
|
|
this.id = 'suite' + suiteNumber++;
|
|
this.children = attrs.children || [];
|
|
this.markedPending = attrs.markedPending || false;
|
|
this.sharedUserContext = function() {
|
|
return attrs.userContext || {};
|
|
};
|
|
this.startedEvent = jasmine.createSpy('startedEvent');
|
|
this.doneEvent = jasmine.createSpy('doneEvent');
|
|
this.hasOwnFailedExpectations = jasmine.createSpy(
|
|
'hasOwnFailedExpectations'
|
|
);
|
|
this.beforeAllFns = attrs.beforeAllFns || [];
|
|
this.afterAllFns = attrs.afterAllFns || [];
|
|
this.cleanupBeforeAfter = function() {};
|
|
this.startTimer = function() {};
|
|
this.endTimer = function() {};
|
|
}
|
|
|
|
function StubSpec(attrs) {
|
|
attrs = attrs || {};
|
|
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) {
|
|
const defaultOptions = {
|
|
getConfig: () => ({
|
|
specFilter: () => true,
|
|
failSpecWithNoExpectations,
|
|
detectLateRejectionHandling
|
|
}),
|
|
focusedRunables: () => [],
|
|
totalSpecsDefined: () => 1,
|
|
TreeProcessor: privateUnderTest.TreeProcessor,
|
|
runableResources: {
|
|
initForRunable: () => {},
|
|
clearForRunable: () => {}
|
|
},
|
|
reportDispatcher,
|
|
globalErrors,
|
|
runQueue
|
|
};
|
|
return new privateUnderTest.Runner({
|
|
...defaultOptions,
|
|
topSuite
|
|
});
|
|
}
|
|
|
|
function arrayNotContaining(item) {
|
|
return {
|
|
asymmetricMatch(other, matchersUtil) {
|
|
if (!Array.isArray(other)) {
|
|
return false;
|
|
}
|
|
|
|
for (const x of other) {
|
|
if (matchersUtil.equals(x, item)) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
};
|
|
}
|
|
|
|
// Precondition: privateUnderTest.TreeRunner.prototype._executeSpec is a spy
|
|
function verifyAndFinishSpec(spec, queueableFn, shouldBeExcluded) {
|
|
const ex = privateUnderTest.TreeRunner.prototype._executeSpec;
|
|
ex.withArgs(spec, 'onComplete').and.callThrough();
|
|
|
|
queueableFn.fn('onComplete');
|
|
expect(ex).toHaveBeenCalledWith(spec, 'onComplete');
|
|
|
|
expect(runQueue).toHaveBeenCalledWith(
|
|
jasmine.objectContaining({
|
|
isLeaf: true,
|
|
SkipPolicy: privateUnderTest.CompleteOnFirstErrorSkipPolicy,
|
|
queueableFns: shouldBeExcluded
|
|
? arrayNotContaining(spec.queueableFn)
|
|
: jasmine.arrayContaining([spec.queueableFn])
|
|
})
|
|
);
|
|
}
|
|
|
|
it('runs a single spec', async function() {
|
|
const spec = new StubSpec();
|
|
const topSuite = new StubSuite({
|
|
children: [spec],
|
|
userContext: { root: 'context' }
|
|
});
|
|
topSuite.doneEvent.and.returnValue({});
|
|
detectLateRejectionHandling = true;
|
|
const subject = makeRunner(topSuite);
|
|
|
|
const promise = subject.execute();
|
|
await Promise.resolve();
|
|
|
|
expect(runQueue).toHaveBeenCalledWith({
|
|
onComplete: jasmine.any(Function),
|
|
onException: jasmine.any(Function),
|
|
userContext: { root: 'context' },
|
|
queueableFns: [{ fn: jasmine.any(Function) }],
|
|
onMultipleDone: null,
|
|
SkipPolicy: privateUnderTest.SkipAfterBeforeAllErrorPolicy
|
|
});
|
|
|
|
const runQueueArgs = runQueue.calls.mostRecent().args[0];
|
|
verifyAndFinishSpec(spec, runQueueArgs.queueableFns[0], false);
|
|
runQueueArgs.onComplete();
|
|
await promise;
|
|
});
|
|
|
|
it('runs an empty suite', async function() {
|
|
const suite = new StubSuite({ userContext: { for: 'suite' } });
|
|
const topSuite = new StubSuite({
|
|
children: [suite],
|
|
userContext: { for: 'topSuite' }
|
|
});
|
|
topSuite.doneEvent.and.returnValue({});
|
|
suite.parentSuite = topSuite;
|
|
const subject = makeRunner(topSuite);
|
|
|
|
const promise = subject.execute();
|
|
await Promise.resolve();
|
|
|
|
expect(runQueue).toHaveBeenCalledWith({
|
|
onComplete: jasmine.any(Function),
|
|
onException: jasmine.any(Function),
|
|
userContext: { for: 'topSuite' },
|
|
queueableFns: [{ fn: jasmine.any(Function) }],
|
|
onMultipleDone: null,
|
|
SkipPolicy: privateUnderTest.SkipAfterBeforeAllErrorPolicy
|
|
});
|
|
|
|
const runQueueArgs = runQueue.calls.mostRecent().args[0];
|
|
const nodeDone = jasmine.createSpy('nodeDone');
|
|
runQueueArgs.queueableFns[0].fn(nodeDone);
|
|
expect(runQueue).toHaveBeenCalledWith({
|
|
onComplete: jasmine.any(Function),
|
|
onMultipleDone: null,
|
|
queueableFns: [{ fn: jasmine.any(Function) }],
|
|
userContext: { for: 'suite' },
|
|
onException: jasmine.any(Function),
|
|
onMultipleDone: null,
|
|
SkipPolicy: privateUnderTest.SkipAfterBeforeAllErrorPolicy
|
|
});
|
|
|
|
suite.startedEvent.and.returnValue('suite started event');
|
|
runQueue.calls.mostRecent().args[0].queueableFns[0].fn('foo');
|
|
expect(reportDispatcher.suiteStarted).toHaveBeenCalledWith(
|
|
'suite started event'
|
|
);
|
|
|
|
suite.doneEvent.and.returnValue({ my: 'result' });
|
|
|
|
runQueue.calls.mostRecent().args[0].onComplete();
|
|
expect(reportDispatcher.suiteDone).toHaveBeenCalledWith({ my: 'result' });
|
|
|
|
runQueueArgs.onComplete();
|
|
await promise;
|
|
});
|
|
|
|
it('runs a non-empty suite', async function() {
|
|
const spec1 = new StubSpec();
|
|
const spec2 = new StubSpec();
|
|
const suite = new StubSuite({ children: [spec1, spec2] });
|
|
const topSuite = new StubSuite({ children: [suite] });
|
|
const subject = makeRunner(topSuite);
|
|
|
|
const promise = subject.execute();
|
|
await Promise.resolve();
|
|
expect(runQueue).toHaveBeenCalledTimes(1);
|
|
let queueableFns = runQueue.calls.mostRecent().args[0].queueableFns;
|
|
queueableFns[0].fn(function() {});
|
|
|
|
expect(runQueue).toHaveBeenCalledTimes(2);
|
|
queueableFns = runQueue.calls.mostRecent().args[0].queueableFns;
|
|
expect(queueableFns.length).toBe(3);
|
|
|
|
verifyAndFinishSpec(spec1, queueableFns[1], false);
|
|
verifyAndFinishSpec(spec2, queueableFns[2], false);
|
|
await expectAsync(promise).toBePending();
|
|
});
|
|
|
|
it('"runs" an excluded suite', async function() {
|
|
const spec = new StubSpec();
|
|
const parent = new StubSuite({ children: [spec] });
|
|
const topSuite = new StubSuite({ children: [parent] });
|
|
parent.parentSuite = topSuite;
|
|
const subject = makeRunner(topSuite);
|
|
|
|
// Empty list of runable IDs excludes everything
|
|
const promise = subject.execute([]);
|
|
await Promise.resolve();
|
|
expect(runQueue).toHaveBeenCalledTimes(1);
|
|
let queueableFns = runQueue.calls.mostRecent().args[0].queueableFns;
|
|
queueableFns[0].fn(function() {});
|
|
|
|
queueableFns = runQueue.calls.mostRecent().args[0].queueableFns;
|
|
expect(queueableFns.length).toBe(2);
|
|
|
|
parent.startedEvent.and.returnValue('parent suite started event');
|
|
queueableFns[0].fn();
|
|
expect(reportDispatcher.suiteStarted).toHaveBeenCalledWith(
|
|
'parent suite started event'
|
|
);
|
|
|
|
verifyAndFinishSpec(spec, queueableFns[1], true);
|
|
|
|
parent.doneEvent.and.returnValue('parent suite done event');
|
|
runQueue.calls.argsFor(1)[0].onComplete();
|
|
expect(reportDispatcher.suiteDone).toHaveBeenCalledWith(
|
|
'parent suite done event'
|
|
);
|
|
await expectAsync(promise).toBePending();
|
|
});
|
|
|
|
it('handles the failSpecWithNoExpectations option', async function() {
|
|
failSpecWithNoExpectations = true;
|
|
const spec = new StubSpec();
|
|
const parent = new StubSuite({ children: [spec] });
|
|
const topSuite = new StubSuite({ children: [parent] });
|
|
parent.parentSuite = topSuite;
|
|
const subject = makeRunner(topSuite);
|
|
|
|
const promise = subject.execute();
|
|
await Promise.resolve();
|
|
expect(runQueue).toHaveBeenCalledTimes(1);
|
|
let queueableFns = runQueue.calls.mostRecent().args[0].queueableFns;
|
|
queueableFns[0].fn(function() {});
|
|
|
|
queueableFns = runQueue.calls.mostRecent().args[0].queueableFns;
|
|
expect(queueableFns.length).toBe(2);
|
|
|
|
queueableFns[1].fn('foo');
|
|
expect(
|
|
privateUnderTest.TreeRunner.prototype._executeSpec
|
|
).toHaveBeenCalledWith(spec, 'foo');
|
|
|
|
await expectAsync(promise).toBePending();
|
|
});
|
|
|
|
it('runs beforeAlls and afterAlls for a suite with children', async function() {
|
|
const spec = new StubSpec();
|
|
const target = new StubSuite({
|
|
children: [spec],
|
|
beforeAllFns: [
|
|
{ fn: 'beforeAll1', timeout: 1 },
|
|
{ fn: 'beforeAll2', timeout: 2 }
|
|
],
|
|
afterAllFns: [
|
|
{ fn: 'afterAll1', timeout: 3 },
|
|
{ fn: 'afterAll2', timeout: 4 }
|
|
]
|
|
});
|
|
const topSuite = new StubSuite({ children: [target] });
|
|
const subject = makeRunner(topSuite);
|
|
|
|
const promise = subject.execute();
|
|
await Promise.resolve();
|
|
expect(runQueue).toHaveBeenCalledTimes(1);
|
|
const queueableFns = runQueue.calls.mostRecent().args[0].queueableFns;
|
|
queueableFns[0].fn(function() {});
|
|
|
|
expect(runQueue.calls.mostRecent().args[0].queueableFns).toEqual([
|
|
{ fn: jasmine.any(Function) },
|
|
{ fn: 'beforeAll1', timeout: 1 },
|
|
{ fn: 'beforeAll2', timeout: 2 },
|
|
{ fn: jasmine.any(Function) },
|
|
{ fn: 'afterAll1', timeout: 3 },
|
|
{ fn: 'afterAll2', timeout: 4 }
|
|
]);
|
|
|
|
await expectAsync(promise).toBePending();
|
|
});
|
|
|
|
it('does not run beforeAlls or afterAlls for a suite with no children', async function() {
|
|
const target = new StubSuite({
|
|
beforeAllFns: [{ fn: 'before' }],
|
|
afterAllFns: [{ fn: 'after' }]
|
|
});
|
|
const topSuite = new StubSuite({ children: [target] });
|
|
const subject = makeRunner(topSuite);
|
|
|
|
const promise = subject.execute();
|
|
await Promise.resolve();
|
|
expect(runQueue).toHaveBeenCalledTimes(1);
|
|
const queueableFns = runQueue.calls.mostRecent().args[0].queueableFns;
|
|
queueableFns[0].fn(function() {});
|
|
|
|
expect(runQueue.calls.mostRecent().args[0].queueableFns.length).toEqual(
|
|
1
|
|
);
|
|
|
|
await expectAsync(promise).toBePending();
|
|
});
|
|
|
|
it('does not run beforeAlls or afterAlls for a suite with only pending children', async function() {
|
|
const spec = new StubSpec({ markedPending: true });
|
|
const target = new StubSuite({
|
|
children: [spec],
|
|
beforeAllFns: [{ fn: 'before' }],
|
|
afterAllFns: [{ fn: 'after' }]
|
|
});
|
|
const topSuite = new StubSuite({ children: [target] });
|
|
const subject = makeRunner(topSuite);
|
|
|
|
const promise = subject.execute();
|
|
await Promise.resolve();
|
|
expect(runQueue).toHaveBeenCalledTimes(1);
|
|
const queueableFns = runQueue.calls.mostRecent().args[0].queueableFns;
|
|
queueableFns[0].fn(function() {});
|
|
|
|
expect(runQueue.calls.mostRecent().args[0].queueableFns.length).toEqual(
|
|
2
|
|
);
|
|
|
|
await expectAsync(promise).toBePending();
|
|
});
|
|
|
|
it('runs specs in the order specified', async function() {
|
|
const specs = [new StubSpec(), new StubSpec()];
|
|
const topSuite = new StubSuite({ children: specs });
|
|
const subject = makeRunner(topSuite);
|
|
|
|
const promise = subject.execute([specs[1].id, specs[0].id]);
|
|
await Promise.resolve();
|
|
expect(runQueue).toHaveBeenCalledTimes(1);
|
|
const queueableFns = runQueue.calls.mostRecent().args[0].queueableFns;
|
|
queueableFns[0].fn('done');
|
|
|
|
expect(
|
|
privateUnderTest.TreeRunner.prototype._executeSpec
|
|
).not.toHaveBeenCalledWith(specs[0], jasmine.anything());
|
|
expect(
|
|
privateUnderTest.TreeRunner.prototype._executeSpec
|
|
).toHaveBeenCalledWith(specs[1], 'done');
|
|
|
|
queueableFns[1].fn('done');
|
|
|
|
expect(
|
|
privateUnderTest.TreeRunner.prototype._executeSpec
|
|
).toHaveBeenCalledWith(specs[0], 'done');
|
|
|
|
await expectAsync(promise).toBePending();
|
|
});
|
|
|
|
it('runs specified specs before non-specified specs within a suite', async function() {
|
|
const specified = new StubSpec();
|
|
const nonSpecified = new StubSpec();
|
|
const topSuite = new StubSuite({ children: [nonSpecified, specified] });
|
|
const subject = makeRunner(topSuite);
|
|
|
|
const promise = subject.execute([specified.id]);
|
|
await Promise.resolve();
|
|
expect(runQueue).toHaveBeenCalledTimes(1);
|
|
const queueableFns = runQueue.calls.mostRecent().args[0].queueableFns;
|
|
queueableFns[0].fn('done');
|
|
|
|
expect(
|
|
privateUnderTest.TreeRunner.prototype._executeSpec
|
|
).not.toHaveBeenCalledWith(nonSpecified, jasmine.anything());
|
|
expect(
|
|
privateUnderTest.TreeRunner.prototype._executeSpec
|
|
).toHaveBeenCalledWith(specified, 'done');
|
|
|
|
queueableFns[1].fn('done');
|
|
|
|
expect(
|
|
privateUnderTest.TreeRunner.prototype._executeSpec
|
|
).toHaveBeenCalledWith(nonSpecified, 'done');
|
|
|
|
await expectAsync(promise).toBePending();
|
|
});
|
|
|
|
it('runs suites and specs with a specified order', async function() {
|
|
const specifiedSpec = new StubSpec();
|
|
const nonSpecifiedSpec = new StubSpec();
|
|
const specifiedSuite = new StubSuite({ children: [nonSpecifiedSpec] });
|
|
const topSuite = new StubSuite({
|
|
children: [specifiedSpec, specifiedSuite]
|
|
});
|
|
const subject = makeRunner(topSuite);
|
|
|
|
const promise = subject.execute([specifiedSuite.id, specifiedSpec.id]);
|
|
await Promise.resolve();
|
|
expect(runQueue).toHaveBeenCalledTimes(1);
|
|
const queueableFns = runQueue.calls.mostRecent().args[0].queueableFns;
|
|
queueableFns[0].fn();
|
|
|
|
expect(specifiedSpec.execute).not.toHaveBeenCalled();
|
|
const nodeQueueableFns = runQueue.calls.mostRecent().args[0].queueableFns;
|
|
nodeQueueableFns[1].fn('done');
|
|
expect(
|
|
privateUnderTest.TreeRunner.prototype._executeSpec
|
|
).toHaveBeenCalledWith(nonSpecifiedSpec, 'done');
|
|
|
|
queueableFns[1].fn('done');
|
|
expect(
|
|
privateUnderTest.TreeRunner.prototype._executeSpec
|
|
).toHaveBeenCalledWith(specifiedSpec, 'done');
|
|
|
|
await expectAsync(promise).toBePending();
|
|
});
|
|
|
|
it('runs suites and specs in the order they were declared', async function() {
|
|
const spec1 = new StubSpec();
|
|
const spec2 = new StubSpec();
|
|
const spec3 = new StubSpec();
|
|
const parent = new StubSuite({ children: [spec2, spec3] });
|
|
const topSuite = new StubSuite({ children: [spec1, parent] });
|
|
const subject = makeRunner(topSuite);
|
|
|
|
const promise = subject.execute();
|
|
await Promise.resolve();
|
|
expect(runQueue).toHaveBeenCalledTimes(1);
|
|
const queueableFns = runQueue.calls.mostRecent().args[0].queueableFns;
|
|
expect(queueableFns.length).toBe(2);
|
|
|
|
queueableFns[0].fn('done');
|
|
expect(
|
|
privateUnderTest.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('done');
|
|
expect(
|
|
privateUnderTest.TreeRunner.prototype._executeSpec
|
|
).toHaveBeenCalledWith(spec2, 'done');
|
|
|
|
childFns[2].fn('done');
|
|
expect(
|
|
privateUnderTest.TreeRunner.prototype._executeSpec
|
|
).toHaveBeenCalledWith(spec3, 'done');
|
|
|
|
await expectAsync(promise).toBePending();
|
|
});
|
|
|
|
it('runs large segments of nodes in the order they were declared', async function() {
|
|
const specs = [];
|
|
|
|
for (let i = 0; i < 11; i++) {
|
|
specs.push(new StubSpec());
|
|
}
|
|
|
|
const topSuite = new StubSuite({ children: specs });
|
|
const subject = makeRunner(topSuite);
|
|
|
|
const promise = subject.execute();
|
|
await Promise.resolve();
|
|
expect(runQueue).toHaveBeenCalledTimes(1);
|
|
const queueableFns = runQueue.calls.mostRecent().args[0].queueableFns;
|
|
expect(queueableFns.length).toBe(11);
|
|
|
|
for (let i = 0; i < 11; i++) {
|
|
queueableFns[i].fn('done');
|
|
expect(
|
|
privateUnderTest.TreeRunner.prototype._executeSpec
|
|
).toHaveBeenCalledWith(specs[i], 'done');
|
|
}
|
|
|
|
await expectAsync(promise).toBePending();
|
|
});
|
|
});
|
|
});
|