Merge remote-tracking branch 'upstream/main' into array_buffer

# Conflicts:
#	spec/core/matchers/matchersUtilSpec.js
This commit is contained in:
Surgie Finesse
2021-03-22 18:31:35 +10:00
246 changed files with 21415 additions and 8225 deletions

View File

@@ -1,19 +1,69 @@
getJasmineRequireObj().DiffBuilder = function(j$) {
return function DiffBuilder() {
var path = new j$.ObjectPath(),
mismatches = [];
return function DiffBuilder(config) {
var prettyPrinter = (config || {}).prettyPrinter || j$.makePrettyPrinter(),
mismatches = new j$.MismatchTree(),
path = new j$.ObjectPath(),
actualRoot = undefined,
expectedRoot = undefined;
return {
record: function (actual, expected, formatter) {
formatter = formatter || defaultFormatter;
mismatches.push(formatter(actual, expected, path));
setRoots: function(actual, expected) {
actualRoot = actual;
expectedRoot = expected;
},
getMessage: function () {
return mismatches.join('\n');
recordMismatch: function(formatter) {
mismatches.add(path, formatter);
},
withPath: function (pathComponent, block) {
getMessage: function() {
var messages = [];
mismatches.traverse(function(path, isLeaf, formatter) {
var actualCustom,
expectedCustom,
useCustom,
derefResult = dereferencePath(
path,
actualRoot,
expectedRoot,
prettyPrinter
),
actual = derefResult.actual,
expected = derefResult.expected;
if (formatter) {
messages.push(formatter(actual, expected, path, prettyPrinter));
return true;
}
actualCustom = prettyPrinter.customFormat_(actual);
expectedCustom = prettyPrinter.customFormat_(expected);
useCustom = !(
j$.util.isUndefined(actualCustom) &&
j$.util.isUndefined(expectedCustom)
);
if (useCustom) {
messages.push(
wrapPrettyPrinted(actualCustom, expectedCustom, path)
);
return false; // don't recurse further
}
if (isLeaf) {
messages.push(
defaultFormatter(actual, expected, path, prettyPrinter)
);
}
return true;
});
return messages.join('\n');
},
withPath: function(pathComponent, block) {
var oldPath = path;
path = path.add(pathComponent);
block();
@@ -21,13 +71,48 @@ getJasmineRequireObj().DiffBuilder = function(j$) {
}
};
function defaultFormatter (actual, expected, path) {
return 'Expected ' +
path + (path.depth() ? ' = ' : '') +
j$.pp(actual) +
function defaultFormatter(actual, expected, path, prettyPrinter) {
return wrapPrettyPrinted(
prettyPrinter(actual),
prettyPrinter(expected),
path
);
}
function wrapPrettyPrinted(actual, expected, path) {
return (
'Expected ' +
path +
(path.depth() ? ' = ' : '') +
actual +
' to equal ' +
j$.pp(expected) +
'.';
expected +
'.'
);
}
};
function dereferencePath(objectPath, actual, expected, pp) {
function handleAsymmetricExpected() {
if (
j$.isAsymmetricEqualityTester_(expected) &&
j$.isFunction_(expected.valuesForDiff_)
) {
var asymmetricResult = expected.valuesForDiff_(actual, pp);
expected = asymmetricResult.self;
actual = asymmetricResult.other;
}
}
var i;
handleAsymmetricExpected();
for (i = 0; i < objectPath.components.length; i++) {
actual = actual[objectPath.components[i]];
expected = expected[objectPath.components[i]];
handleAsymmetricExpected();
}
return { actual: actual, expected: expected };
}
};

View File

@@ -0,0 +1,61 @@
getJasmineRequireObj().MismatchTree = function(j$) {
/*
To be able to apply custom object formatters at all possible levels of an
object graph, DiffBuilder needs to be able to know not just where the
mismatch occurred but also all ancestors of the mismatched value in both
the expected and actual object graphs. MismatchTree maintains that context
and provides it via the traverse method.
*/
function MismatchTree(path) {
this.path = path || new j$.ObjectPath([]);
this.formatter = undefined;
this.children = [];
this.isMismatch = false;
}
MismatchTree.prototype.add = function(path, formatter) {
var key, child;
if (path.depth() === 0) {
this.formatter = formatter;
this.isMismatch = true;
} else {
key = path.components[0];
path = path.shift();
child = this.child(key);
if (!child) {
child = new MismatchTree(this.path.add(key));
this.children.push(child);
}
child.add(path, formatter);
}
};
MismatchTree.prototype.traverse = function(visit) {
var i,
hasChildren = this.children.length > 0;
if (this.isMismatch || hasChildren) {
if (visit(this.path, !hasChildren, this.formatter)) {
for (i = 0; i < this.children.length; i++) {
this.children[i].traverse(visit);
}
}
}
};
MismatchTree.prototype.child = function(key) {
var i, pathEls;
for (i = 0; i < this.children.length; i++) {
pathEls = this.children[i].path.components;
if (pathEls[pathEls.length - 1] === key) {
return this.children[i];
}
}
};
return MismatchTree;
};

View File

@@ -4,7 +4,8 @@ getJasmineRequireObj().NullDiffBuilder = function(j$) {
withPath: function(_, block) {
block();
},
record: function() {}
setRoots: function() {},
recordMismatch: function() {}
};
};
};

View File

@@ -15,6 +15,10 @@ getJasmineRequireObj().ObjectPath = function(j$) {
return new ObjectPath(this.components.concat([component]));
};
ObjectPath.prototype.shift = function() {
return new ObjectPath(this.components.slice(1));
};
ObjectPath.prototype.depth = function() {
return this.components.length;
};
@@ -28,7 +32,7 @@ getJasmineRequireObj().ObjectPath = function(j$) {
return '.' + prop;
}
return '[\'' + prop + '\']';
return "['" + prop + "']";
}
function map(array, fn) {

View File

@@ -0,0 +1,30 @@
/* eslint-disable compat/compat */
getJasmineRequireObj().toBePending = function(j$) {
/**
* Expect a promise to be pending, i.e. the promise is neither resolved nor rejected.
* @function
* @async
* @name async-matchers#toBePending
* @since 3.6
* @example
* await expectAsync(aPromise).toBePending();
*/
return function toBePending() {
return {
compare: function(actual) {
if (!j$.isPromiseLike(actual)) {
throw new Error('Expected toBePending to be called on a promise.');
}
var want = {};
return Promise.race([actual, Promise.resolve(want)]).then(
function(got) {
return { pass: want === got };
},
function() {
return { pass: false };
}
);
}
};
};
};

View File

@@ -4,17 +4,25 @@ getJasmineRequireObj().toBeRejected = function(j$) {
* @function
* @async
* @name async-matchers#toBeRejected
* @since 3.1.0
* @example
* await expectAsync(aPromise).toBeRejected();
* @example
* return expectAsync(aPromise).toBeRejected();
*/
return function toBeResolved(util) {
return function toBeRejected() {
return {
compare: function(actual) {
if (!j$.isPromiseLike(actual)) {
throw new Error('Expected toBeRejected to be called on a promise.');
}
return actual.then(
function() { return {pass: false}; },
function() { return {pass: true}; }
function() {
return { pass: false };
},
function() {
return { pass: true };
}
);
}
};

View File

@@ -4,41 +4,55 @@ getJasmineRequireObj().toBeRejectedWith = function(j$) {
* @function
* @async
* @name async-matchers#toBeRejectedWith
* @since 3.3.0
* @param {Object} expected - Value that the promise is expected to be rejected with
* @example
* await expectAsync(aPromise).toBeRejectedWith({prop: 'value'});
* @example
* return expectAsync(aPromise).toBeRejectedWith({prop: 'value'});
*/
return function toBeRejectedWith(util, customEqualityTesters) {
return function toBeRejectedWith(matchersUtil) {
return {
compare: function(actualPromise, expectedValue) {
if (!j$.isPromiseLike(actualPromise)) {
throw new Error(
'Expected toBeRejectedWith to be called on a promise.'
);
}
function prefix(passed) {
return 'Expected a promise ' +
return (
'Expected a promise ' +
(passed ? 'not ' : '') +
'to be rejected with ' + j$.pp(expectedValue);
'to be rejected with ' +
matchersUtil.pp(expectedValue)
);
}
return actualPromise.then(
function() {
return {
pass: false,
message: prefix(false) + ' but it was resolved.'
};
},
function(actualValue) {
if (util.equals(actualValue, expectedValue, customEqualityTesters)) {
return {
pass: true,
message: prefix(true) + '.'
};
} else {
return {
pass: false,
message: prefix(false) + ' but it was rejected with ' + j$.pp(actualValue) + '.'
message: prefix(false) + ' but it was resolved.'
};
},
function(actualValue) {
if (matchersUtil.equals(actualValue, expectedValue)) {
return {
pass: true,
message: prefix(true) + '.'
};
} else {
return {
pass: false,
message:
prefix(false) +
' but it was rejected with ' +
matchersUtil.pp(actualValue) +
'.'
};
}
}
}
);
}
};

View File

@@ -0,0 +1,122 @@
getJasmineRequireObj().toBeRejectedWithError = function(j$) {
/**
* Expect a promise to be rejected with a value matched to the expected
* @function
* @async
* @name async-matchers#toBeRejectedWithError
* @since 3.5.0
* @param {Error} [expected] - `Error` constructor the object that was thrown needs to be an instance of. If not provided, `Error` will be used.
* @param {RegExp|String} [message] - The message that should be set on the thrown `Error`
* @example
* await expectAsync(aPromise).toBeRejectedWithError(MyCustomError, 'Error message');
* await expectAsync(aPromise).toBeRejectedWithError(MyCustomError, /Error message/);
* await expectAsync(aPromise).toBeRejectedWithError(MyCustomError);
* await expectAsync(aPromise).toBeRejectedWithError('Error message');
* return expectAsync(aPromise).toBeRejectedWithError(/Error message/);
*/
return function toBeRejectedWithError(matchersUtil) {
return {
compare: function(actualPromise, arg1, arg2) {
if (!j$.isPromiseLike(actualPromise)) {
throw new Error(
'Expected toBeRejectedWithError to be called on a promise.'
);
}
var expected = getExpectedFromArgs(arg1, arg2, matchersUtil);
return actualPromise.then(
function() {
return {
pass: false,
message: 'Expected a promise to be rejected but it was resolved.'
};
},
function(actualValue) {
return matchError(actualValue, expected, matchersUtil);
}
);
}
};
};
function matchError(actual, expected, matchersUtil) {
if (!j$.isError_(actual)) {
return fail(expected, 'rejected with ' + matchersUtil.pp(actual));
}
if (!(actual instanceof expected.error)) {
return fail(
expected,
'rejected with type ' + j$.fnNameFor(actual.constructor)
);
}
var actualMessage = actual.message;
if (
actualMessage === expected.message ||
typeof expected.message === 'undefined'
) {
return pass(expected);
}
if (
expected.message instanceof RegExp &&
expected.message.test(actualMessage)
) {
return pass(expected);
}
return fail(expected, 'rejected with ' + matchersUtil.pp(actual));
}
function pass(expected) {
return {
pass: true,
message:
'Expected a promise not to be rejected with ' +
expected.printValue +
', but it was.'
};
}
function fail(expected, message) {
return {
pass: false,
message:
'Expected a promise to be rejected with ' +
expected.printValue +
' but it was ' +
message +
'.'
};
}
function getExpectedFromArgs(arg1, arg2, matchersUtil) {
var error, message;
if (isErrorConstructor(arg1)) {
error = arg1;
message = arg2;
} else {
error = Error;
message = arg1;
}
return {
error: error,
message: message,
printValue:
j$.fnNameFor(error) +
(typeof message === 'undefined' ? '' : ': ' + matchersUtil.pp(message))
};
}
function isErrorConstructor(value) {
return (
typeof value === 'function' &&
(value === Error || j$.isError_(value.prototype))
);
}
};

View File

@@ -4,17 +4,26 @@ getJasmineRequireObj().toBeResolved = function(j$) {
* @function
* @async
* @name async-matchers#toBeResolved
* @since 3.1.0
* @example
* await expectAsync(aPromise).toBeResolved();
* @example
* return expectAsync(aPromise).toBeResolved();
*/
return function toBeResolved(util) {
return function toBeResolved() {
return {
compare: function(actual) {
if (!j$.isPromiseLike(actual)) {
throw new Error('Expected toBeResolved to be called on a promise.');
}
return actual.then(
function() { return {pass: true}; },
function() { return {pass: false}; }
function() {
return { pass: true };
},
function() {
return { pass: false };
}
);
}
};

View File

@@ -4,41 +4,53 @@ getJasmineRequireObj().toBeResolvedTo = function(j$) {
* @function
* @async
* @name async-matchers#toBeResolvedTo
* @since 3.1.0
* @param {Object} expected - Value that the promise is expected to resolve to
* @example
* await expectAsync(aPromise).toBeResolvedTo({prop: 'value'});
* @example
* return expectAsync(aPromise).toBeResolvedTo({prop: 'value'});
*/
return function toBeResolvedTo(util, customEqualityTesters) {
return function toBeResolvedTo(matchersUtil) {
return {
compare: function(actualPromise, expectedValue) {
if (!j$.isPromiseLike(actualPromise)) {
throw new Error('Expected toBeResolvedTo to be called on a promise.');
}
function prefix(passed) {
return 'Expected a promise ' +
return (
'Expected a promise ' +
(passed ? 'not ' : '') +
'to be resolved to ' + j$.pp(expectedValue);
'to be resolved to ' +
matchersUtil.pp(expectedValue)
);
}
return actualPromise.then(
function(actualValue) {
if (util.equals(actualValue, expectedValue, customEqualityTesters)) {
return {
pass: true,
message: prefix(true) + '.'
};
} else {
if (matchersUtil.equals(actualValue, expectedValue)) {
return {
pass: true,
message: prefix(true) + '.'
};
} else {
return {
pass: false,
message:
prefix(false) +
' but it was resolved to ' +
matchersUtil.pp(actualValue) +
'.'
};
}
},
function() {
return {
pass: false,
message: prefix(false) + ' but it was resolved to ' + j$.pp(actualValue) + '.'
message: prefix(false) + ' but it was rejected.'
};
}
},
function() {
return {
pass: false,
message: prefix(false) + ' but it was rejected.'
};
}
);
}
};

View File

@@ -1,99 +1,201 @@
getJasmineRequireObj().matchersUtil = function(j$) {
// TODO: what to do about jasmine.pp not being inject? move to JSON.stringify? gut PrettyPrinter?
getJasmineRequireObj().MatchersUtil = function(j$) {
// TODO: convert all uses of j$.pp to use the injected pp
return {
equals: equals,
/**
* _Note:_ Do not construct this directly. Jasmine will construct one and
* pass it to matchers and asymmetric equality testers.
* @name MatchersUtil
* @classdesc Utilities for use in implementing matchers
* @constructor
*/
function MatchersUtil(options) {
options = options || {};
this.customTesters_ = options.customTesters || [];
/**
* Formats a value for use in matcher failure messages and similar contexts,
* taking into account the current set of custom value formatters.
* @function
* @name MatchersUtil#pp
* @since 3.6.0
* @param {*} value The value to pretty-print
* @return {string} The pretty-printed value
*/
this.pp = options.pp || function() {};
}
contains: function(haystack, needle, customTesters) {
customTesters = customTesters || [];
/**
* Determines whether `haystack` contains `needle`, using the same comparison
* logic as {@link MatchersUtil#equals}.
* @function
* @name MatchersUtil#contains
* @since 2.0.0
* @param {*} haystack The collection to search
* @param {*} needle The value to search for
* @param [customTesters] An array of custom equality testers
* @returns {boolean} True if `needle` was found in `haystack`
*/
MatchersUtil.prototype.contains = function(haystack, needle, customTesters) {
if (j$.isSet(haystack)) {
return haystack.has(needle);
}
if ((Object.prototype.toString.apply(haystack) === '[object Set]')) {
return haystack.has(needle);
}
if ((Object.prototype.toString.apply(haystack) === '[object Array]') ||
(!!haystack && !haystack.indexOf))
{
for (var i = 0; i < haystack.length; i++) {
if (equals(haystack[i], needle, customTesters)) {
return true;
}
}
return false;
}
return !!haystack && haystack.indexOf(needle) >= 0;
},
buildFailureMessage: function() {
var args = Array.prototype.slice.call(arguments, 0),
matcherName = args[0],
isNot = args[1],
actual = args[2],
expected = args.slice(3),
englishyPredicate = matcherName.replace(/[A-Z]/g, function(s) { return ' ' + s.toLowerCase(); });
var message = 'Expected ' +
j$.pp(actual) +
(isNot ? ' not ' : ' ') +
englishyPredicate;
if (expected.length > 0) {
for (var i = 0; i < expected.length; i++) {
if (i > 0) {
message += ',';
}
message += ' ' + j$.pp(expected[i]);
if (
Object.prototype.toString.apply(haystack) === '[object Array]' ||
(!!haystack && !haystack.indexOf)
) {
for (var i = 0; i < haystack.length; i++) {
if (this.equals(haystack[i], needle, customTesters)) {
return true;
}
}
return false;
}
return message + '.';
return !!haystack && haystack.indexOf(needle) >= 0;
};
MatchersUtil.prototype.buildFailureMessage = function() {
var self = this;
var args = Array.prototype.slice.call(arguments, 0),
matcherName = args[0],
isNot = args[1],
actual = args[2],
expected = args.slice(3),
englishyPredicate = matcherName.replace(/[A-Z]/g, function(s) {
return ' ' + s.toLowerCase();
});
var message =
'Expected ' +
self.pp(actual) +
(isNot ? ' not ' : ' ') +
englishyPredicate;
if (expected.length > 0) {
for (var i = 0; i < expected.length; i++) {
if (i > 0) {
message += ',';
}
message += ' ' + self.pp(expected[i]);
}
}
return message + '.';
};
MatchersUtil.prototype.asymmetricDiff_ = function(
a,
b,
aStack,
bStack,
customTesters,
diffBuilder
) {
if (j$.isFunction_(b.valuesForDiff_)) {
var values = b.valuesForDiff_(a, this.pp);
this.eq_(
values.other,
values.self,
aStack,
bStack,
customTesters,
diffBuilder
);
} else {
diffBuilder.recordMismatch();
}
};
function isAsymmetric(obj) {
return obj && j$.isA_('Function', obj.asymmetricMatch);
}
MatchersUtil.prototype.asymmetricMatch_ = function(
a,
b,
aStack,
bStack,
customTesters,
diffBuilder
) {
var asymmetricA = j$.isAsymmetricEqualityTester_(a),
asymmetricB = j$.isAsymmetricEqualityTester_(b),
shim,
result;
function asymmetricMatch(a, b, customTesters, diffBuilder) {
var asymmetricA = isAsymmetric(a),
asymmetricB = isAsymmetric(b),
result;
if (asymmetricA && asymmetricB) {
if (asymmetricA === asymmetricB) {
return undefined;
}
shim = j$.asymmetricEqualityTesterArgCompatShim(this, customTesters);
if (asymmetricA) {
result = a.asymmetricMatch(b, customTesters);
result = a.asymmetricMatch(b, shim);
if (!result) {
diffBuilder.record(a, b);
diffBuilder.recordMismatch();
}
return result;
}
if (asymmetricB) {
result = b.asymmetricMatch(a, customTesters);
result = b.asymmetricMatch(a, shim);
if (!result) {
diffBuilder.record(a, b);
this.asymmetricDiff_(a, b, aStack, bStack, customTesters, diffBuilder);
}
return result;
}
}
};
function equals(a, b, customTesters, diffBuilder) {
customTesters = customTesters || [];
/**
* Determines whether two values are deeply equal to each other.
* @function
* @name MatchersUtil#equals
* @since 2.0.0
* @param {*} a The first value to compare
* @param {*} b The second value to compare
* @param [customTesters] An array of custom equality testers
* @returns {boolean} True if the values are equal
*/
MatchersUtil.prototype.equals = function(
a,
b,
customTestersOrDiffBuilder,
diffBuilderOrNothing
) {
var customTesters, diffBuilder;
if (isDiffBuilder(customTestersOrDiffBuilder)) {
diffBuilder = customTestersOrDiffBuilder;
} else {
customTesters = customTestersOrDiffBuilder;
diffBuilder = diffBuilderOrNothing;
}
customTesters = customTesters || this.customTesters_;
diffBuilder = diffBuilder || j$.NullDiffBuilder();
diffBuilder.setRoots(a, b);
return eq(a, b, [], [], customTesters, diffBuilder);
}
return this.eq_(a, b, [], [], customTesters, diffBuilder);
};
// Equality function lovingly adapted from isEqual in
// [Underscore](http://underscorejs.org)
function eq(a, b, aStack, bStack, customTesters, diffBuilder) {
var result = true, i;
MatchersUtil.prototype.eq_ = function(
a,
b,
aStack,
bStack,
customTesters,
diffBuilder
) {
var result = true,
self = this,
i;
var asymmetricResult = asymmetricMatch(a, b, customTesters, diffBuilder);
var asymmetricResult = this.asymmetricMatch_(
a,
b,
aStack,
bStack,
customTesters,
diffBuilder
);
if (!j$.util.isUndefined(asymmetricResult)) {
return asymmetricResult;
}
@@ -102,7 +204,7 @@ getJasmineRequireObj().matchersUtil = function(j$) {
var customTesterResult = customTesters[i](a, b);
if (!j$.util.isUndefined(customTesterResult)) {
if (!customTesterResult) {
diffBuilder.record(a, b);
diffBuilder.recordMismatch();
}
return customTesterResult;
}
@@ -111,18 +213,17 @@ getJasmineRequireObj().matchersUtil = function(j$) {
if (a instanceof Error && b instanceof Error) {
result = a.message == b.message;
if (!result) {
diffBuilder.record(a, b);
diffBuilder.recordMismatch();
}
return result;
}
// Identical objects are equal. `0 === -0`, but they aren't identical.
// See the [Harmony `egal` proposal](http://wiki.ecmascript.org/doku.php?id=harmony:egal).
if (a === b) {
result = a !== 0 || 1 / a == 1 / b;
if (!result) {
diffBuilder.record(a, b);
diffBuilder.recordMismatch();
}
return result;
}
@@ -130,13 +231,13 @@ getJasmineRequireObj().matchersUtil = function(j$) {
if (a === null || b === null) {
result = a === b;
if (!result) {
diffBuilder.record(a, b);
diffBuilder.recordMismatch();
}
return result;
}
var className = Object.prototype.toString.call(a);
if (className != Object.prototype.toString.call(b)) {
diffBuilder.record(a, b);
diffBuilder.recordMismatch();
return false;
}
switch (className) {
@@ -146,15 +247,16 @@ getJasmineRequireObj().matchersUtil = function(j$) {
// equivalent to `new String("5")`.
result = a == String(b);
if (!result) {
diffBuilder.record(a, b);
diffBuilder.recordMismatch();
}
return result;
case '[object Number]':
// `NaN`s are equivalent, but non-reflexive. An `egal` comparison is performed for
// other numeric values.
result = a != +a ? b != +b : (a === 0 ? 1 / a == 1 / b : a == +b);
result =
a != +a ? b != +b : a === 0 && b === 0 ? 1 / a == 1 / b : a == +b;
if (!result) {
diffBuilder.record(a, b);
diffBuilder.recordMismatch();
}
return result;
case '[object Date]':
@@ -164,7 +266,7 @@ getJasmineRequireObj().matchersUtil = function(j$) {
// of `NaN` are not equivalent.
result = +a == +b;
if (!result) {
diffBuilder.record(a, b);
diffBuilder.recordMismatch();
}
return result;
// RegExps are compared by their source patterns and flags.
@@ -199,13 +301,15 @@ getJasmineRequireObj().matchersUtil = function(j$) {
}
return result;
case '[object RegExp]':
return a.source == b.source &&
return (
a.source == b.source &&
a.global == b.global &&
a.multiline == b.multiline &&
a.ignoreCase == b.ignoreCase;
a.ignoreCase == b.ignoreCase
);
}
if (typeof a != 'object' || typeof b != 'object') {
diffBuilder.record(a, b);
diffBuilder.recordMismatch();
return false;
}
@@ -215,12 +319,12 @@ getJasmineRequireObj().matchersUtil = function(j$) {
// At first try to use DOM3 method isEqualNode
result = a.isEqualNode(b);
if (!result) {
diffBuilder.record(a, b);
diffBuilder.recordMismatch();
}
return result;
}
if (aIsDomNode || bIsDomNode) {
diffBuilder.record(a, b);
diffBuilder.recordMismatch();
return false;
}
@@ -236,7 +340,9 @@ getJasmineRequireObj().matchersUtil = function(j$) {
while (length--) {
// Linear search. Performance is inversely proportional to the number of
// unique nested structures.
if (aStack[length] == a) { return bStack[length] == b; }
if (aStack[length] == a) {
return bStack[length] == b;
}
}
// Add the first object to the stack of traversed objects.
aStack.push(a);
@@ -250,19 +356,28 @@ getJasmineRequireObj().matchersUtil = function(j$) {
diffBuilder.withPath('length', function() {
if (aLength !== bLength) {
diffBuilder.record(aLength, bLength);
diffBuilder.recordMismatch();
result = false;
}
});
for (i = 0; i < aLength || i < bLength; i++) {
var formatter = false;
diffBuilder.withPath(i, function() {
if (i >= bLength) {
diffBuilder.record(a[i], void 0, actualArrayIsLongerFormatter);
diffBuilder.recordMismatch(
actualArrayIsLongerFormatter.bind(null, self.pp)
);
result = false;
} else {
result = eq(i < aLength ? a[i] : void 0, i < bLength ? b[i] : void 0, aStack, bStack, customTesters, diffBuilder) && result;
result =
self.eq_(
i < aLength ? a[i] : void 0,
i < bLength ? b[i] : void 0,
aStack,
bStack,
customTesters,
diffBuilder
) && result;
}
});
}
@@ -271,17 +386,17 @@ getJasmineRequireObj().matchersUtil = function(j$) {
}
} else if (j$.isMap(a) && j$.isMap(b)) {
if (a.size != b.size) {
diffBuilder.record(a, b);
diffBuilder.recordMismatch();
return false;
}
var keysA = [];
var keysB = [];
a.forEach( function( valueA, keyA ) {
keysA.push( keyA );
a.forEach(function(valueA, keyA) {
keysA.push(keyA);
});
b.forEach( function( valueB, keyB ) {
keysB.push( keyB );
b.forEach(function(valueB, keyB) {
keysB.push(keyB);
});
// For both sets of keys, check they map to equal values in both maps.
@@ -302,33 +417,50 @@ getJasmineRequireObj().matchersUtil = function(j$) {
// Only use the cmpKey when one of the keys is asymmetric and the corresponding key matches,
// otherwise explicitly look up the mapKey in the other Map since we want keys with unique
// obj identity (that are otherwise equal) to not match.
if (isAsymmetric(mapKey) || isAsymmetric(cmpKey) &&
eq(mapKey, cmpKey, aStack, bStack, customTesters, j$.NullDiffBuilder())) {
if (
j$.isAsymmetricEqualityTester_(mapKey) ||
(j$.isAsymmetricEqualityTester_(cmpKey) &&
this.eq_(
mapKey,
cmpKey,
aStack,
bStack,
customTesters,
j$.NullDiffBuilder()
))
) {
mapValueB = b.get(cmpKey);
} else {
mapValueB = b.get(mapKey);
}
result = eq(mapValueA, mapValueB, aStack, bStack, customTesters, j$.NullDiffBuilder());
result = this.eq_(
mapValueA,
mapValueB,
aStack,
bStack,
customTesters,
j$.NullDiffBuilder()
);
}
}
if (!result) {
diffBuilder.record(a, b);
diffBuilder.recordMismatch();
return false;
}
} else if (j$.isSet(a) && j$.isSet(b)) {
if (a.size != b.size) {
diffBuilder.record(a, b);
diffBuilder.recordMismatch();
return false;
}
var valuesA = [];
a.forEach( function( valueA ) {
valuesA.push( valueA );
a.forEach(function(valueA) {
valuesA.push(valueA);
});
var valuesB = [];
b.forEach( function( valueB ) {
valuesB.push( valueB );
b.forEach(function(valueB) {
valuesB.push(valueB);
});
// For both sets, check they are all contained in the other set
@@ -352,7 +484,14 @@ getJasmineRequireObj().matchersUtil = function(j$) {
otherValue = otherValues[l];
prevStackSize = baseStack.length;
// compare by value equality
found = eq(baseValue, otherValue, baseStack, otherStack, customTesters, j$.NullDiffBuilder());
found = this.eq_(
baseValue,
otherValue,
baseStack,
otherStack,
customTesters,
j$.NullDiffBuilder()
);
if (!found && prevStackSize !== baseStack.length) {
baseStack.splice(prevStackSize);
otherStack.splice(prevStackSize);
@@ -363,31 +502,43 @@ getJasmineRequireObj().matchersUtil = function(j$) {
}
if (!result) {
diffBuilder.record(a, b);
diffBuilder.recordMismatch();
return false;
}
} else if (j$.isURL(a) && j$.isURL(b)) {
// URLs have no enumrable properties, so the default object comparison
// would consider any two URLs to be equal.
return a.toString() === b.toString();
} else {
// Objects with different constructors are not equivalent, but `Object`s
// or `Array`s from different frames are.
var aCtor = a.constructor, bCtor = b.constructor;
if (aCtor !== bCtor &&
isFunction(aCtor) && isFunction(bCtor) &&
a instanceof aCtor && b instanceof bCtor &&
!(aCtor instanceof aCtor && bCtor instanceof bCtor)) {
diffBuilder.record(a, b, constructorsAreDifferentFormatter);
var aCtor = a.constructor,
bCtor = b.constructor;
if (
aCtor !== bCtor &&
isFunction(aCtor) &&
isFunction(bCtor) &&
a instanceof aCtor &&
b instanceof bCtor &&
!(aCtor instanceof aCtor && bCtor instanceof bCtor)
) {
diffBuilder.recordMismatch(
constructorsAreDifferentFormatter.bind(null, this.pp)
);
return false;
}
}
// Deep compare objects.
var aKeys = keys(a, className == '[object Array]'), key;
var aKeys = keys(a, className == '[object Array]'),
key;
size = aKeys.length;
// Ensure that both objects contain the same number of properties before comparing deep equality.
if (keys(b, className == '[object Array]').length !== size) {
diffBuilder.record(a, b, objectKeysAreDifferentFormatter);
diffBuilder.recordMismatch(
objectKeysAreDifferentFormatter.bind(null, this.pp)
);
return false;
}
@@ -395,13 +546,17 @@ getJasmineRequireObj().matchersUtil = function(j$) {
key = aKeys[i];
// Deep compare each member
if (!j$.util.has(b, key)) {
diffBuilder.record(a, b, objectKeysAreDifferentFormatter);
diffBuilder.recordMismatch(
objectKeysAreDifferentFormatter.bind(null, this.pp)
);
result = false;
continue;
}
diffBuilder.withPath(key, function() {
if(!eq(a[key], b[key], aStack, bStack, customTesters, diffBuilder)) {
if (
!self.eq_(a[key], b[key], aStack, bStack, customTesters, diffBuilder)
) {
result = false;
}
});
@@ -416,26 +571,27 @@ getJasmineRequireObj().matchersUtil = function(j$) {
bStack.pop();
return result;
}
};
function keys(obj, isArray) {
var allKeys = Object.keys ? Object.keys(obj) :
(function(o) {
var allKeys = Object.keys
? Object.keys(obj)
: (function(o) {
var keys = [];
for (var key in o) {
if (j$.util.has(o, key)) {
keys.push(key);
}
if (j$.util.has(o, key)) {
keys.push(key);
}
}
return keys;
})(obj);
})(obj);
if (!isArray) {
return allKeys;
}
if (allKeys.length === 0) {
return allKeys;
return allKeys;
}
var extraKeys = [];
@@ -448,59 +604,73 @@ getJasmineRequireObj().matchersUtil = function(j$) {
return extraKeys;
}
function has(obj, key) {
return Object.prototype.hasOwnProperty.call(obj, key);
}
function isFunction(obj) {
return typeof obj === 'function';
}
function objectKeysAreDifferentFormatter(actual, expected, path) {
function objectKeysAreDifferentFormatter(pp, actual, expected, path) {
var missingProperties = j$.util.objectDifference(expected, actual),
extraProperties = j$.util.objectDifference(actual, expected),
missingPropertiesMessage = formatKeyValuePairs(missingProperties),
extraPropertiesMessage = formatKeyValuePairs(extraProperties),
messages = [];
extraProperties = j$.util.objectDifference(actual, expected),
missingPropertiesMessage = formatKeyValuePairs(pp, missingProperties),
extraPropertiesMessage = formatKeyValuePairs(pp, extraProperties),
messages = [];
if (!path.depth()) {
path = 'object';
}
if (missingPropertiesMessage.length) {
messages.push('Expected ' + path + ' to have properties' + missingPropertiesMessage);
messages.push(
'Expected ' + path + ' to have properties' + missingPropertiesMessage
);
}
if (extraPropertiesMessage.length) {
messages.push('Expected ' + path + ' not to have properties' + extraPropertiesMessage);
messages.push(
'Expected ' + path + ' not to have properties' + extraPropertiesMessage
);
}
return messages.join('\n');
}
function constructorsAreDifferentFormatter(actual, expected, path) {
function constructorsAreDifferentFormatter(pp, actual, expected, path) {
if (!path.depth()) {
path = 'object';
}
return 'Expected ' +
path + ' to be a kind of ' +
return (
'Expected ' +
path +
' to be a kind of ' +
j$.fnNameFor(expected.constructor) +
', but was ' + j$.pp(actual) + '.';
', but was ' +
pp(actual) +
'.'
);
}
function actualArrayIsLongerFormatter(actual, expected, path) {
return 'Unexpected ' +
path + (path.depth() ? ' = ' : '') +
j$.pp(actual) +
' in array.';
function actualArrayIsLongerFormatter(pp, actual, expected, path) {
return (
'Unexpected ' +
path +
(path.depth() ? ' = ' : '') +
pp(actual) +
' in array.'
);
}
function formatKeyValuePairs(obj) {
function formatKeyValuePairs(pp, obj) {
var formatted = '';
for (var key in obj) {
formatted += '\n ' + key + ': ' + j$.pp(obj[key]);
formatted += '\n ' + key + ': ' + pp(obj[key]);
}
return formatted;
}
function isDiffBuilder(obj) {
return obj && typeof obj.recordMismatch === 'function';
}
return MatchersUtil;
};

View File

@@ -3,6 +3,7 @@ getJasmineRequireObj().nothing = function() {
* {@link expect} nothing explicitly.
* @function
* @name matchers#nothing
* @since 2.8.0
* @example
* expect().nothing();
*/

View File

@@ -1,9 +1,11 @@
getJasmineRequireObj().requireAsyncMatchers = function(jRequire, j$) {
var availableMatchers = [
'toBePending',
'toBeResolved',
'toBeRejected',
'toBeResolvedTo',
'toBeRejectedWith'
'toBeRejectedWith',
'toBeRejectedWithError'
],
matchers = {};

View File

@@ -4,6 +4,7 @@ getJasmineRequireObj().requireMatchers = function(jRequire, j$) {
'toBe',
'toBeCloseTo',
'toBeDefined',
'toBeInstanceOf',
'toBeFalse',
'toBeFalsy',
'toBeGreaterThan',
@@ -19,15 +20,17 @@ getJasmineRequireObj().requireMatchers = function(jRequire, j$) {
'toBeUndefined',
'toContain',
'toEqual',
'toHaveSize',
'toHaveBeenCalled',
'toHaveBeenCalledBefore',
'toHaveBeenCalledOnceWith',
'toHaveBeenCalledTimes',
'toHaveBeenCalledWith',
'toHaveClass',
'toMatch',
'toThrow',
'toThrowError',
'toThrowMatching',
'toThrowMatching'
],
matchers = {};

View File

@@ -3,21 +3,29 @@ getJasmineRequireObj().toBe = function(j$) {
* {@link expect} the actual value to be `===` to the expected value.
* @function
* @name matchers#toBe
* @since 1.3.0
* @param {Object} expected - The expected value to compare against.
* @example
* expect(thing).toBe(realThing);
*/
function toBe(util) {
var tip = ' Tip: To check for deep equality, use .toEqual() instead of .toBe().';
function toBe(matchersUtil) {
var tip =
' Tip: To check for deep equality, use .toEqual() instead of .toBe().';
return {
compare: function(actual, expected) {
var result = {
pass: actual === expected,
pass: actual === expected
};
if (typeof expected === 'object') {
result.message = util.buildFailureMessage('toBe', result.pass, actual, expected) + tip;
result.message =
matchersUtil.buildFailureMessage(
'toBe',
result.pass,
actual,
expected
) + tip;
}
return result;

View File

@@ -3,6 +3,7 @@ getJasmineRequireObj().toBeCloseTo = function() {
* {@link expect} the actual value to be within a specified precision of the expected value.
* @function
* @name matchers#toBeCloseTo
* @since 1.3.0
* @param {Object} expected - The expected value to compare against.
* @param {Number} [precision=2] - The number of decimal points to check.
* @example
@@ -16,8 +17,13 @@ getJasmineRequireObj().toBeCloseTo = function() {
}
if (expected === null || actual === null) {
throw new Error('Cannot use toBeCloseTo with null. Arguments evaluated to: ' +
'expect(' + actual + ').toBeCloseTo(' + expected + ').'
throw new Error(
'Cannot use toBeCloseTo with null. Arguments evaluated to: ' +
'expect(' +
actual +
').toBeCloseTo(' +
expected +
').'
);
}
@@ -26,7 +32,7 @@ getJasmineRequireObj().toBeCloseTo = function() {
var maxDelta = Math.pow(10, -precision) / 2;
return {
pass: Math.round(delta * pow) / pow <= maxDelta
pass: Math.round(delta * pow) <= maxDelta * pow
};
}
};

View File

@@ -3,6 +3,7 @@ getJasmineRequireObj().toBeDefined = function() {
* {@link expect} the actual value to be defined. (Not `undefined`)
* @function
* @name matchers#toBeDefined
* @since 1.3.0
* @example
* expect(result).toBeDefined();
*/
@@ -10,7 +11,7 @@ getJasmineRequireObj().toBeDefined = function() {
return {
compare: function(actual) {
return {
pass: (void 0 !== actual)
pass: void 0 !== actual
};
}
};

View File

@@ -3,6 +3,7 @@ getJasmineRequireObj().toBeFalse = function() {
* {@link expect} the actual value to be `false`.
* @function
* @name matchers#toBeFalse
* @since 3.5.0
* @example
* expect(result).toBeFalse();
*/

View File

@@ -3,6 +3,7 @@ getJasmineRequireObj().toBeFalsy = function() {
* {@link expect} the actual value to be falsy
* @function
* @name matchers#toBeFalsy
* @since 2.0.0
* @example
* expect(result).toBeFalsy();
*/
@@ -10,7 +11,7 @@ getJasmineRequireObj().toBeFalsy = function() {
return {
compare: function(actual) {
return {
pass: !!!actual
pass: !actual
};
}
};

View File

@@ -3,6 +3,7 @@ getJasmineRequireObj().toBeGreaterThan = function() {
* {@link expect} the actual value to be greater than the expected value.
* @function
* @name matchers#toBeGreaterThan
* @since 2.0.0
* @param {Number} expected - The value to compare against.
* @example
* expect(result).toBeGreaterThan(3);
@@ -19,4 +20,3 @@ getJasmineRequireObj().toBeGreaterThan = function() {
return toBeGreaterThan;
};

View File

@@ -3,6 +3,7 @@ getJasmineRequireObj().toBeGreaterThanOrEqual = function() {
* {@link expect} the actual value to be greater than or equal to the expected value.
* @function
* @name matchers#toBeGreaterThanOrEqual
* @since 2.0.0
* @param {Number} expected - The expected value to compare against.
* @example
* expect(result).toBeGreaterThanOrEqual(25);

View File

@@ -0,0 +1,64 @@
getJasmineRequireObj().toBeInstanceOf = function(j$) {
var usageError = j$.formatErrorMsg(
'<toBeInstanceOf>',
'expect(value).toBeInstanceOf(<ConstructorFunction>)'
);
/**
* {@link expect} the actual to be an instance of the expected class
* @function
* @name matchers#toBeInstanceOf
* @since 3.5.0
* @param {Object} expected - The class or constructor function to check for
* @example
* expect('foo').toBeInstanceOf(String);
* expect(3).toBeInstanceOf(Number);
* expect(new Error()).toBeInstanceOf(Error);
*/
function toBeInstanceOf(matchersUtil) {
return {
compare: function(actual, expected) {
var actualType =
actual && actual.constructor
? j$.fnNameFor(actual.constructor)
: matchersUtil.pp(actual),
expectedType = expected
? j$.fnNameFor(expected)
: matchersUtil.pp(expected),
expectedMatcher,
pass;
try {
expectedMatcher = new j$.Any(expected);
pass = expectedMatcher.asymmetricMatch(actual);
} catch (error) {
throw new Error(
usageError('Expected value is not a constructor function')
);
}
if (pass) {
return {
pass: true,
message:
'Expected instance of ' +
actualType +
' not to be an instance of ' +
expectedType
};
} else {
return {
pass: false,
message:
'Expected instance of ' +
actualType +
' to be an instance of ' +
expectedType
};
}
}
};
}
return toBeInstanceOf;
};

View File

@@ -3,13 +3,13 @@ getJasmineRequireObj().toBeLessThan = function() {
* {@link expect} the actual value to be less than the expected value.
* @function
* @name matchers#toBeLessThan
* @since 2.0.0
* @param {Number} expected - The expected value to compare against.
* @example
* expect(result).toBeLessThan(0);
*/
function toBeLessThan() {
return {
compare: function(actual, expected) {
return {
pass: actual < expected

View File

@@ -3,13 +3,13 @@ getJasmineRequireObj().toBeLessThanOrEqual = function() {
* {@link expect} the actual value to be less than or equal to the expected value.
* @function
* @name matchers#toBeLessThanOrEqual
* @since 2.0.0
* @param {Number} expected - The expected value to compare against.
* @example
* expect(result).toBeLessThanOrEqual(123);
*/
function toBeLessThanOrEqual() {
return {
compare: function(actual, expected) {
return {
pass: actual <= expected

View File

@@ -3,20 +3,23 @@ getJasmineRequireObj().toBeNaN = function(j$) {
* {@link expect} the actual value to be `NaN` (Not a Number).
* @function
* @name matchers#toBeNaN
* @since 1.3.0
* @example
* expect(thing).toBeNaN();
*/
function toBeNaN() {
function toBeNaN(matchersUtil) {
return {
compare: function(actual) {
var result = {
pass: (actual !== actual)
pass: actual !== actual
};
if (result.pass) {
result.message = 'Expected actual not to be NaN.';
} else {
result.message = function() { return 'Expected ' + j$.pp(actual) + ' to be NaN.'; };
result.message = function() {
return 'Expected ' + matchersUtil.pp(actual) + ' to be NaN.';
};
}
return result;

View File

@@ -3,20 +3,23 @@ getJasmineRequireObj().toBeNegativeInfinity = function(j$) {
* {@link expect} the actual value to be `-Infinity` (-infinity).
* @function
* @name matchers#toBeNegativeInfinity
* @since 2.6.0
* @example
* expect(thing).toBeNegativeInfinity();
*/
function toBeNegativeInfinity() {
function toBeNegativeInfinity(matchersUtil) {
return {
compare: function(actual) {
var result = {
pass: (actual === Number.NEGATIVE_INFINITY)
pass: actual === Number.NEGATIVE_INFINITY
};
if (result.pass) {
result.message = 'Expected actual not to be -Infinity.';
} else {
result.message = function() { return 'Expected ' + j$.pp(actual) + ' to be -Infinity.'; };
result.message = function() {
return 'Expected ' + matchersUtil.pp(actual) + ' to be -Infinity.';
};
}
return result;

View File

@@ -3,6 +3,7 @@ getJasmineRequireObj().toBeNull = function() {
* {@link expect} the actual value to be `null`.
* @function
* @name matchers#toBeNull
* @since 1.3.0
* @example
* expect(result).toBeNull();
*/

View File

@@ -3,20 +3,23 @@ getJasmineRequireObj().toBePositiveInfinity = function(j$) {
* {@link expect} the actual value to be `Infinity` (infinity).
* @function
* @name matchers#toBePositiveInfinity
* @since 2.6.0
* @example
* expect(thing).toBePositiveInfinity();
*/
function toBePositiveInfinity() {
function toBePositiveInfinity(matchersUtil) {
return {
compare: function(actual) {
var result = {
pass: (actual === Number.POSITIVE_INFINITY)
pass: actual === Number.POSITIVE_INFINITY
};
if (result.pass) {
result.message = 'Expected actual not to be Infinity.';
} else {
result.message = function() { return 'Expected ' + j$.pp(actual) + ' to be Infinity.'; };
result.message = function() {
return 'Expected ' + matchersUtil.pp(actual) + ' to be Infinity.';
};
}
return result;

View File

@@ -3,6 +3,7 @@ getJasmineRequireObj().toBeTrue = function() {
* {@link expect} the actual value to be `true`.
* @function
* @name matchers#toBeTrue
* @since 3.5.0
* @example
* expect(result).toBeTrue();
*/

View File

@@ -3,6 +3,7 @@ getJasmineRequireObj().toBeTruthy = function() {
* {@link expect} the actual value to be truthy.
* @function
* @name matchers#toBeTruthy
* @since 2.0.0
* @example
* expect(thing).toBeTruthy();
*/

View File

@@ -3,6 +3,7 @@ getJasmineRequireObj().toBeUndefined = function() {
* {@link expect} the actual value to be `undefined`.
* @function
* @name matchers#toBeUndefined
* @since 1.3.0
* @example
* expect(result).toBeUndefined():
*/

View File

@@ -3,19 +3,17 @@ getJasmineRequireObj().toContain = function() {
* {@link expect} the actual value to contain a specific value.
* @function
* @name matchers#toContain
* @since 2.0.0
* @param {Object} expected - The value to look for.
* @example
* expect(array).toContain(anElement);
* expect(string).toContain(substring);
*/
function toContain(util, customEqualityTesters) {
customEqualityTesters = customEqualityTesters || [];
function toContain(matchersUtil) {
return {
compare: function(actual, expected) {
return {
pass: util.contains(actual, expected, customEqualityTesters)
pass: matchersUtil.contains(actual, expected)
};
}
};

View File

@@ -3,21 +3,20 @@ getJasmineRequireObj().toEqual = function(j$) {
* {@link expect} the actual value to be equal to the expected, using deep equality comparison.
* @function
* @name matchers#toEqual
* @since 1.3.0
* @param {Object} expected - Expected value
* @example
* expect(bigObject).toEqual({"foo": ['bar', 'baz']});
*/
function toEqual(util, customEqualityTesters) {
customEqualityTesters = customEqualityTesters || [];
function toEqual(matchersUtil) {
return {
compare: function(actual, expected) {
var result = {
pass: false
},
diffBuilder = j$.DiffBuilder();
diffBuilder = j$.DiffBuilder({ prettyPrinter: matchersUtil.pp });
result.pass = util.equals(actual, expected, customEqualityTesters, diffBuilder);
result.pass = matchersUtil.equals(actual, expected, diffBuilder);
// TODO: only set error message if test fails
result.message = diffBuilder.getMessage();

View File

@@ -1,33 +1,42 @@
getJasmineRequireObj().toHaveBeenCalled = function(j$) {
var getErrorMsg = j$.formatErrorMsg('<toHaveBeenCalled>', 'expect(<spyObj>).toHaveBeenCalled()');
var getErrorMsg = j$.formatErrorMsg(
'<toHaveBeenCalled>',
'expect(<spyObj>).toHaveBeenCalled()'
);
/**
* {@link expect} the actual (a {@link Spy}) to have been called.
* @function
* @name matchers#toHaveBeenCalled
* @since 1.3.0
* @example
* expect(mySpy).toHaveBeenCalled();
* expect(mySpy).not.toHaveBeenCalled();
*/
function toHaveBeenCalled() {
function toHaveBeenCalled(matchersUtil) {
return {
compare: function(actual) {
var result = {};
if (!j$.isSpy(actual)) {
throw new Error(getErrorMsg('Expected a spy, but got ' + j$.pp(actual) + '.'));
throw new Error(
getErrorMsg(
'Expected a spy, but got ' + matchersUtil.pp(actual) + '.'
)
);
}
if (arguments.length > 1) {
throw new Error(getErrorMsg('Does not take arguments, use toHaveBeenCalledWith'));
throw new Error(
getErrorMsg('Does not take arguments, use toHaveBeenCalledWith')
);
}
result.pass = actual.calls.any();
result.message = result.pass ?
'Expected spy ' + actual.and.identity + ' not to have been called.' :
'Expected spy ' + actual.and.identity + ' to have been called.';
result.message = result.pass
? 'Expected spy ' + actual.and.identity + ' not to have been called.'
: 'Expected spy ' + actual.and.identity + ' to have been called.';
return result;
}

View File

@@ -1,33 +1,46 @@
getJasmineRequireObj().toHaveBeenCalledBefore = function(j$) {
var getErrorMsg = j$.formatErrorMsg('<toHaveBeenCalledBefore>', 'expect(<spyObj>).toHaveBeenCalledBefore(<spyObj>)');
var getErrorMsg = j$.formatErrorMsg(
'<toHaveBeenCalledBefore>',
'expect(<spyObj>).toHaveBeenCalledBefore(<spyObj>)'
);
/**
* {@link expect} the actual value (a {@link Spy}) to have been called before another {@link Spy}.
* @function
* @name matchers#toHaveBeenCalledBefore
* @since 2.6.0
* @param {Spy} expected - {@link Spy} that should have been called after the `actual` {@link Spy}.
* @example
* expect(mySpy).toHaveBeenCalledBefore(otherSpy);
*/
function toHaveBeenCalledBefore() {
function toHaveBeenCalledBefore(matchersUtil) {
return {
compare: function(firstSpy, latterSpy) {
if (!j$.isSpy(firstSpy)) {
throw new Error(getErrorMsg('Expected a spy, but got ' + j$.pp(firstSpy) + '.'));
throw new Error(
getErrorMsg(
'Expected a spy, but got ' + matchersUtil.pp(firstSpy) + '.'
)
);
}
if (!j$.isSpy(latterSpy)) {
throw new Error(getErrorMsg('Expected a spy, but got ' + j$.pp(latterSpy) + '.'));
throw new Error(
getErrorMsg(
'Expected a spy, but got ' + matchersUtil.pp(latterSpy) + '.'
)
);
}
var result = { pass: false };
if (!firstSpy.calls.count()) {
result.message = 'Expected spy ' + firstSpy.and.identity + ' to have been called.';
result.message =
'Expected spy ' + firstSpy.and.identity + ' to have been called.';
return result;
}
if (!latterSpy.calls.count()) {
result.message = 'Expected spy ' + latterSpy.and.identity + ' to have been called.';
result.message =
'Expected spy ' + latterSpy.and.identity + ' to have been called.';
return result;
}
@@ -37,17 +50,36 @@ getJasmineRequireObj().toHaveBeenCalledBefore = function(j$) {
result.pass = latest1stSpyCall < first2ndSpyCall;
if (result.pass) {
result.message = 'Expected spy ' + firstSpy.and.identity + ' to not have been called before spy ' + latterSpy.and.identity + ', but it was';
result.message =
'Expected spy ' +
firstSpy.and.identity +
' to not have been called before spy ' +
latterSpy.and.identity +
', but it was';
} else {
var first1stSpyCall = firstSpy.calls.first().invocationOrder;
var latest2ndSpyCall = latterSpy.calls.mostRecent().invocationOrder;
if(first1stSpyCall < first2ndSpyCall) {
result.message = 'Expected latest call to spy ' + firstSpy.and.identity + ' to have been called before first call to spy ' + latterSpy.and.identity + ' (no interleaved calls)';
if (first1stSpyCall < first2ndSpyCall) {
result.message =
'Expected latest call to spy ' +
firstSpy.and.identity +
' to have been called before first call to spy ' +
latterSpy.and.identity +
' (no interleaved calls)';
} else if (latest2ndSpyCall > latest1stSpyCall) {
result.message = 'Expected first call to spy ' + latterSpy.and.identity + ' to have been called after latest call to spy ' + firstSpy.and.identity + ' (no interleaved calls)';
result.message =
'Expected first call to spy ' +
latterSpy.and.identity +
' to have been called after latest call to spy ' +
firstSpy.and.identity +
' (no interleaved calls)';
} else {
result.message = 'Expected spy ' + firstSpy.and.identity + ' to have been called before spy ' + latterSpy.and.identity;
result.message =
'Expected spy ' +
firstSpy.and.identity +
' to have been called before spy ' +
latterSpy.and.identity;
}
}

View File

@@ -0,0 +1,99 @@
getJasmineRequireObj().toHaveBeenCalledOnceWith = function(j$) {
var getErrorMsg = j$.formatErrorMsg(
'<toHaveBeenCalledOnceWith>',
'expect(<spyObj>).toHaveBeenCalledOnceWith(...arguments)'
);
/**
* {@link expect} the actual (a {@link Spy}) to have been called exactly once, and exactly with the particular arguments.
* @function
* @name matchers#toHaveBeenCalledOnceWith
* @since 3.6.0
* @param {...Object} - The arguments to look for
* @example
* expect(mySpy).toHaveBeenCalledOnceWith('foo', 'bar', 2);
*/
function toHaveBeenCalledOnceWith(util) {
return {
compare: function() {
var args = Array.prototype.slice.call(arguments, 0),
actual = args[0],
expectedArgs = args.slice(1);
if (!j$.isSpy(actual)) {
throw new Error(
getErrorMsg('Expected a spy, but got ' + util.pp(actual) + '.')
);
}
var prettyPrintedCalls = actual.calls
.allArgs()
.map(function(argsForCall) {
return ' ' + util.pp(argsForCall);
});
if (
actual.calls.count() === 1 &&
util.contains(actual.calls.allArgs(), expectedArgs)
) {
return {
pass: true,
message:
'Expected spy ' +
actual.and.identity +
' to have been called 0 times, multiple times, or once, but with arguments different from:\n' +
' ' +
util.pp(expectedArgs) +
'\n' +
'But the actual call was:\n' +
prettyPrintedCalls.join(',\n') +
'.\n\n'
};
}
function getDiffs() {
return actual.calls.allArgs().map(function(argsForCall, callIx) {
var diffBuilder = new j$.DiffBuilder();
util.equals(argsForCall, expectedArgs, diffBuilder);
return diffBuilder.getMessage();
});
}
function butString() {
switch (actual.calls.count()) {
case 0:
return 'But it was never called.\n\n';
case 1:
return (
'But the actual call was:\n' +
prettyPrintedCalls.join(',\n') +
'.\n' +
getDiffs().join('\n') +
'\n\n'
);
default:
return (
'But the actual calls were:\n' +
prettyPrintedCalls.join(',\n') +
'.\n\n'
);
}
}
return {
pass: false,
message:
'Expected spy ' +
actual.and.identity +
' to have been called only once, and with given args:\n' +
' ' +
util.pp(expectedArgs) +
'\n' +
butString()
};
}
};
}
return toHaveBeenCalledOnceWith;
};

View File

@@ -1,36 +1,59 @@
getJasmineRequireObj().toHaveBeenCalledTimes = function(j$) {
var getErrorMsg = j$.formatErrorMsg('<toHaveBeenCalledTimes>', 'expect(<spyObj>).toHaveBeenCalledTimes(<Number>)');
var getErrorMsg = j$.formatErrorMsg(
'<toHaveBeenCalledTimes>',
'expect(<spyObj>).toHaveBeenCalledTimes(<Number>)'
);
/**
* {@link expect} the actual (a {@link Spy}) to have been called the specified number of times.
* @function
* @name matchers#toHaveBeenCalledTimes
* @since 2.4.0
* @param {Number} expected - The number of invocations to look for.
* @example
* expect(mySpy).toHaveBeenCalledTimes(3);
*/
function toHaveBeenCalledTimes() {
function toHaveBeenCalledTimes(matchersUtil) {
return {
compare: function(actual, expected) {
if (!j$.isSpy(actual)) {
throw new Error(getErrorMsg('Expected a spy, but got ' + j$.pp(actual) + '.'));
throw new Error(
getErrorMsg(
'Expected a spy, but got ' + matchersUtil.pp(actual) + '.'
)
);
}
var args = Array.prototype.slice.call(arguments, 0),
result = { pass: false };
if (!j$.isNumber_(expected)){
throw new Error(getErrorMsg('The expected times failed is a required argument and must be a number.'));
if (!j$.isNumber_(expected)) {
throw new Error(
getErrorMsg(
'The expected times failed is a required argument and must be a number.'
)
);
}
actual = args[0];
var calls = actual.calls.count();
var timesMessage = expected === 1 ? 'once' : expected + ' times';
result.pass = calls === expected;
result.message = result.pass ?
'Expected spy ' + actual.and.identity + ' not to have been called ' + timesMessage + '. It was called ' + calls + ' times.' :
'Expected spy ' + actual.and.identity + ' to have been called ' + timesMessage + '. It was called ' + calls + ' times.';
result.message = result.pass
? 'Expected spy ' +
actual.and.identity +
' not to have been called ' +
timesMessage +
'. It was called ' +
calls +
' times.'
: 'Expected spy ' +
actual.and.identity +
' to have been called ' +
timesMessage +
'. It was called ' +
calls +
' times.';
return result;
}
};

View File

@@ -1,16 +1,19 @@
getJasmineRequireObj().toHaveBeenCalledWith = function(j$) {
var getErrorMsg = j$.formatErrorMsg('<toHaveBeenCalledWith>', 'expect(<spyObj>).toHaveBeenCalledWith(...arguments)');
var getErrorMsg = j$.formatErrorMsg(
'<toHaveBeenCalledWith>',
'expect(<spyObj>).toHaveBeenCalledWith(...arguments)'
);
/**
* {@link expect} the actual (a {@link Spy}) to have been called with particular arguments at least once.
* @function
* @name matchers#toHaveBeenCalledWith
* @since 1.3.0
* @param {...Object} - The arguments to look for
* @example
* expect(mySpy).toHaveBeenCalledWith('foo', 'bar', 2);
*/
function toHaveBeenCalledWith(util, customEqualityTesters) {
function toHaveBeenCalledWith(matchersUtil) {
return {
compare: function() {
var args = Array.prototype.slice.call(arguments, 0),
@@ -19,19 +22,74 @@ getJasmineRequireObj().toHaveBeenCalledWith = function(j$) {
result = { pass: false };
if (!j$.isSpy(actual)) {
throw new Error(getErrorMsg('Expected a spy, but got ' + j$.pp(actual) + '.'));
throw new Error(
getErrorMsg(
'Expected a spy, but got ' + matchersUtil.pp(actual) + '.'
)
);
}
if (!actual.calls.any()) {
result.message = function() { return '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:\n' +
' ' +
matchersUtil.pp(expectedArgs) +
'\nbut it was never called.'
);
};
return result;
}
if (util.contains(actual.calls.allArgs(), expectedArgs, customEqualityTesters)) {
if (matchersUtil.contains(actual.calls.allArgs(), expectedArgs)) {
result.pass = true;
result.message = function() { return '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:\n' +
' ' +
matchersUtil.pp(expectedArgs) +
'\nbut it was.'
);
};
} else {
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, '') + '.'; };
result.message = function() {
var prettyPrintedCalls = actual.calls
.allArgs()
.map(function(argsForCall) {
return ' ' + matchersUtil.pp(argsForCall);
});
var diffs = actual.calls
.allArgs()
.map(function(argsForCall, callIx) {
var diffBuilder = new j$.DiffBuilder();
matchersUtil.equals(argsForCall, expectedArgs, diffBuilder);
return (
'Call ' +
callIx +
':\n' +
diffBuilder.getMessage().replace(/^/gm, ' ')
);
});
return (
'Expected spy ' +
actual.and.identity +
' to have been called with:\n' +
' ' +
matchersUtil.pp(expectedArgs) +
'\n' +
'' +
'but actual calls were:\n' +
prettyPrintedCalls.join(',\n') +
'.\n\n' +
diffs.join('\n')
);
};
}
return result;

View File

@@ -3,17 +3,18 @@ getJasmineRequireObj().toHaveClass = function(j$) {
* {@link expect} the actual value to be a DOM element that has the expected class
* @function
* @name matchers#toHaveClass
* @since 3.0.0
* @param {Object} expected - The class name to test for
* @example
* var el = document.createElement('div');
* el.className = 'foo bar baz';
* expect(el).toHaveClass('bar');
*/
function toHaveClass(util, customEqualityTesters) {
function toHaveClass(matchersUtil) {
return {
compare: function(actual, expected) {
if (!isElement(actual)) {
throw new Error(j$.pp(actual) + ' is not a DOM element');
throw new Error(matchersUtil.pp(actual) + ' is not a DOM element');
}
return {
@@ -24,9 +25,9 @@ getJasmineRequireObj().toHaveClass = function(j$) {
}
function isElement(maybeEl) {
return maybeEl &&
maybeEl.classList &&
j$.isFunction_(maybeEl.classList.contains);
return (
maybeEl && maybeEl.classList && j$.isFunction_(maybeEl.classList.contains)
);
}
return toHaveClass;

View File

@@ -0,0 +1,51 @@
getJasmineRequireObj().toHaveSize = function(j$) {
/**
* {@link expect} the actual size to be equal to the expected, using array-like length or object keys size.
* @function
* @name matchers#toHaveSize
* @since 3.6.0
* @param {Object} expected - Expected size
* @example
* array = [1,2];
* expect(array).toHaveSize(2);
*/
function toHaveSize() {
return {
compare: function(actual, expected) {
var result = {
pass: false
};
if (
j$.isA_('WeakSet', actual) ||
j$.isWeakMap(actual) ||
j$.isDataView(actual)
) {
throw new Error('Cannot get size of ' + actual + '.');
}
if (j$.isSet(actual) || j$.isMap(actual)) {
result.pass = actual.size === expected;
} else if (isLength(actual.length)) {
result.pass = actual.length === expected;
} else {
result.pass = Object.keys(actual).length === expected;
}
return result;
}
};
}
var MAX_SAFE_INTEGER = Number.MAX_SAFE_INTEGER || 9007199254740991; // eslint-disable-line compat/compat
function isLength(value) {
return (
typeof value == 'number' &&
value > -1 &&
value % 1 === 0 &&
value <= MAX_SAFE_INTEGER
);
}
return toHaveSize;
};

View File

@@ -1,11 +1,14 @@
getJasmineRequireObj().toMatch = function(j$) {
var getErrorMsg = j$.formatErrorMsg('<toMatch>', 'expect(<expectation>).toMatch(<string> || <regexp>)');
var getErrorMsg = j$.formatErrorMsg(
'<toMatch>',
'expect(<expectation>).toMatch(<string> || <regexp>)'
);
/**
* {@link expect} the actual value to match a regular expression
* @function
* @name matchers#toMatch
* @since 1.3.0
* @param {RegExp|String} expected - Value to look for in the string.
* @example
* expect("my string").toMatch(/string$/);

View File

@@ -1,17 +1,20 @@
getJasmineRequireObj().toThrow = function(j$) {
var getErrorMsg = j$.formatErrorMsg('<toThrow>', 'expect(function() {<expectation>}).toThrow()');
var getErrorMsg = j$.formatErrorMsg(
'<toThrow>',
'expect(function() {<expectation>}).toThrow()'
);
/**
* {@link expect} a function to `throw` something.
* @function
* @name matchers#toThrow
* @since 2.0.0
* @param {Object} [expected] - Value that should be thrown. If not provided, simply the fact that something was thrown will be checked.
* @example
* expect(function() { return 'things'; }).toThrow('foo');
* expect(function() { return 'stuff'; }).toThrow();
*/
function toThrow(util) {
function toThrow(matchersUtil) {
return {
compare: function(actual, expected) {
var result = { pass: false },
@@ -36,16 +39,36 @@ getJasmineRequireObj().toThrow = function(j$) {
if (arguments.length == 1) {
result.pass = true;
result.message = function() { return 'Expected function not to throw, but it threw ' + j$.pp(thrown) + '.'; };
result.message = function() {
return (
'Expected function not to throw, but it threw ' +
matchersUtil.pp(thrown) +
'.'
);
};
return result;
}
if (util.equals(thrown, expected)) {
if (matchersUtil.equals(thrown, expected)) {
result.pass = true;
result.message = function() { return 'Expected function not to throw ' + j$.pp(expected) + '.'; };
result.message = function() {
return (
'Expected function not to throw ' +
matchersUtil.pp(expected) +
'.'
);
};
} else {
result.message = function() { return 'Expected function to throw ' + j$.pp(expected) + ', but it threw ' + j$.pp(thrown) + '.'; };
result.message = function() {
return (
'Expected function to throw ' +
matchersUtil.pp(expected) +
', but it threw ' +
matchersUtil.pp(thrown) +
'.'
);
};
}
return result;

View File

@@ -1,11 +1,14 @@
getJasmineRequireObj().toThrowError = function(j$) {
var getErrorMsg = j$.formatErrorMsg('<toThrowError>', 'expect(function() {<expectation>}).toThrowError(<ErrorConstructor>, <message>)');
var getErrorMsg = j$.formatErrorMsg(
'<toThrowError>',
'expect(function() {<expectation>}).toThrowError(<ErrorConstructor>, <message>)'
);
/**
* {@link expect} a function to `throw` an `Error`.
* @function
* @name matchers#toThrowError
* @since 2.0.0
* @param {Error} [expected] - `Error` constructor the object that was thrown needs to be an instance of. If not provided, `Error` will be used.
* @param {RegExp|String} [message] - The message that should be set on the thrown `Error`
* @example
@@ -15,7 +18,7 @@ getJasmineRequireObj().toThrowError = function(j$) {
* expect(function() { return 'other'; }).toThrowError(/foo/);
* expect(function() { return 'other'; }).toThrowError();
*/
function toThrowError () {
function toThrowError(matchersUtil) {
return {
compare: function(actual) {
var errorMatcher = getMatcher.apply(null, arguments),
@@ -33,7 +36,13 @@ getJasmineRequireObj().toThrowError = function(j$) {
}
if (!j$.isError_(thrown)) {
return fail(function() { return 'Expected function to throw an Error, but it threw ' + j$.pp(thrown) + '.'; });
return fail(function() {
return (
'Expected function to throw an Error, but it threw ' +
matchersUtil.pp(thrown) +
'.'
);
});
}
return errorMatcher.match(thrown);
@@ -67,7 +76,11 @@ getJasmineRequireObj().toThrowError = function(j$) {
function anyMatcher() {
return {
match: function(error) {
return pass('Expected function not to throw an Error, but it threw ' + j$.fnNameFor(error) + '.');
return pass(
'Expected function not to throw an Error, but it threw ' +
j$.fnNameFor(error) +
'.'
);
}
};
}
@@ -75,9 +88,13 @@ getJasmineRequireObj().toThrowError = function(j$) {
function exactMatcher(expected, errorType) {
if (expected && !isStringOrRegExp(expected)) {
if (errorType) {
throw new Error(getErrorMsg('Expected error message is not a string or RegExp.'));
throw new Error(
getErrorMsg('Expected error message is not a string or RegExp.')
);
} else {
throw new Error(getErrorMsg('Expected is not an Error, string, or RegExp.'));
throw new Error(
getErrorMsg('Expected is not an Error, string, or RegExp.')
);
}
}
@@ -89,14 +106,18 @@ getJasmineRequireObj().toThrowError = function(j$) {
}
}
var errorTypeDescription = errorType ? j$.fnNameFor(errorType) : 'an exception';
var errorTypeDescription = errorType
? j$.fnNameFor(errorType)
: 'an exception';
function thrownDescription(thrown) {
var thrownName = errorType ? j$.fnNameFor(thrown.constructor) : 'an exception',
thrownMessage = '';
var thrownName = errorType
? j$.fnNameFor(thrown.constructor)
: 'an exception',
thrownMessage = '';
if (expected) {
thrownMessage = ' with message ' + j$.pp(thrown.message);
thrownMessage = ' with message ' + matchersUtil.pp(thrown.message);
}
return thrownName + thrownMessage;
@@ -106,27 +127,40 @@ getJasmineRequireObj().toThrowError = function(j$) {
if (expected === null) {
return '';
} else if (expected instanceof RegExp) {
return ' with a message matching ' + j$.pp(expected);
return ' with a message matching ' + matchersUtil.pp(expected);
} else {
return ' with message ' + j$.pp(expected);
return ' with message ' + matchersUtil.pp(expected);
}
}
function matches(error) {
return (errorType === null || error instanceof errorType) &&
(expected === null || messageMatch(error.message));
return (
(errorType === null || error instanceof errorType) &&
(expected === null || messageMatch(error.message))
);
}
return {
match: function(thrown) {
if (matches(thrown)) {
return pass(function() {
return 'Expected function not to throw ' + errorTypeDescription + messageDescription() + '.';
return (
'Expected function not to throw ' +
errorTypeDescription +
messageDescription() +
'.'
);
});
} else {
return fail(function() {
return 'Expected function to throw ' + errorTypeDescription + messageDescription() +
', but it threw ' + thrownDescription(thrown) + '.';
return (
'Expected function to throw ' +
errorTypeDescription +
messageDescription() +
', but it threw ' +
thrownDescription(thrown) +
'.'
);
});
}
}
@@ -134,7 +168,7 @@ getJasmineRequireObj().toThrowError = function(j$) {
}
function isStringOrRegExp(potential) {
return potential instanceof RegExp || (typeof potential == 'string');
return potential instanceof RegExp || typeof potential == 'string';
}
function isAnErrorType(type) {

View File

@@ -1,15 +1,19 @@
getJasmineRequireObj().toThrowMatching = function(j$) {
var usageError = j$.formatErrorMsg('<toThrowMatching>', 'expect(function() {<expectation>}).toThrowMatching(<Predicate>)');
var usageError = j$.formatErrorMsg(
'<toThrowMatching>',
'expect(function() {<expectation>}).toThrowMatching(<Predicate>)'
);
/**
* {@link expect} a function to `throw` something matching a predicate.
* @function
* @name matchers#toThrowMatching
* @since 3.0.0
* @param {Function} predicate - A function that takes the thrown exception as its parameter and returns true if it matches.
* @example
* expect(function() { throw new Error('nope'); }).toThrowMatching(function(thrown) { return thrown.message === 'nope'; });
*/
function toThrowMatching() {
function toThrowMatching(matchersUtil) {
return {
compare: function(actual, predicate) {
var thrown;
@@ -30,23 +34,32 @@ getJasmineRequireObj().toThrowMatching = function(j$) {
}
if (predicate(thrown)) {
return pass('Expected function not to throw an exception matching a predicate.');
return pass(
'Expected function not to throw an exception matching a predicate.'
);
} else {
return fail(function() {
return 'Expected function to throw an exception matching a predicate, ' +
'but it threw ' + thrownDescription(thrown) + '.';
});
return fail(function() {
return (
'Expected function to throw an exception matching a predicate, ' +
'but it threw ' +
thrownDescription(thrown) +
'.'
);
});
}
}
};
}
function thrownDescription(thrown) {
if (thrown && thrown.constructor) {
return j$.fnNameFor(thrown.constructor) + ' with message ' +
j$.pp(thrown.message);
} else {
return j$.pp(thrown);
function thrownDescription(thrown) {
if (thrown && thrown.constructor) {
return (
j$.fnNameFor(thrown.constructor) +
' with message ' +
matchersUtil.pp(thrown.message)
);
} else {
return matchersUtil.pp(thrown);
}
}
}