Allow matcher custom failure messages to be a function

By deferring the evaluation of these messages, we can avoid the
expensive creation of them when in the majority use case (tests are
    passing) they are not needed.

These failure messages were causing performance problems with larger
objects needed to be pretty printed as discussed in #520 and brought up
by @rdy.

[fixes #65925900][fixes #520]
This commit is contained in:
Sheel Choksi
2014-02-18 19:58:07 -08:00
parent 46d2c43da1
commit 76ca5ef6d4
11 changed files with 142 additions and 98 deletions

View File

@@ -43,7 +43,11 @@ getJasmineRequireObj().Expectation = function() {
args.unshift(name);
message = this.util.buildFailureMessage.apply(null, args);
} else {
message = result.message;
if (Object.prototype.toString.apply(result.message) === "[object Function]") {
message = result.message();
} else {
message = result.message;
}
}
}

View File

@@ -10,7 +10,7 @@ getJasmineRequireObj().toBeNaN = function(j$) {
if (result.pass) {
result.message = "Expected actual not to be NaN.";
} else {
result.message = "Expected " + j$.pp(actual) + " to be NaN.";
result.message = function() { return "Expected " + j$.pp(actual) + " to be NaN."; };
}
return result;

View File

@@ -13,15 +13,15 @@ getJasmineRequireObj().toHaveBeenCalledWith = function(j$) {
}
if (!actual.calls.any()) {
result.message = "Expected spy " + actual.and.identity() + " to have been called with " + j$.pp(expectedArgs) + " but it was never called.";
result.message = function() { return "Expected spy " + actual.and.identity() + " to have been called with " + j$.pp(expectedArgs) + " but it was never called."; };
return result;
}
if (util.contains(actual.calls.allArgs(), expectedArgs)) {
result.pass = true;
result.message = "Expected spy " + actual.and.identity() + " not to have been called with " + j$.pp(expectedArgs) + " but it was.";
result.message = function() { return "Expected spy " + actual.and.identity() + " not to have been called with " + j$.pp(expectedArgs) + " but it was."; };
} else {
result.message = "Expected spy " + actual.and.identity() + " to have been called with " + j$.pp(expectedArgs) + " but actual calls were " + j$.pp(actual.calls.allArgs()).replace(/^\[ | \]$/g, '') + ".";
result.message = function() { return "Expected spy " + actual.and.identity() + " to have been called with " + j$.pp(expectedArgs) + " but actual calls were " + j$.pp(actual.calls.allArgs()).replace(/^\[ | \]$/g, '') + "."; };
}
return result;

View File

@@ -25,16 +25,16 @@ getJasmineRequireObj().toThrow = function(j$) {
if (arguments.length == 1) {
result.pass = true;
result.message = "Expected function not to throw, but it threw " + j$.pp(thrown) + ".";
result.message = function() { return "Expected function not to throw, but it threw " + j$.pp(thrown) + "."; };
return result;
}
if (util.equals(thrown, expected)) {
result.pass = true;
result.message = "Expected function not to throw " + j$.pp(expected) + ".";
result.message = function() { return "Expected function not to throw " + j$.pp(expected) + "."; };
} else {
result.message = "Expected function to throw " + j$.pp(expected) + ", but it threw " + j$.pp(thrown) + ".";
result.message = function() { return "Expected function to throw " + j$.pp(expected) + ", but it threw " + j$.pp(thrown) + "."; };
}
return result;

View File

@@ -3,6 +3,8 @@ getJasmineRequireObj().toThrowError = function(j$) {
return {
compare: function(actual) {
var threw = false,
pass = {pass: true},
fail = {pass: false},
thrown,
errorType,
message,
@@ -24,15 +26,18 @@ getJasmineRequireObj().toThrowError = function(j$) {
}
if (!threw) {
return fail("Expected function to throw an Error.");
fail.message = "Expected function to throw an Error.";
return fail;
}
if (!(thrown instanceof Error)) {
return fail("Expected function to throw an Error, but it threw " + thrown + ".");
fail.message = function() { return "Expected function to throw an Error, but it threw " + j$.pp(thrown) + "."; };
return fail;
}
if (arguments.length == 1) {
return pass("Expected function not to throw an Error, but it threw " + fnNameFor(thrown) + ".");
pass.message = "Expected function not to throw an Error, but it threw " + fnNameFor(thrown) + ".";
return pass;
}
if (errorType) {
@@ -42,45 +47,55 @@ getJasmineRequireObj().toThrowError = function(j$) {
if (errorType && message) {
if (thrown.constructor == errorType && util.equals(thrown.message, message)) {
return pass("Expected function not to throw " + name + " with message \"" + message + "\".");
pass.message = function() { return "Expected function not to throw " + name + " with message " + j$.pp(message) + "."; };
return pass;
} else {
return fail("Expected function to throw " + name + " with message \"" + message +
"\", but it threw " + constructorName + " with message \"" + thrown.message + "\".");
fail.message = function() { return "Expected function to throw " + name + " with message " + j$.pp(message) +
", but it threw " + constructorName + " with message " + j$.pp(thrown.message) + "."; };
return fail;
}
}
if (errorType && regexp) {
if (thrown.constructor == errorType && regexp.test(thrown.message)) {
return pass("Expected function not to throw " + name + " with message matching " + regexp + ".");
pass.message = function() { return "Expected function not to throw " + name + " with message matching " + j$.pp(regexp) + "."; };
return pass;
} else {
return fail("Expected function to throw " + name + " with message matching " + regexp +
", but it threw " + constructorName + " with message \"" + thrown.message + "\".");
fail.message = function() { return "Expected function to throw " + name + " with message matching " + j$.pp(regexp) +
", but it threw " + constructorName + " with message " + j$.pp(thrown.message) + "."; };
return fail;
}
}
if (errorType) {
if (thrown.constructor == errorType) {
return pass("Expected function not to throw " + name + ".");
pass.message = "Expected function not to throw " + name + ".";
return pass;
} else {
return fail("Expected function to throw " + name + ", but it threw " + constructorName + ".");
fail.message = "Expected function to throw " + name + ", but it threw " + constructorName + ".";
return fail;
}
}
if (message) {
if (thrown.message == message) {
return pass("Expected function not to throw an exception with message " + j$.pp(message) + ".");
pass.message = function() { return "Expected function not to throw an exception with message " + j$.pp(message) + "."; };
return pass;
} else {
return fail("Expected function to throw an exception with message " + j$.pp(message) +
", but it threw an exception with message " + j$.pp(thrown.message) + ".");
fail.message = function() { return "Expected function to throw an exception with message " + j$.pp(message) +
", but it threw an exception with message " + j$.pp(thrown.message) + "."; };
return fail;
}
}
if (regexp) {
if (regexp.test(thrown.message)) {
return pass("Expected function not to throw an exception with a message matching " + j$.pp(regexp) + ".");
pass.message = function() { return "Expected function not to throw an exception with a message matching " + j$.pp(regexp) + "."; };
return pass;
} else {
return fail("Expected function to throw an exception with a message matching " + j$.pp(regexp) +
", but it threw an exception with message " + j$.pp(thrown.message) + ".");
fail.message = function() { return "Expected function to throw an exception with a message matching " + j$.pp(regexp) +
", but it threw an exception with message " + j$.pp(thrown.message) + "."; };
return fail;
}
}
@@ -88,20 +103,6 @@ getJasmineRequireObj().toThrowError = function(j$) {
return func.name || func.toString().match(/^\s*function\s*(\w*)\s*\(/)[1];
}
function pass(notMessage) {
return {
pass: true,
message: notMessage
};
}
function fail(message) {
return {
pass: false,
message: message
};
}
function extractExpectedParams() {
if (arguments.length == 1) {
return;