Pass custom testers to asymmetric testers

This allows custom equality testers to affect asymmetric matches.
This avoid suprises when combining addCustomEqualityTester with
objectContaining or arrayContaining.

Closes #1138
This commit is contained in:
Joey Parrish
2016-06-23 14:33:39 -07:00
parent f6da084642
commit de7b7c029e
8 changed files with 75 additions and 16 deletions

View File

@@ -2642,13 +2642,13 @@ getJasmineRequireObj().ArrayContaining = function(j$) {
this.sample = sample;
}
ArrayContaining.prototype.asymmetricMatch = function(other) {
ArrayContaining.prototype.asymmetricMatch = function(other, customTesters) {
var className = Object.prototype.toString.call(this.sample);
if (className !== '[object Array]') { throw new Error('You must provide an array to arrayContaining, not \'' + this.sample + '\'.'); }
for (var i = 0; i < this.sample.length; i++) {
var item = this.sample[i];
if (!j$.matchersUtil.contains(other, item)) {
if (!j$.matchersUtil.contains(other, item, customTesters)) {
return false;
}
}
@@ -2693,12 +2693,12 @@ getJasmineRequireObj().ObjectContaining = function(j$) {
return hasProperty(getPrototype(obj), property);
}
ObjectContaining.prototype.asymmetricMatch = function(other) {
ObjectContaining.prototype.asymmetricMatch = function(other, customTesters) {
if (typeof(this.sample) !== 'object') { throw new Error('You must provide an object to objectContaining, not \''+this.sample+'\'.'); }
for (var property in this.sample) {
if (!hasProperty(other, property) ||
!j$.matchersUtil.equals(this.sample[property], other[property])) {
!j$.matchersUtil.equals(this.sample[property], other[property], customTesters)) {
return false;
}
}
@@ -2813,7 +2813,7 @@ getJasmineRequireObj().matchersUtil = function(j$) {
return obj && j$.isA_('Function', obj.asymmetricMatch);
}
function asymmetricMatch(a, b) {
function asymmetricMatch(a, b, customTesters) {
var asymmetricA = isAsymmetric(a),
asymmetricB = isAsymmetric(b);
@@ -2822,11 +2822,11 @@ getJasmineRequireObj().matchersUtil = function(j$) {
}
if (asymmetricA) {
return a.asymmetricMatch(b);
return a.asymmetricMatch(b, customTesters);
}
if (asymmetricB) {
return b.asymmetricMatch(a);
return b.asymmetricMatch(a, customTesters);
}
}
@@ -2835,7 +2835,7 @@ getJasmineRequireObj().matchersUtil = function(j$) {
function eq(a, b, aStack, bStack, customTesters) {
var result = true;
var asymmetricResult = asymmetricMatch(a, b);
var asymmetricResult = asymmetricMatch(a, b, customTesters);
if (!j$.util.isUndefined(asymmetricResult)) {
return asymmetricResult;
}

View File

@@ -36,4 +36,17 @@ describe("ArrayContaining", function() {
expect(containing.jasmineToString()).toMatch("<jasmine.arrayContaining");
});
it("uses custom equality testers", function() {
var tester = function(a, b) {
// All "foo*" strings match each other.
if (typeof a == "string" && typeof b == "string" &&
a.substr(0, 3) == "foo" && b.substr(0, 3) == "foo") {
return true;
}
};
var containing = new jasmineUnderTest.ArrayContaining(["fooVal"]);
expect(containing.asymmetricMatch(["fooBar"], [tester])).toBe(true);
});
});

View File

@@ -86,4 +86,17 @@ describe("ObjectContaining", function() {
expect(containing.asymmetricMatch(obj)).toBe(true);
});
it("uses custom equality testers", function() {
var tester = function(a, b) {
// All "foo*" strings match each other.
if (typeof a == "string" && typeof b == "string" &&
a.substr(0, 3) == "foo" && b.substr(0, 3) == "foo") {
return true;
}
};
var containing = new jasmineUnderTest.ObjectContaining({foo: "fooVal"});
expect(containing.asymmetricMatch({foo: "fooBar"}, [tester])).toBe(true);
});
});

View File

@@ -56,6 +56,29 @@ describe("Custom Matchers (Integration)", function() {
env.execute();
});
it("passes the spec if the custom equality matcher passes for types nested inside asymmetric equality testers", function(done) {
env.it("spec using custom equality matcher", function() {
var customEqualityFn = function(a, b) {
// All "foo*" strings match each other.
if (typeof a == "string" && typeof b == "string" &&
a.substr(0, 3) == "foo" && b.substr(0, 3) == "foo") {
return true;
}
};
env.addCustomEqualityTester(customEqualityFn);
env.expect({foo: 'fooValue'}).toEqual(jasmine.objectContaining({foo: 'fooBar'}));
env.expect(['fooValue']).toEqual(jasmine.arrayContaining(['fooBar']));
});
var specExpectations = function(result) {
expect(result.status).toEqual('passed');
};
env.addReporter({ specDone: specExpectations, jasmineDone: done });
env.execute();
});
it("uses the negative compare function for a negative comparison, if provided", function(done) {
env.it("spec with custom negative comparison matcher", function() {
env.addMatchers({

View File

@@ -284,6 +284,16 @@ describe("matchersUtil", function() {
expect(jasmineUnderTest.matchersUtil.equals(true, asymmetricTester, [symmetricTester])).toBe(true);
});
it("passes custom equality matchers to asymmetric equality testers", function() {
var tester = function(a, b) {};
var asymmetricTester = { asymmetricMatch: jasmine.createSpy('asymmetricMatch') };
asymmetricTester.asymmetricMatch.and.returnValue(true);
var other = {};
expect(jasmineUnderTest.matchersUtil.equals(asymmetricTester, other, [tester])).toBe(true);
expect(asymmetricTester.asymmetricMatch).toHaveBeenCalledWith(other, [tester]);
});
it("passes when an Any is compared to an Any that checks for the same type", function() {
var any1 = new jasmineUnderTest.Any(Function),
any2 = new jasmineUnderTest.Any(Function);

View File

@@ -3,13 +3,13 @@ getJasmineRequireObj().ArrayContaining = function(j$) {
this.sample = sample;
}
ArrayContaining.prototype.asymmetricMatch = function(other) {
ArrayContaining.prototype.asymmetricMatch = function(other, customTesters) {
var className = Object.prototype.toString.call(this.sample);
if (className !== '[object Array]') { throw new Error('You must provide an array to arrayContaining, not \'' + this.sample + '\'.'); }
for (var i = 0; i < this.sample.length; i++) {
var item = this.sample[i];
if (!j$.matchersUtil.contains(other, item)) {
if (!j$.matchersUtil.contains(other, item, customTesters)) {
return false;
}
}

View File

@@ -28,12 +28,12 @@ getJasmineRequireObj().ObjectContaining = function(j$) {
return hasProperty(getPrototype(obj), property);
}
ObjectContaining.prototype.asymmetricMatch = function(other) {
ObjectContaining.prototype.asymmetricMatch = function(other, customTesters) {
if (typeof(this.sample) !== 'object') { throw new Error('You must provide an object to objectContaining, not \''+this.sample+'\'.'); }
for (var property in this.sample) {
if (!hasProperty(other, property) ||
!j$.matchersUtil.equals(this.sample[property], other[property])) {
!j$.matchersUtil.equals(this.sample[property], other[property], customTesters)) {
return false;
}
}

View File

@@ -55,7 +55,7 @@ getJasmineRequireObj().matchersUtil = function(j$) {
return obj && j$.isA_('Function', obj.asymmetricMatch);
}
function asymmetricMatch(a, b) {
function asymmetricMatch(a, b, customTesters) {
var asymmetricA = isAsymmetric(a),
asymmetricB = isAsymmetric(b);
@@ -64,11 +64,11 @@ getJasmineRequireObj().matchersUtil = function(j$) {
}
if (asymmetricA) {
return a.asymmetricMatch(b);
return a.asymmetricMatch(b, customTesters);
}
if (asymmetricB) {
return b.asymmetricMatch(a);
return b.asymmetricMatch(a, customTesters);
}
}
@@ -77,7 +77,7 @@ getJasmineRequireObj().matchersUtil = function(j$) {
function eq(a, b, aStack, bStack, customTesters) {
var result = true;
var asymmetricResult = asymmetricMatch(a, b);
var asymmetricResult = asymmetricMatch(a, b, customTesters);
if (!j$.util.isUndefined(asymmetricResult)) {
return asymmetricResult;
}