Merge branch 'custom-object-formatters' into cof-merge-candidate

This commit is contained in:
Steve Gravrock
2020-02-11 11:44:38 -08:00
84 changed files with 3047 additions and 1017 deletions

View File

@@ -276,6 +276,7 @@ getJasmineRequireObj().Env = function(j$) {
}
var customMatchers =
runnableResources[currentRunnable().id].customMatchers;
for (var matcherName in matchersToAdd) {
customMatchers[matcherName] = matchersToAdd[matcherName];
}
@@ -289,11 +290,24 @@ getJasmineRequireObj().Env = function(j$) {
}
var customAsyncMatchers =
runnableResources[currentRunnable().id].customAsyncMatchers;
for (var matcherName in matchersToAdd) {
customAsyncMatchers[matcherName] = matchersToAdd[matcherName];
}
};
this.addCustomObjectFormatter = function(formatter) {
if (!currentRunnable()) {
throw new Error(
'Custom object formatters must be added in a before function or a spec'
);
}
runnableResources[currentRunnable().id].customObjectFormatters.push(
formatter
);
};
j$.Expectation.addCoreMatchers(j$.matchers);
j$.Expectation.addAsyncCoreMatchers(j$.asyncMatchers);
@@ -307,10 +321,28 @@ getJasmineRequireObj().Env = function(j$) {
return 'suite' + nextSuiteId++;
};
var makePrettyPrinter = function() {
var customObjectFormatters =
runnableResources[currentRunnable().id].customObjectFormatters;
return j$.makePrettyPrinter(customObjectFormatters);
};
var makeMatchersUtil = function() {
var customEqualityTesters =
runnableResources[currentRunnable().id].customEqualityTesters;
return new j$.MatchersUtil({
customTesters: customEqualityTesters,
pp: makePrettyPrinter()
});
};
var expectationFactory = function(actual, spec) {
var customEqualityTesters =
runnableResources[spec.id].customEqualityTesters;
return j$.Expectation.factory({
util: j$.matchersUtil,
customEqualityTesters: runnableResources[spec.id].customEqualityTesters,
matchersUtil: makeMatchersUtil(),
customEqualityTesters: customEqualityTesters,
customMatchers: runnableResources[spec.id].customMatchers,
actual: actual,
addExpectationResult: addExpectationResult
@@ -349,7 +381,7 @@ getJasmineRequireObj().Env = function(j$) {
var asyncExpectationFactory = function(actual, spec, runableType) {
return j$.Expectation.asyncFactory({
util: j$.matchersUtil,
matchersUtil: makeMatchersUtil(),
customEqualityTesters: runnableResources[spec.id].customEqualityTesters,
customAsyncMatchers: runnableResources[spec.id].customAsyncMatchers,
actual: actual,
@@ -378,7 +410,8 @@ getJasmineRequireObj().Env = function(j$) {
customMatchers: {},
customAsyncMatchers: {},
customSpyStrategies: {},
defaultStrategyFn: undefined
defaultStrategyFn: undefined,
customObjectFormatters: []
};
if (runnableResources[parentRunnableId]) {
@@ -1171,7 +1204,7 @@ getJasmineRequireObj().Env = function(j$) {
message += error;
} else {
// pretty print all kind of objects. This includes arrays.
message += j$.pp(error);
message += makePrettyPrinter()(error);
}
}

View File

@@ -129,7 +129,7 @@ getJasmineRequireObj().Expectation = function(j$) {
return result;
}
function negatedFailureMessage(result, matcherName, args, util) {
function negatedFailureMessage(result, matcherName, args, matchersUtil) {
if (result.message) {
if (j$.isFunction_(result.message)) {
return result.message();
@@ -141,7 +141,7 @@ getJasmineRequireObj().Expectation = function(j$) {
args = args.slice();
args.unshift(true);
args.unshift(matcherName);
return util.buildFailureMessage.apply(null, args);
return matchersUtil.buildFailureMessage.apply(matchersUtil, args);
}
function negate(result) {

View File

@@ -16,7 +16,7 @@ getJasmineRequireObj().ExpectationFilterChain = function() {
result,
matcherName,
args,
util
matchersUtil
) {
return this.callFirst_('buildFailureMessage', arguments).result;
};

View File

@@ -1,6 +1,8 @@
getJasmineRequireObj().Expector = function(j$) {
function Expector(options) {
this.util = options.util || { buildFailureMessage: function() {} };
this.matchersUtil = options.matchersUtil || {
buildFailureMessage: function() {}
};
this.customEqualityTesters = options.customEqualityTesters || [];
this.actual = options.actual;
this.addExpectationResult = options.addExpectationResult || function() {};
@@ -18,7 +20,7 @@ getJasmineRequireObj().Expector = function(j$) {
this.args.unshift(this.actual);
var matcher = matcherFactory(this.util, this.customEqualityTesters);
var matcher = matcherFactory(this.matchersUtil, this.customEqualityTesters);
var comparisonFunc = this.filters.selectComparisonFunc(matcher);
return comparisonFunc || matcher.compare;
};
@@ -34,7 +36,7 @@ getJasmineRequireObj().Expector = function(j$) {
result,
this.matcherName,
this.args,
this.util,
this.matchersUtil,
defaultMessage
);
return this.filters.modifyFailureMessage(msg || defaultMessage());
@@ -44,7 +46,10 @@ getJasmineRequireObj().Expector = function(j$) {
var args = self.args.slice();
args.unshift(false);
args.unshift(self.matcherName);
return self.util.buildFailureMessage.apply(null, args);
return self.matchersUtil.buildFailureMessage.apply(
self.matchersUtil,
args
);
} else if (j$.isFunction_(result.message)) {
return result.message();
} else {

View File

@@ -1,9 +1,11 @@
getJasmineRequireObj().pp = function(j$) {
function PrettyPrinter() {
getJasmineRequireObj().makePrettyPrinter = function(j$) {
function SinglePrettyPrintRun(customObjectFormatters, pp) {
this.customObjectFormatters_ = customObjectFormatters;
this.ppNestLevel_ = 0;
this.seen = [];
this.length = 0;
this.stringParts = [];
this.pp_ = pp;
}
function hasCustomToString(value) {
@@ -21,10 +23,14 @@ getJasmineRequireObj().pp = function(j$) {
}
}
PrettyPrinter.prototype.format = function(value) {
SinglePrettyPrintRun.prototype.format = function(value) {
this.ppNestLevel_++;
try {
if (j$.util.isUndefined(value)) {
var customFormatResult = this.applyCustomFormatters_(value);
if (customFormatResult) {
this.emitScalar(customFormatResult);
} else if (j$.util.isUndefined(value)) {
this.emitScalar('undefined');
} else if (value === null) {
this.emitScalar('null');
@@ -33,7 +39,7 @@ getJasmineRequireObj().pp = function(j$) {
} else if (value === j$.getGlobal()) {
this.emitScalar('<global>');
} else if (value.jasmineToString) {
this.emitScalar(value.jasmineToString());
this.emitScalar(value.jasmineToString(this.pp_));
} else if (typeof value === 'string') {
this.emitString(value);
} else if (j$.isSpy(value)) {
@@ -95,7 +101,11 @@ getJasmineRequireObj().pp = function(j$) {
}
};
PrettyPrinter.prototype.iterateObject = function(obj, fn) {
SinglePrettyPrintRun.prototype.applyCustomFormatters_ = function(value) {
return customFormat(value, this.customObjectFormatters_);
};
SinglePrettyPrintRun.prototype.iterateObject = function(obj, fn) {
var objKeys = keys(obj, j$.isArray_(obj));
var isGetter = function isGetter(prop) {};
@@ -114,15 +124,15 @@ getJasmineRequireObj().pp = function(j$) {
return objKeys.length > length;
};
PrettyPrinter.prototype.emitScalar = function(value) {
SinglePrettyPrintRun.prototype.emitScalar = function(value) {
this.append(value);
};
PrettyPrinter.prototype.emitString = function(value) {
SinglePrettyPrintRun.prototype.emitString = function(value) {
this.append("'" + value + "'");
};
PrettyPrinter.prototype.emitArray = function(array) {
SinglePrettyPrintRun.prototype.emitArray = function(array) {
if (this.ppNestLevel_ > j$.MAX_PRETTY_PRINT_DEPTH) {
this.append('Array');
return;
@@ -158,7 +168,7 @@ getJasmineRequireObj().pp = function(j$) {
this.append(' ]');
};
PrettyPrinter.prototype.emitSet = function(set) {
SinglePrettyPrintRun.prototype.emitSet = function(set) {
if (this.ppNestLevel_ > j$.MAX_PRETTY_PRINT_DEPTH) {
this.append('Set');
return;
@@ -183,7 +193,7 @@ getJasmineRequireObj().pp = function(j$) {
this.append(' )');
};
PrettyPrinter.prototype.emitMap = function(map) {
SinglePrettyPrintRun.prototype.emitMap = function(map) {
if (this.ppNestLevel_ > j$.MAX_PRETTY_PRINT_DEPTH) {
this.append('Map');
return;
@@ -208,7 +218,7 @@ getJasmineRequireObj().pp = function(j$) {
this.append(' )');
};
PrettyPrinter.prototype.emitObject = function(obj) {
SinglePrettyPrintRun.prototype.emitObject = function(obj) {
var ctor = obj.constructor,
constructorName;
@@ -244,7 +254,7 @@ getJasmineRequireObj().pp = function(j$) {
this.append(' })');
};
PrettyPrinter.prototype.emitTypedArray = function(arr) {
SinglePrettyPrintRun.prototype.emitTypedArray = function(arr) {
var constructorName = j$.fnNameFor(arr.constructor),
limitedArray = Array.prototype.slice.call(
arr,
@@ -260,7 +270,7 @@ getJasmineRequireObj().pp = function(j$) {
this.append(constructorName + ' [ ' + itemsString + ' ]');
};
PrettyPrinter.prototype.emitDomElement = function(el) {
SinglePrettyPrintRun.prototype.emitDomElement = function(el) {
var tagName = el.tagName.toLowerCase(),
attrs = el.attributes,
i,
@@ -286,7 +296,11 @@ getJasmineRequireObj().pp = function(j$) {
this.append(out);
};
PrettyPrinter.prototype.formatProperty = function(obj, property, isGetter) {
SinglePrettyPrintRun.prototype.formatProperty = function(
obj,
property,
isGetter
) {
this.append(property);
this.append(': ');
if (isGetter) {
@@ -296,7 +310,7 @@ getJasmineRequireObj().pp = function(j$) {
}
};
PrettyPrinter.prototype.append = function(value) {
SinglePrettyPrintRun.prototype.append = function(value) {
// This check protects us from the rare case where an object has overriden
// `toString()` with an invalid implementation (returning a non-string).
if (typeof value !== 'string') {
@@ -360,9 +374,32 @@ getJasmineRequireObj().pp = function(j$) {
return extraKeys;
}
return function(value) {
var prettyPrinter = new PrettyPrinter();
prettyPrinter.format(value);
return prettyPrinter.stringParts.join('');
function customFormat(value, customObjectFormatters) {
var i, result;
for (i = 0; i < customObjectFormatters.length; i++) {
result = customObjectFormatters[i](value);
if (result !== undefined) {
return result;
}
}
}
return function(customObjectFormatters) {
customObjectFormatters = customObjectFormatters || [];
var pp = function(value) {
var prettyPrinter = new SinglePrettyPrintRun(customObjectFormatters, pp);
prettyPrinter.format(value);
return prettyPrinter.stringParts.join('');
};
pp.customFormat_ = function(value) {
return customFormat(value, customObjectFormatters);
};
return pp;
};
};

View File

@@ -7,6 +7,11 @@ getJasmineRequireObj().Spy = function(j$) {
};
})();
var matchersUtil = new j$.MatchersUtil({
customTesters: [],
pp: j$.makePrettyPrinter()
});
/**
* _Note:_ Do not construct this directly, use {@link spyOn}, {@link spyOnProperty}, {@link jasmine.createSpy}, or {@link jasmine.createSpyObj}
* @constructor
@@ -202,7 +207,7 @@ getJasmineRequireObj().Spy = function(j$) {
var i;
for (i = 0; i < this.strategies.length; i++) {
if (j$.matchersUtil.equals(args, this.strategies[i].args)) {
if (matchersUtil.equals(args, this.strategies[i].args)) {
return this.strategies[i].strategy;
}
}

View File

@@ -0,0 +1,105 @@
getJasmineRequireObj().asymmetricEqualityTesterArgCompatShim = function(j$) {
/*
Older versions of Jasmine passed an array of custom equality testers as the
second argument to each asymmetric equality tester's `asymmetricMatch`
method. Newer versions will pass a `MatchersUtil` instance. The
asymmetricEqualityTesterArgCompatShim allows for a graceful migration from
the old interface to the new by "being" both an array of custom equality
testers and a `MatchersUtil` at the same time.
This code should be removed in the next major release.
*/
var likelyArrayProps = [
'concat',
'constructor',
'copyWithin',
'entries',
'every',
'fill',
'filter',
'find',
'findIndex',
'flat',
'flatMap',
'forEach',
'includes',
'indexOf',
'join',
'keys',
'lastIndexOf',
'length',
'map',
'pop',
'push',
'reduce',
'reduceRight',
'reverse',
'shift',
'slice',
'some',
'sort',
'splice',
'toLocaleString',
'toSource',
'toString',
'unshift',
'values'
];
function asymmetricEqualityTesterArgCompatShim(
matchersUtil,
customEqualityTesters
) {
var self = Object.create(matchersUtil),
props,
i,
k;
copy(self, customEqualityTesters, 'length');
for (i = 0; i < customEqualityTesters.length; i++) {
copy(self, customEqualityTesters, i);
}
var props = arrayProps();
for (i = 0; i < props.length; i++) {
k = props[i];
if (k !== 'length') {
copy(self, Array.prototype, k);
}
}
return self;
}
function copy(dest, src, propName) {
Object.defineProperty(dest, propName, {
get: function() {
return src[propName];
}
});
}
function arrayProps() {
var props, a, k;
if (!Object.getOwnPropertyDescriptors) {
return likelyArrayProps.filter(function(k) {
return Array.prototype.hasOwnProperty(k);
});
}
props = Object.getOwnPropertyDescriptors(Array.prototype);
a = [];
for (k in props) {
a.push(k);
}
return a;
}
return asymmetricEqualityTesterArgCompatShim;
};

View File

@@ -3,7 +3,7 @@ getJasmineRequireObj().ArrayContaining = function(j$) {
this.sample = sample;
}
ArrayContaining.prototype.asymmetricMatch = function(other, customTesters) {
ArrayContaining.prototype.asymmetricMatch = function(other, matchersUtil) {
if (!j$.isArray_(this.sample)) {
throw new Error('You must provide an array to arrayContaining, not ' + j$.pp(this.sample) + '.');
}
@@ -17,7 +17,7 @@ getJasmineRequireObj().ArrayContaining = function(j$) {
for (var i = 0; i < this.sample.length; i++) {
var item = this.sample[i];
if (!j$.matchersUtil.contains(other, item, customTesters)) {
if (!matchersUtil.contains(other, item)) {
return false;
}
}
@@ -25,8 +25,8 @@ getJasmineRequireObj().ArrayContaining = function(j$) {
return true;
};
ArrayContaining.prototype.jasmineToString = function () {
return '<jasmine.arrayContaining(' + j$.pp(this.sample) +')>';
ArrayContaining.prototype.jasmineToString = function (pp) {
return '<jasmine.arrayContaining(' + pp(this.sample) +')>';
};
return ArrayContaining;

View File

@@ -4,7 +4,7 @@ getJasmineRequireObj().ArrayWithExactContents = function(j$) {
this.sample = sample;
}
ArrayWithExactContents.prototype.asymmetricMatch = function(other, customTesters) {
ArrayWithExactContents.prototype.asymmetricMatch = function(other, matchersUtil) {
if (!j$.isArray_(this.sample)) {
throw new Error('You must provide an array to arrayWithExactContents, not ' + j$.pp(this.sample) + '.');
}
@@ -15,7 +15,7 @@ getJasmineRequireObj().ArrayWithExactContents = function(j$) {
for (var i = 0; i < this.sample.length; i++) {
var item = this.sample[i];
if (!j$.matchersUtil.contains(other, item, customTesters)) {
if (!matchersUtil.contains(other, item)) {
return false;
}
}
@@ -23,8 +23,8 @@ getJasmineRequireObj().ArrayWithExactContents = function(j$) {
return true;
};
ArrayWithExactContents.prototype.jasmineToString = function() {
return '<jasmine.arrayWithExactContents ' + j$.pp(this.sample) + '>';
ArrayWithExactContents.prototype.jasmineToString = function(pp) {
return '<jasmine.arrayWithExactContents(' + pp(this.sample) + ')>';
};
return ArrayWithExactContents;

View File

@@ -7,7 +7,7 @@ getJasmineRequireObj().MapContaining = function(j$) {
this.sample = sample;
}
MapContaining.prototype.asymmetricMatch = function(other, customTesters) {
MapContaining.prototype.asymmetricMatch = function(other, matchersUtil) {
if (!j$.isMap(other)) return false;
var hasAllMatches = true;
@@ -17,8 +17,8 @@ getJasmineRequireObj().MapContaining = function(j$) {
var hasMatch = false;
j$.util.forEachBreakable(other, function(oBreakLoop, oValue, oKey) {
if (
j$.matchersUtil.equals(oKey, key, customTesters)
&& j$.matchersUtil.equals(oValue, value, customTesters)
matchersUtil.equals(oKey, key)
&& matchersUtil.equals(oValue, value)
) {
hasMatch = true;
oBreakLoop();
@@ -33,8 +33,8 @@ getJasmineRequireObj().MapContaining = function(j$) {
return hasAllMatches;
};
MapContaining.prototype.jasmineToString = function() {
return '<jasmine.mapContaining(' + j$.pp(this.sample) + ')>';
MapContaining.prototype.jasmineToString = function(pp) {
return '<jasmine.mapContaining(' + pp(this.sample) + ')>';
};
return MapContaining;

View File

@@ -28,13 +28,13 @@ getJasmineRequireObj().ObjectContaining = function(j$) {
return hasProperty(getPrototype(obj), property);
}
ObjectContaining.prototype.asymmetricMatch = function(other, customTesters) {
ObjectContaining.prototype.asymmetricMatch = function(other, matchersUtil) {
if (typeof(this.sample) !== 'object') { throw new Error('You must provide an object to objectContaining, not \''+this.sample+'\'.'); }
if (typeof(other) !== 'object') { return false; }
for (var property in this.sample) {
if (!hasProperty(other, property) ||
!j$.matchersUtil.equals(this.sample[property], other[property], customTesters)) {
!matchersUtil.equals(this.sample[property], other[property])) {
return false;
}
}
@@ -42,10 +42,10 @@ getJasmineRequireObj().ObjectContaining = function(j$) {
return true;
};
ObjectContaining.prototype.valuesForDiff_ = function(other) {
ObjectContaining.prototype.valuesForDiff_ = function(other, pp) {
if (!j$.isObject_(other)) {
return {
self: this.jasmineToString(),
self: this.jasmineToString(pp),
other: other
};
}
@@ -63,8 +63,8 @@ getJasmineRequireObj().ObjectContaining = function(j$) {
};
};
ObjectContaining.prototype.jasmineToString = function() {
return '<jasmine.objectContaining(' + j$.pp(this.sample) + ')>';
ObjectContaining.prototype.jasmineToString = function(pp) {
return '<jasmine.objectContaining(' + pp(this.sample) + ')>';
};
return ObjectContaining;

View File

@@ -7,17 +7,17 @@ getJasmineRequireObj().SetContaining = function(j$) {
this.sample = sample;
}
SetContaining.prototype.asymmetricMatch = function(other, customTesters) {
SetContaining.prototype.asymmetricMatch = function(other, matchersUtil) {
if (!j$.isSet(other)) return false;
var hasAllMatches = true;
j$.util.forEachBreakable(this.sample, function(breakLoop, item) {
// for each item in `sample` there should be at least one matching item in `other`
// (not using `j$.matchersUtil.contains` because it compares set members by reference,
// (not using `matchersUtil.contains` because it compares set members by reference,
// not by deep value equality)
var hasMatch = false;
j$.util.forEachBreakable(other, function(oBreakLoop, oItem) {
if (j$.matchersUtil.equals(oItem, item, customTesters)) {
if (matchersUtil.equals(oItem, item)) {
hasMatch = true;
oBreakLoop();
}
@@ -31,8 +31,8 @@ getJasmineRequireObj().SetContaining = function(j$) {
return hasAllMatches;
};
SetContaining.prototype.jasmineToString = function() {
return '<jasmine.setContaining(' + j$.pp(this.sample) + ')>';
SetContaining.prototype.jasmineToString = function(pp) {
return '<jasmine.setContaining(' + pp(this.sample) + ')>';
};
return SetContaining;

View File

@@ -1,16 +1,51 @@
getJasmineRequireObj().DiffBuilder = function(j$) {
return function DiffBuilder() {
var path = new j$.ObjectPath(),
mismatches = [];
getJasmineRequireObj().DiffBuilder = function (j$) {
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;
},
recordMismatch: function (formatter) {
mismatches.add(path, formatter);
},
getMessage: function () {
return mismatches.join('\n');
var messages = [];
mismatches.traverse(function (path, isLeaf, formatter) {
var actualCustom, expectedCustom, useCustom,
actual = path.dereference(actualRoot),
expected = path.dereference(expectedRoot);
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) {
@@ -21,12 +56,16 @@ getJasmineRequireObj().DiffBuilder = function(j$) {
}
};
function defaultFormatter (actual, expected, path) {
function defaultFormatter(actual, expected, path, prettyPrinter) {
return wrapPrettyPrinted(prettyPrinter(actual), prettyPrinter(expected), path);
}
function wrapPrettyPrinted(actual, expected, path) {
return 'Expected ' +
path + (path.depth() ? ' = ' : '') +
j$.pp(actual) +
actual +
' to equal ' +
j$.pp(expected) +
expected +
'.';
}
};

View File

@@ -0,0 +1,62 @@
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

@@ -11,10 +11,24 @@ getJasmineRequireObj().ObjectPath = function(j$) {
}
};
ObjectPath.prototype.dereference = function(obj) {
var i;
for (i = 0; i < this.components.length; i++) {
obj = obj[this.components[i]];
}
return obj;
};
ObjectPath.prototype.add = function(component) {
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;
};

View File

@@ -10,7 +10,7 @@ getJasmineRequireObj().toBeRejected = function(j$) {
* @example
* return expectAsync(aPromise).toBeRejected();
*/
return function toBeRejected(util) {
return function toBeRejected() {
return {
compare: function(actual) {
if (!j$.isPromiseLike(actual)) {

View File

@@ -11,7 +11,7 @@ getJasmineRequireObj().toBeRejectedWith = function(j$) {
* @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)) {
@@ -21,7 +21,7 @@ getJasmineRequireObj().toBeRejectedWith = function(j$) {
function prefix(passed) {
return 'Expected a promise ' +
(passed ? 'not ' : '') +
'to be rejected with ' + j$.pp(expectedValue);
'to be rejected with ' + matchersUtil.pp(expectedValue);
}
return actualPromise.then(
@@ -32,7 +32,7 @@ getJasmineRequireObj().toBeRejectedWith = function(j$) {
};
},
function(actualValue) {
if (util.equals(actualValue, expectedValue, customEqualityTesters)) {
if (matchersUtil.equals(actualValue, expectedValue)) {
return {
pass: true,
message: prefix(true) + '.'
@@ -40,7 +40,7 @@ getJasmineRequireObj().toBeRejectedWith = function(j$) {
} else {
return {
pass: false,
message: prefix(false) + ' but it was rejected with ' + j$.pp(actualValue) + '.'
message: prefix(false) + ' but it was rejected with ' + matchersUtil.pp(actualValue) + '.'
};
}
}

View File

@@ -14,14 +14,14 @@ getJasmineRequireObj().toBeRejectedWithError = function(j$) {
* await expectAsync(aPromise).toBeRejectedWithError('Error message');
* return expectAsync(aPromise).toBeRejectedWithError(/Error message/);
*/
return function toBeRejectedWithError() {
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);
var expected = getExpectedFromArgs(arg1, arg2, matchersUtil);
return actualPromise.then(
function() {
@@ -30,15 +30,15 @@ getJasmineRequireObj().toBeRejectedWithError = function(j$) {
message: 'Expected a promise to be rejected but it was resolved.'
};
},
function(actualValue) { return matchError(actualValue, expected); }
function(actualValue) { return matchError(actualValue, expected, matchersUtil); }
);
}
};
};
function matchError(actual, expected) {
function matchError(actual, expected, matchersUtil) {
if (!j$.isError_(actual)) {
return fail(expected, 'rejected with ' + j$.pp(actual));
return fail(expected, 'rejected with ' + matchersUtil.pp(actual));
}
if (!(actual instanceof expected.error)) {
@@ -55,7 +55,7 @@ getJasmineRequireObj().toBeRejectedWithError = function(j$) {
return pass(expected);
}
return fail(expected, 'rejected with ' + j$.pp(actual));
return fail(expected, 'rejected with ' + matchersUtil.pp(actual));
}
function pass(expected) {
@@ -73,7 +73,7 @@ getJasmineRequireObj().toBeRejectedWithError = function(j$) {
}
function getExpectedFromArgs(arg1, arg2) {
function getExpectedFromArgs(arg1, arg2, matchersUtil) {
var error, message;
if (isErrorConstructor(arg1)) {
@@ -87,7 +87,7 @@ getJasmineRequireObj().toBeRejectedWithError = function(j$) {
return {
error: error,
message: message,
printValue: j$.fnNameFor(error) + (typeof message === 'undefined' ? '' : ': ' + j$.pp(message))
printValue: j$.fnNameFor(error) + (typeof message === 'undefined' ? '' : ': ' + matchersUtil.pp(message))
};
}

View File

@@ -10,7 +10,7 @@ getJasmineRequireObj().toBeResolved = function(j$) {
* @example
* return expectAsync(aPromise).toBeResolved();
*/
return function toBeResolved(util) {
return function toBeResolved() {
return {
compare: function(actual) {
if (!j$.isPromiseLike(actual)) {

View File

@@ -11,7 +11,7 @@ getJasmineRequireObj().toBeResolvedTo = function(j$) {
* @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)) {
@@ -21,12 +21,12 @@ getJasmineRequireObj().toBeResolvedTo = function(j$) {
function prefix(passed) {
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)) {
if (matchersUtil.equals(actualValue, expectedValue)) {
return {
pass: true,
message: prefix(true) + '.'
@@ -34,7 +34,7 @@ getJasmineRequireObj().toBeResolvedTo = function(j$) {
} else {
return {
pass: false,
message: prefix(false) + ' but it was resolved to ' + j$.pp(actualValue) + '.'
message: prefix(false) + ' but it was resolved to ' + matchersUtil.pp(actualValue) + '.'
};
}
},

View File

@@ -1,72 +1,100 @@
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
* @since 2.0.0
* @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
* @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 || [];
if (j$.isSet(haystack)) {
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]);
}
}
return message + '.';
/**
* Determines whether `haystack` contains `needle`, using the same comparison
* logic as {@link MatchersUtil#equals}.
* @function
* @name MatchersUtil#contains
* @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 Array]') ||
(!!haystack && !haystack.indexOf))
{
for (var i = 0; i < haystack.length; i++) {
if (this.equals(haystack[i], needle, customTesters)) {
return true;
}
}
return false;
}
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 + '.';
};
function isAsymmetric(obj) {
return obj && j$.isA_('Function', obj.asymmetricMatch);
}
function asymmetricDiff(a, b, aStack, bStack, customTesters, diffBuilder) {
MatchersUtil.prototype.asymmetricDiff_ = function(a, b, aStack, bStack, customTesters, diffBuilder) {
if (j$.isFunction_(b.valuesForDiff_)) {
var values = b.valuesForDiff_(a);
eq(values.other, values.self, aStack, bStack, customTesters, diffBuilder);
this.eq_(values.other, values.self, aStack, bStack, customTesters, diffBuilder);
} else {
diffBuilder.record(a, b);
diffBuilder.recordMismatch();
}
}
};
function asymmetricMatch(a, b, aStack, bStack, customTesters, diffBuilder) {
MatchersUtil.prototype.asymmetricMatch_ = function(a, b, aStack, bStack, customTesters, diffBuilder) {
var asymmetricA = isAsymmetric(a),
asymmetricB = isAsymmetric(b),
shim = j$.asymmetricEqualityTesterArgCompatShim(this, customTesters),
result;
if (asymmetricA && asymmetricB) {
@@ -74,37 +102,56 @@ getJasmineRequireObj().matchersUtil = function(j$) {
}
if (asymmetricA) {
result = a.asymmetricMatch(b, customTesters);
result = a.asymmetricMatch(b, shim);
if (!result) {
// TODO: Do we want to build an asymmetric diff when the actual was an
// asymmeteric equality tester? Might be confusing.
diffBuilder.record(a, b);
diffBuilder.recordMismatch();
}
return result;
}
if (asymmetricB) {
result = b.asymmetricMatch(a, customTesters);
result = b.asymmetricMatch(a, shim);
if (!result) {
asymmetricDiff(a, b, aStack, bStack, customTesters, diffBuilder);
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
* @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, aStack, bStack, customTesters, diffBuilder);
var asymmetricResult = this.asymmetricMatch_(a, b, aStack, bStack, customTesters, diffBuilder);
if (!j$.util.isUndefined(asymmetricResult)) {
return asymmetricResult;
}
@@ -113,7 +160,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;
}
@@ -122,7 +169,7 @@ 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;
}
@@ -132,7 +179,7 @@ getJasmineRequireObj().matchersUtil = function(j$) {
if (a === b) {
result = a !== 0 || 1 / a == 1 / b;
if (!result) {
diffBuilder.record(a, b);
diffBuilder.recordMismatch();
}
return result;
}
@@ -140,13 +187,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) {
@@ -156,7 +203,7 @@ 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]':
@@ -164,7 +211,7 @@ getJasmineRequireObj().matchersUtil = function(j$) {
// other numeric values.
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]':
@@ -174,7 +221,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.
@@ -185,7 +232,7 @@ getJasmineRequireObj().matchersUtil = function(j$) {
a.ignoreCase == b.ignoreCase;
}
if (typeof a != 'object' || typeof b != 'object') {
diffBuilder.record(a, b);
diffBuilder.recordMismatch();
return false;
}
@@ -195,12 +242,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;
}
@@ -230,7 +277,7 @@ getJasmineRequireObj().matchersUtil = function(j$) {
diffBuilder.withPath('length', function() {
if (aLength !== bLength) {
diffBuilder.record(aLength, bLength);
diffBuilder.recordMismatch();
result = false;
}
});
@@ -238,10 +285,10 @@ getJasmineRequireObj().matchersUtil = function(j$) {
for (i = 0; i < aLength || i < bLength; i++) {
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;
}
});
}
@@ -250,7 +297,7 @@ 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;
}
@@ -282,22 +329,22 @@ getJasmineRequireObj().matchersUtil = function(j$) {
// 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())) {
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;
}
@@ -331,7 +378,7 @@ 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);
@@ -342,7 +389,7 @@ getJasmineRequireObj().matchersUtil = function(j$) {
}
if (!result) {
diffBuilder.record(a, b);
diffBuilder.recordMismatch();
return false;
}
} else {
@@ -355,7 +402,7 @@ getJasmineRequireObj().matchersUtil = function(j$) {
a instanceof aCtor && b instanceof bCtor &&
!(aCtor instanceof aCtor && bCtor instanceof bCtor)) {
diffBuilder.record(a, b, constructorsAreDifferentFormatter);
diffBuilder.recordMismatch(constructorsAreDifferentFormatter.bind(null, this.pp));
return false;
}
}
@@ -366,7 +413,7 @@ getJasmineRequireObj().matchersUtil = function(j$) {
// 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;
}
@@ -374,13 +421,13 @@ 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;
}
});
@@ -395,7 +442,7 @@ getJasmineRequireObj().matchersUtil = function(j$) {
bStack.pop();
return result;
}
};
function keys(obj, isArray) {
var allKeys = Object.keys ? Object.keys(obj) :
@@ -431,11 +478,11 @@ getJasmineRequireObj().matchersUtil = function(j$) {
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),
missingPropertiesMessage = formatKeyValuePairs(pp, missingProperties),
extraPropertiesMessage = formatKeyValuePairs(pp, extraProperties),
messages = [];
if (!path.depth()) {
@@ -453,7 +500,7 @@ getJasmineRequireObj().matchersUtil = function(j$) {
return messages.join('\n');
}
function constructorsAreDifferentFormatter(actual, expected, path) {
function constructorsAreDifferentFormatter(pp, actual, expected, path) {
if (!path.depth()) {
path = 'object';
}
@@ -461,21 +508,27 @@ getJasmineRequireObj().matchersUtil = function(j$) {
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) {
function actualArrayIsLongerFormatter(pp, actual, expected, path) {
return 'Unexpected ' +
path + (path.depth() ? ' = ' : '') +
j$.pp(actual) +
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

@@ -8,7 +8,7 @@ getJasmineRequireObj().toBe = function(j$) {
* @example
* expect(thing).toBe(realThing);
*/
function toBe(util) {
function toBe(matchersUtil) {
var tip = ' Tip: To check for deep equality, use .toEqual() instead of .toBe().';
return {
@@ -18,7 +18,7 @@ getJasmineRequireObj().toBe = function(j$) {
};
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

@@ -12,11 +12,11 @@ getJasmineRequireObj().toBeInstanceOf = function(j$) {
* expect(3).toBeInstanceOf(Number);
* expect(new Error()).toBeInstanceOf(Error);
*/
function toBeInstanceOf(util, customEqualityTesters) {
function toBeInstanceOf(matchersUtil) {
return {
compare: function(actual, expected) {
var actualType = actual && actual.constructor ? j$.fnNameFor(actual.constructor) : j$.pp(actual),
expectedType = expected ? j$.fnNameFor(expected) : j$.pp(expected),
var actualType = actual && actual.constructor ? j$.fnNameFor(actual.constructor) : matchersUtil.pp(actual),
expectedType = expected ? j$.fnNameFor(expected) : matchersUtil.pp(expected),
expectedMatcher,
pass;

View File

@@ -7,7 +7,7 @@ getJasmineRequireObj().toBeNaN = function(j$) {
* @example
* expect(thing).toBeNaN();
*/
function toBeNaN() {
function toBeNaN(matchersUtil) {
return {
compare: function(actual) {
var result = {
@@ -17,7 +17,7 @@ getJasmineRequireObj().toBeNaN = function(j$) {
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

@@ -7,7 +7,7 @@ getJasmineRequireObj().toBeNegativeInfinity = function(j$) {
* @example
* expect(thing).toBeNegativeInfinity();
*/
function toBeNegativeInfinity() {
function toBeNegativeInfinity(matchersUtil) {
return {
compare: function(actual) {
var result = {
@@ -17,7 +17,7 @@ getJasmineRequireObj().toBeNegativeInfinity = function(j$) {
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

@@ -7,7 +7,7 @@ getJasmineRequireObj().toBePositiveInfinity = function(j$) {
* @example
* expect(thing).toBePositiveInfinity();
*/
function toBePositiveInfinity() {
function toBePositiveInfinity(matchersUtil) {
return {
compare: function(actual) {
var result = {
@@ -17,7 +17,7 @@ getJasmineRequireObj().toBePositiveInfinity = function(j$) {
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

@@ -9,14 +9,12 @@ getJasmineRequireObj().toContain = function() {
* 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

@@ -8,17 +8,15 @@ getJasmineRequireObj().toEqual = function(j$) {
* @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

@@ -11,13 +11,13 @@ getJasmineRequireObj().toHaveBeenCalled = function(j$) {
* 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) {

View File

@@ -11,14 +11,14 @@ getJasmineRequireObj().toHaveBeenCalledBefore = function(j$) {
* @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 };

View File

@@ -11,11 +11,11 @@ getJasmineRequireObj().toHaveBeenCalledTimes = function(j$) {
* @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),

View File

@@ -11,7 +11,7 @@ getJasmineRequireObj().toHaveBeenCalledWith = function(j$) {
* @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),
@@ -20,40 +20,40 @@ 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:\n' +
' ' + j$.pp(expectedArgs) +
' ' + 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:\n' +
' ' + j$.pp(expectedArgs) +
' ' + matchersUtil.pp(expectedArgs) +
'\nbut it was.';
};
} else {
result.message = function() {
var prettyPrintedCalls = actual.calls.allArgs().map(function(argsForCall) {
return ' ' + j$.pp(argsForCall);
return ' ' + matchersUtil.pp(argsForCall);
});
var diffs = actual.calls.allArgs().map(function(argsForCall, callIx) {
var diffBuilder = new j$.DiffBuilder();
util.equals(argsForCall, expectedArgs, customEqualityTesters, diffBuilder);
matchersUtil.equals(argsForCall, expectedArgs, diffBuilder);
return 'Call ' + callIx + ':\n' +
diffBuilder.getMessage().replace(/^/mg, ' ');
});
return 'Expected spy ' + actual.and.identity + ' to have been called with:\n' +
' ' + j$.pp(expectedArgs) + '\n' + '' +
' ' + matchersUtil.pp(expectedArgs) + '\n' + '' +
'but actual calls were:\n' +
prettyPrintedCalls.join(',\n') + '.\n\n' +
diffs.join('\n');

View File

@@ -10,11 +10,11 @@ getJasmineRequireObj().toHaveClass = function(j$) {
* 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 {

View File

@@ -12,7 +12,7 @@ getJasmineRequireObj().toThrow = function(j$) {
* 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 },
@@ -37,16 +37,16 @@ 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

@@ -16,7 +16,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),
@@ -34,7 +34,7 @@ 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);
@@ -97,7 +97,7 @@ getJasmineRequireObj().toThrowError = function(j$) {
thrownMessage = '';
if (expected) {
thrownMessage = ' with message ' + j$.pp(thrown.message);
thrownMessage = ' with message ' + matchersUtil.pp(thrown.message);
}
return thrownName + thrownMessage;
@@ -107,9 +107,9 @@ 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);
}
}

View File

@@ -10,7 +10,7 @@ getJasmineRequireObj().toThrowMatching = function(j$) {
* @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;
@@ -40,14 +40,14 @@ getJasmineRequireObj().toThrowMatching = function(j$) {
}
}
};
}
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);
}
}
}

View File

@@ -51,13 +51,22 @@ var getJasmineRequireObj = (function(jasmineGlobal) {
j$.buildExpectationResult = jRequire.buildExpectationResult(j$);
j$.noopTimer = jRequire.noopTimer();
j$.JsApiReporter = jRequire.JsApiReporter(j$);
j$.matchersUtil = jRequire.matchersUtil(j$);
j$.asymmetricEqualityTesterArgCompatShim = jRequire.asymmetricEqualityTesterArgCompatShim(
j$
);
j$.makePrettyPrinter = jRequire.makePrettyPrinter(j$);
j$.pp = j$.makePrettyPrinter();
j$.MatchersUtil = jRequire.MatchersUtil(j$);
j$.matchersUtil = new j$.MatchersUtil({
customTesters: [],
pp: j$.pp
});
j$.ObjectContaining = jRequire.ObjectContaining(j$);
j$.ArrayContaining = jRequire.ArrayContaining(j$);
j$.ArrayWithExactContents = jRequire.ArrayWithExactContents(j$);
j$.MapContaining = jRequire.MapContaining(j$);
j$.SetContaining = jRequire.SetContaining(j$);
j$.pp = jRequire.pp(j$);
j$.QueueRunner = jRequire.QueueRunner(j$);
j$.ReportDispatcher = jRequire.ReportDispatcher(j$);
j$.Spec = jRequire.Spec(j$);
@@ -75,6 +84,7 @@ var getJasmineRequireObj = (function(jasmineGlobal) {
j$.DiffBuilder = jRequire.DiffBuilder(j$);
j$.NullDiffBuilder = jRequire.NullDiffBuilder(j$);
j$.ObjectPath = jRequire.ObjectPath(j$);
j$.MismatchTree = jRequire.MismatchTree(j$);
j$.GlobalErrors = jRequire.GlobalErrors(j$);
j$.Truthy = jRequire.Truthy(j$);

View File

@@ -319,6 +319,20 @@ getJasmineRequireObj().interface = function(jasmine, env) {
return env.addAsyncMatchers(matchers);
};
/**
* Add a custom object formatter for the current scope of specs.
*
* _Note:_ This is only callable from within a {@link beforeEach}, {@link it}, or {@link beforeAll}.
* @name jasmine.addCustomObjectFormatter
* @since 3.6.0
* @function
* @param {Function} formatter - A function which takes a value to format and returns a string if it knows how to format it, and `undefined` otherwise.
* @see custom_object_formatter
*/
jasmine.addCustomObjectFormatter = function(formatter) {
return env.addCustomObjectFormatter(formatter);
};
/**
* Get the currently booted mock {Clock} for this Jasmine environment.
* @name jasmine.clock