Adds new configuration option to failSpecWithNoExpectations that will report specs without expectations as failures if enabled

This commit is contained in:
Dmitriy T
2019-08-22 16:08:43 -04:00
committed by Steve Gravrock
parent e8870db8d3
commit 7263a38c3f
9 changed files with 172 additions and 48 deletions

View File

@@ -118,7 +118,7 @@ The easiest way to run the tests in **Internet Explorer** is to run a VM that ha
1. Ensure all specs are green in browser *and* node
1. Ensure eslint and prettier are clean as part of your `npm test` command. You can run `npm run cleanup` to have prettier re-write the files.
1. Build `jasmine.js` with `grunt buildDistribution` and run all specs again - this ensures that your changes self-test well
1. Build `jasmine.js` with `npm run build` and run all specs again - this ensures that your changes self-test well
1. Revert your changes to `jasmine.js` and `jasmine-html.js`
* We do this because `jasmine.js` and `jasmine-html.js` are auto-generated (as you've seen in the previous steps) and accepting multiple pull requests when this auto-generated file changes causes lots of headaches
* When we accept your pull request, we will generate these files as a separate commit and merge the entire branch into master

View File

@@ -16,6 +16,7 @@
"posttest": "eslint src/**/*.js spec/**/*.js && prettier --check src/**/*.js spec/**/*.js",
"test": "grunt execSpecsInNode",
"cleanup": "prettier --write src/**/*.js spec/**/*.js",
"build": "grunt buildDistribution",
"serve": "node spec/support/localJasmineBrowser.js",
"serve:performance": "node spec/support/localJasmineBrowser.js jasmine-browser-performance.json",
"ci": "node spec/support/ci.js",

View File

@@ -296,7 +296,7 @@ describe('TreeProcessor', function() {
queueRunner.calls.mostRecent().args[0].queueableFns[0].fn('foo');
expect(leaf.execute).toHaveBeenCalledWith('foo', false);
expect(leaf.execute).toHaveBeenCalledWith('foo', false, false);
});
it('runs a node with no children', function() {
@@ -368,10 +368,10 @@ describe('TreeProcessor', function() {
expect(queueableFns.length).toBe(3);
queueableFns[1].fn('foo');
expect(leaf1.execute).toHaveBeenCalledWith('foo', false);
expect(leaf1.execute).toHaveBeenCalledWith('foo', false, false);
queueableFns[2].fn('bar');
expect(leaf2.execute).toHaveBeenCalledWith('bar', false);
expect(leaf2.execute).toHaveBeenCalledWith('bar', false, false);
});
it('cascades errors up the tree', function() {
@@ -397,7 +397,7 @@ describe('TreeProcessor', function() {
expect(queueableFns.length).toBe(2);
queueableFns[1].fn('foo');
expect(leaf.execute).toHaveBeenCalledWith('foo', false);
expect(leaf.execute).toHaveBeenCalledWith('foo', false, false);
queueRunner.calls.mostRecent().args[0].onComplete('things');
expect(nodeComplete).toHaveBeenCalled();
@@ -433,7 +433,7 @@ describe('TreeProcessor', function() {
expect(nodeStart).toHaveBeenCalledWith(node, 'bar');
queueableFns[1].fn('foo');
expect(leaf1.execute).toHaveBeenCalledWith('foo', true);
expect(leaf1.execute).toHaveBeenCalledWith('foo', true, false);
node.getResult.and.returnValue({ im: 'disabled' });
@@ -445,6 +445,35 @@ describe('TreeProcessor', function() {
);
});
it('should execute node with correct arguments when failSpecWithNoExpectations option is set', function() {
var leaf = new Leaf(),
node = new Node({ children: [leaf] }),
root = new Node({ children: [node] }),
queueRunner = jasmine.createSpy('queueRunner'),
nodeStart = jasmine.createSpy('nodeStart'),
nodeComplete = jasmine.createSpy('nodeComplete'),
processor = new jasmineUnderTest.TreeProcessor({
tree: root,
runnableIds: [],
queueRunnerFactory: queueRunner,
nodeStart: nodeStart,
nodeComplete: nodeComplete,
failSpecWithNoExpectations: true
}),
treeComplete = jasmine.createSpy('treeComplete'),
nodeDone = jasmine.createSpy('nodeDone');
processor.execute(treeComplete);
var queueableFns = queueRunner.calls.mostRecent().args[0].queueableFns;
queueableFns[0].fn(nodeDone);
queueableFns = queueRunner.calls.mostRecent().args[0].queueableFns;
expect(queueableFns.length).toBe(2);
queueableFns[1].fn('foo');
expect(leaf.execute).toHaveBeenCalledWith('foo', true, true);
});
it('runs beforeAlls for a node with children', function() {
var leaf = new Leaf(),
node = new Node({
@@ -600,11 +629,11 @@ describe('TreeProcessor', function() {
queueableFns[0].fn();
expect(nonSpecified.execute).not.toHaveBeenCalled();
expect(specified.execute).toHaveBeenCalledWith(undefined, false);
expect(specified.execute).toHaveBeenCalledWith(undefined, false, false);
queueableFns[1].fn();
expect(nonSpecified.execute).toHaveBeenCalledWith(undefined, true);
expect(nonSpecified.execute).toHaveBeenCalledWith(undefined, true, false);
});
it('runs nodes and leaves with a specified order', function() {

View File

@@ -2138,6 +2138,41 @@ describe("Env integration", function() {
});
});
describe('when spec has no expectations', function() {
var env, reporter;
beforeEach(function() {
env = new jasmineUnderTest.Env();
reporter = jasmine.createSpyObj('reporter', ['jasmineDone', 'suiteDone', 'specDone']);
env.addReporter(reporter);
env.it('is a spec without any expectations', function() {
// does nothing, just a mock spec without expectations
});
});
it('should report "failed" status if "failSpecWithNoExpectations" is enabled', function(done) {
reporter.jasmineDone.and.callFake(function(e) {
expect(e.overallStatus).toEqual('failed');
done();
});
env.configure({ failSpecWithNoExpectations: true });
env.execute();
});
it('should report "passed" status if "failSpecWithNoExpectations" is disabled', function(done) {
reporter.jasmineDone.and.callFake(function(e) {
expect(e.overallStatus).toEqual('passed');
done();
});
env.configure({ failSpecWithNoExpectations: false });
env.execute();
});
});
describe('When a top-level beforeAll fails', function() {
it('is "failed"', function(done) {
var env = new jasmineUnderTest.Env(),

View File

@@ -62,19 +62,19 @@ describe('HtmlReporter', function() {
});
describe('when a spec is done', function() {
it('logs errors to the console and prints a special symbol if it is an empty spec', function() {
if (typeof console === 'undefined') {
console = { warn: function() {} };
}
describe('and no expectations ran', function() {
var container, reporter;
beforeEach(function() {
if (typeof console === 'undefined') {
console = { warn: function() {}, error: function() {} };
}
var env = new jasmineUnderTest.Env(),
container = document.createElement('div'),
getContainer = function() {
return container;
},
container = document.createElement('div');
reporter = new jasmineUnderTest.HtmlReporter({
env: env,
getContainer: getContainer,
env: new jasmineUnderTest.Env(),
getContainer: function() {
return container;
},
createElement: function() {
return document.createElement.apply(document, arguments);
},
@@ -83,21 +83,39 @@ describe('HtmlReporter', function() {
}
});
spyOn(console, 'warn');
spyOn(console, 'warn');
spyOn(console, 'error');
reporter.initialize();
reporter.specDone({
status: 'passed',
fullName: 'Some Name',
passedExpectations: [],
failedExpectations: []
reporter.initialize();
});
it('should log warning to the console and print a special symbol when empty spec status is passed', function() {
reporter.specDone({
status: 'passed',
fullName: 'Some Name',
passedExpectations: [],
failedExpectations: []
});
expect(console.warn).toHaveBeenCalledWith(
"Spec 'Some Name' has no expectations."
);
var specEl = container.querySelector('.jasmine-symbol-summary li');
expect(specEl.getAttribute('class')).toEqual('jasmine-empty');
});
it('should log error to the console and print a failure symbol when empty spec status is failed', function() {
reporter.specDone({
status: 'failed',
fullName: 'Some Name',
passedExpectations: [],
failedExpectations: []
});
expect(console.error).toHaveBeenCalledWith(
"Spec 'Some Name' has no expectations."
);
var specEl = container.querySelector('.jasmine-symbol-summary li');
expect(specEl.getAttribute('class')).toEqual('jasmine-failed');
});
expect(console.warn).toHaveBeenCalledWith(
"Spec 'Some Name' has no expectations."
);
var specEl = container.querySelector('.jasmine-symbol-summary li');
expect(specEl.getAttribute('class')).toEqual('jasmine-empty');
});
it('reports the status symbol of a excluded spec', function() {

View File

@@ -65,6 +65,16 @@ getJasmineRequireObj().Env = function(j$) {
* @default false
*/
failFast: false,
/**
* Whether to fail the spec if it ran no expectations. By default
* a spec that ran no expectations is reported as passed. Setting this
* to true will report such spec as a failure.
* @name Configuration#failSpecWithNoExpectations
* @since 3.5.0
* @type Boolean
* @default false
*/
failSpecWithNoExpectations: false,
/**
* Whether to cause specs to only have one expectation failure.
* @name Configuration#oneFailurePerSpec
@@ -167,6 +177,11 @@ getJasmineRequireObj().Env = function(j$) {
config.failFast = configuration.failFast;
}
if (configuration.hasOwnProperty('failSpecWithNoExpectations')) {
config.failSpecWithNoExpectations =
configuration.failSpecWithNoExpectations;
}
if (configuration.hasOwnProperty('oneFailurePerSpec')) {
config.oneFailurePerSpec = configuration.oneFailurePerSpec;
}
@@ -641,6 +656,7 @@ getJasmineRequireObj().Env = function(j$) {
tree: topSuite,
runnableIds: runnablesToRun,
queueRunnerFactory: queueRunnerFactory,
failSpecWithNoExpectations: config.failSpecWithNoExpectations,
nodeStart: function(suite, next) {
currentlyExecutingSuites.push(suite);
defaultResourcesForRunnable(suite.id, suite.parentSuite.id);

View File

@@ -82,7 +82,7 @@ getJasmineRequireObj().Spec = function(j$) {
return this.asyncExpectationFactory(actual, this);
};
Spec.prototype.execute = function(onComplete, excluded) {
Spec.prototype.execute = function(onComplete, excluded, failSpecWithNoExp) {
var self = this;
var onStart = {
@@ -95,7 +95,7 @@ getJasmineRequireObj().Spec = function(j$) {
var complete = {
fn: function(done) {
self.queueableFn.fn = null;
self.result.status = self.status(excluded);
self.result.status = self.status(excluded, failSpecWithNoExp);
self.resultCallback(self.result, done);
}
};
@@ -166,7 +166,7 @@ getJasmineRequireObj().Spec = function(j$) {
return this.result;
};
Spec.prototype.status = function(excluded) {
Spec.prototype.status = function(excluded, failSpecWithNoExpectations) {
if (excluded === true) {
return 'excluded';
}
@@ -175,11 +175,17 @@ getJasmineRequireObj().Spec = function(j$) {
return 'pending';
}
if (this.result.failedExpectations.length > 0) {
if (
this.result.failedExpectations.length > 0 ||
(failSpecWithNoExpectations &&
this.result.failedExpectations.length +
this.result.passedExpectations.length ===
0)
) {
return 'failed';
} else {
return 'passed';
}
return 'passed';
};
Spec.prototype.getFullName = function() {

View File

@@ -5,6 +5,7 @@ getJasmineRequireObj().TreeProcessor = function() {
queueRunnerFactory = attrs.queueRunnerFactory,
nodeStart = attrs.nodeStart || function() {},
nodeComplete = attrs.nodeComplete || function() {},
failSpecWithNoExpectations = !!attrs.failSpecWithNoExpectations,
orderChildren =
attrs.orderChildren ||
function(node) {
@@ -222,7 +223,11 @@ getJasmineRequireObj().TreeProcessor = function() {
} else {
return {
fn: function(done) {
node.execute(done, stats[node.id].excluded);
node.execute(
done,
stats[node.id].excluded,
failSpecWithNoExpectations
);
}
};
}

View File

@@ -112,12 +112,13 @@ jasmineRequire.HtmlReporter = function(j$) {
this.specDone = function(result) {
stateBuilder.specDone(result);
if (
noExpectations(result) &&
typeof console !== 'undefined' &&
typeof console.error !== 'undefined'
) {
console.warn("Spec '" + result.fullName + "' has no expectations.");
if (noExpectations(result)) {
var noSpecMsg = "Spec '" + result.fullName + "' has no expectations.";
if (result.status === 'failed') {
console.error(noSpecMsg);
} else {
console.warn(noSpecMsg);
}
}
if (!symbols) {
@@ -140,7 +141,7 @@ jasmineRequire.HtmlReporter = function(j$) {
};
this.displaySpecInCorrectFormat = function(result) {
return noExpectations(result)
return noExpectations(result) && result.status === 'passed'
? 'jasmine-empty'
: this.resultStatus(result.status);
};
@@ -363,6 +364,16 @@ jasmineRequire.HtmlReporter = function(j$) {
);
}
if (result.failedExpectations.length === 0) {
messages.appendChild(
createDom(
'div',
{ className: 'jasmine-result-message' },
'Spec has no expectations'
)
);
}
return failure;
}
@@ -654,9 +665,12 @@ jasmineRequire.HtmlReporter = function(j$) {
}
function noExpectations(result) {
var allExpectations =
result.failedExpectations.length + result.passedExpectations.length;
return (
result.failedExpectations.length + result.passedExpectations.length ===
0 && result.status === 'passed'
allExpectations === 0 &&
(result.status === 'passed' || result.status === 'failed')
);
}