Backfill some unit tests for Runner's interaction with TreeProcessor

This commit is contained in:
Steve Gravrock
2025-07-29 09:48:32 -07:00
parent bb777e93e5
commit 5e88fde655
4 changed files with 278 additions and 2 deletions

View File

@@ -1663,6 +1663,7 @@ getJasmineRequireObj().Env = function(j$) {
runableResources,
reporter,
queueRunnerFactory,
TreeProcessor: j$.TreeProcessor,
getConfig: () => config,
reportSpecDone
});
@@ -9284,6 +9285,7 @@ getJasmineRequireObj().Runner = function(j$) {
this.focusedRunables_ = options.focusedRunables;
this.runableResources_ = options.runableResources;
this.queueRunnerFactory_ = options.queueRunnerFactory;
this.TreeProcessor_ = options.TreeProcessor;
this.reporter_ = options.reporter;
this.getConfig_ = options.getConfig;
this.reportSpecDone_ = options.reportSpecDone;
@@ -9331,7 +9333,7 @@ getJasmineRequireObj().Runner = function(j$) {
seed: j$.isNumber_(config.seed) ? config.seed + '' : config.seed
});
const processor = new j$.TreeProcessor({
const processor = new this.TreeProcessor_({
tree: this.topSuite_,
runnableIds: runablesToRun,
queueRunnerFactory: options => {

272
spec/core/RunnerSpec.js Normal file
View File

@@ -0,0 +1,272 @@
describe('Runner', function() {
describe('TreeProcessor nodeComplete callback', function() {
it('throws if the wrong suite is passed to nodeComplete', async function() {
const TreeProcessor = spyTreeProcessorCtor();
const subject = new jasmineUnderTest.Runner({
...defaultCtorOptions(),
TreeProcessor
});
const promise = subject.execute();
expect(TreeProcessor).toHaveBeenCalled();
await checkImmediateRejection(promise);
const treeProcessorOpts = TreeProcessor.calls.argsFor(0)[0];
const suiteToRun = {
parentSuite: stubSuite(),
startTimer: () => {}
};
await callWithNext(treeProcessorOpts.nodeStart, [suiteToRun]);
expect(function() {
const someOtherSuite = {};
treeProcessorOpts.nodeComplete(someOtherSuite, {}, () => {});
}).toThrowError('Tried to complete the wrong suite');
});
it("ends the suite's timer", async function() {
const TreeProcessor = spyTreeProcessorCtor();
const subject = new jasmineUnderTest.Runner({
...defaultCtorOptions(),
TreeProcessor
});
const promise = subject.execute();
expect(TreeProcessor).toHaveBeenCalled();
await checkImmediateRejection(promise);
const treeProcessorOpts = TreeProcessor.calls.argsFor(0)[0];
const suiteToRun = {
parentSuite: stubSuite(),
startTimer: () => {},
endTimer: jasmine.createSpy('suiteToRun.endTimer')
};
await callWithNext(treeProcessorOpts.nodeStart, [suiteToRun]);
await callWithNext(treeProcessorOpts.nodeComplete, [suiteToRun, {}]);
expect(suiteToRun.endTimer).toHaveBeenCalled();
});
it('sets hasFailures to true when the suite fails', async function() {
const TreeProcessor = spyTreeProcessorCtor();
const subject = new jasmineUnderTest.Runner({
...defaultCtorOptions(),
TreeProcessor
});
const promise = subject.execute();
expect(TreeProcessor).toHaveBeenCalled();
await checkImmediateRejection(promise);
const treeProcessorOpts = TreeProcessor.calls.argsFor(0)[0];
const suiteToRun = {
parentSuite: stubSuite(),
startTimer: () => {},
endTimer: jasmine.createSpy('suiteToRun.endTimer')
};
await callWithNext(treeProcessorOpts.nodeStart, [suiteToRun]);
await callWithNext(treeProcessorOpts.nodeComplete, [
suiteToRun,
{ status: 'failed' }
]);
expect(subject.hasFailures).toBe(true);
});
it('does not set hasFailures to true when the suite passes', async function() {
const TreeProcessor = spyTreeProcessorCtor();
const subject = new jasmineUnderTest.Runner({
...defaultCtorOptions(),
TreeProcessor
});
const promise = subject.execute();
expect(TreeProcessor).toHaveBeenCalled();
await checkImmediateRejection(promise);
const treeProcessorOpts = TreeProcessor.calls.argsFor(0)[0];
const suiteToRun = {
parentSuite: stubSuite(),
startTimer: () => {},
endTimer: jasmine.createSpy('suiteToRun.endTimer')
};
await callWithNext(treeProcessorOpts.nodeStart, [suiteToRun]);
await callWithNext(treeProcessorOpts.nodeComplete, [
suiteToRun,
{ status: 'passed' }
]);
expect(subject.hasFailures).toBe(false);
});
it('does not set hasFailures to false when the suite passes', async function() {
const TreeProcessor = spyTreeProcessorCtor();
const subject = new jasmineUnderTest.Runner({
...defaultCtorOptions(),
TreeProcessor
});
const promise = subject.execute();
expect(TreeProcessor).toHaveBeenCalled();
await checkImmediateRejection(promise);
const treeProcessorOpts = TreeProcessor.calls.argsFor(0)[0];
subject.hasFailures = true;
const suiteToRun = {
parentSuite: stubSuite(),
startTimer: () => {},
endTimer: jasmine.createSpy('suiteToRun.endTimer')
};
await callWithNext(treeProcessorOpts.nodeStart, [suiteToRun]);
await callWithNext(treeProcessorOpts.nodeComplete, [
suiteToRun,
{ status: 'passed' }
]);
expect(subject.hasFailures).toBe(true);
});
describe('reporting', function() {
it('reports the suiteDone event', async function() {
const TreeProcessor = spyTreeProcessorCtor();
const reporter = spyReporter();
const subject = new jasmineUnderTest.Runner({
...defaultCtorOptions(),
TreeProcessor,
reporter
});
const promise = subject.execute();
expect(TreeProcessor).toHaveBeenCalled();
await checkImmediateRejection(promise);
const treeProcessorOpts = TreeProcessor.calls.argsFor(0)[0];
const suiteToRun = {
parentSuite: stubSuite(),
startTimer: () => {},
endTimer: jasmine.createSpy('suiteToRun.endTimer')
};
await callWithNext(treeProcessorOpts.nodeStart, [suiteToRun]);
await callWithNext(treeProcessorOpts.nodeComplete, [
suiteToRun,
{ status: 'passed' }
]);
expect(reporter.suiteDone).toHaveBeenCalled();
});
describe('when the suite had a beforeAll failure', function() {
it('reports children before the suiteDone event', async function() {
const TreeProcessor = spyTreeProcessorCtor();
const reporter = spyReporter();
const reportSpecDone = jasmine
.createSpy('reportSpecDone')
.and.callFake(function(child, result, next) {
next();
});
const subject = new jasmineUnderTest.Runner({
...defaultCtorOptions(),
TreeProcessor,
reporter,
reportSpecDone
});
const promise = subject.execute();
expect(TreeProcessor).toHaveBeenCalled();
await checkImmediateRejection(promise);
const treeProcessorOpts = TreeProcessor.calls.argsFor(0)[0];
const suiteToRun = {
parentSuite: stubSuite(),
children: [
{
result: { description: 'a spec' },
addExpectationResult: jasmine.createSpy('addExpectationResult')
}
],
startTimer: () => {},
endTimer: jasmine.createSpy('suiteToRun.endTimer')
};
await callWithNext(treeProcessorOpts.nodeStart, [suiteToRun]);
suiteToRun.hadBeforeAllFailure = true;
await callWithNext(treeProcessorOpts.nodeComplete, [
suiteToRun,
{ status: 'passed' }
]);
expect(
suiteToRun.children[0].addExpectationResult
).toHaveBeenCalledWith(
false,
{
passed: false,
message:
'Not run because a beforeAll function failed. The beforeAll failure will be reported on the suite that caused it.'
},
true
);
expect(
suiteToRun.children[0].addExpectationResult
).toHaveBeenCalledBefore(reportSpecDone);
expect(reportSpecDone).toHaveBeenCalledBefore(reporter.suiteDone);
expect(reporter.specStarted).toHaveBeenCalledBefore(reportSpecDone);
});
});
});
});
async function callWithNext(fn, args) {
let next;
const nextPromise = new Promise(function(resolve) {
next = resolve;
});
fn.apply(null, [...args, next]);
await nextPromise;
}
// Check whether promise has already been rejected
async function checkImmediateRejection(promise) {
await Promise.race([promise, Promise.resolve()]);
}
function spyTreeProcessorCtor() {
return jasmine.createSpy('TreeProcessor ctor').and.returnValue({
execute: () => {},
processTree: () => ({ valid: true })
});
}
function spyReporter() {
return jasmine.createSpyObj('reporter', {
jasmineStarted: Promise.resolve(),
jasmineDone: Promise.resolve(),
suiteStarted: Promise.resolve(),
suiteDone: Promise.resolve(),
specStarted: Promise.resolve(),
specDone: Promise.resolve()
});
}
function defaultCtorOptions() {
return {
topSuite: stubSuite(),
runableResources: {
initForRunable: () => {},
clearForRunable: () => {}
},
reporter: spyReporter(),
focusedRunables: () => [],
getConfig: () => ({}),
totalSpecsDefined: () => 1
};
}
function stubSuite() {
return {
result: {
failedExpectations: []
}
};
}
});

View File

@@ -497,6 +497,7 @@ getJasmineRequireObj().Env = function(j$) {
runableResources,
reporter,
queueRunnerFactory,
TreeProcessor: j$.TreeProcessor,
getConfig: () => config,
reportSpecDone
});

View File

@@ -7,6 +7,7 @@ getJasmineRequireObj().Runner = function(j$) {
this.focusedRunables_ = options.focusedRunables;
this.runableResources_ = options.runableResources;
this.queueRunnerFactory_ = options.queueRunnerFactory;
this.TreeProcessor_ = options.TreeProcessor;
this.reporter_ = options.reporter;
this.getConfig_ = options.getConfig;
this.reportSpecDone_ = options.reportSpecDone;
@@ -54,7 +55,7 @@ getJasmineRequireObj().Runner = function(j$) {
seed: j$.isNumber_(config.seed) ? config.seed + '' : config.seed
});
const processor = new j$.TreeProcessor({
const processor = new this.TreeProcessor_({
tree: this.topSuite_,
runnableIds: runablesToRun,
queueRunnerFactory: options => {