From 6d3953356db6a51b3cb600bc67861ec6559c856e Mon Sep 17 00:00:00 2001 From: Zaven Muradyan Date: Mon, 23 Oct 2017 23:01:50 -0700 Subject: [PATCH] Add special handling of asymmetric matcher objects as keys in Maps. The previous Map equality code was assuming that the set of keys would be identical between the two Maps. This change adds insertion-order tracking for each key with its corresponding key. If one of the two keys is an asymmetric equality obj, the keys are eq()'d, and if it succeeds, the corresponding values are compared. Otherwise, the "main" key is looked up directly in the other object; this is to prevent similar-looking obj keys with different obj identities from comparing equal. Fixes #1432. --- spec/core/asymmetric_equality/AnythingSpec.js | 16 +++++++++++++ spec/core/matchers/toEqualSpec.js | 8 +++++++ src/core/matchers/matchersUtil.js | 24 ++++++++++++++++--- 3 files changed, 45 insertions(+), 3 deletions(-) diff --git a/spec/core/asymmetric_equality/AnythingSpec.js b/spec/core/asymmetric_equality/AnythingSpec.js index 8ee1f4e5..f248226a 100644 --- a/spec/core/asymmetric_equality/AnythingSpec.js +++ b/spec/core/asymmetric_equality/AnythingSpec.js @@ -23,6 +23,22 @@ describe("Anything", function() { expect(anything.asymmetricMatch([1,2,3])).toBe(true); }); + it("matches a Map", function() { + jasmine.getEnv().requireFunctioningMaps(); + + var anything = new jasmineUnderTest.Anything(); + + expect(anything.asymmetricMatch(new Map())).toBe(true); + }); + + it("matches a Set", function() { + jasmine.getEnv().requireFunctioningSets(); + + var anything = new jasmineUnderTest.Anything(); + + expect(anything.asymmetricMatch(new Set())).toBe(true); + }); + it("doesn't match undefined", function() { var anything = new jasmineUnderTest.Anything(); diff --git a/spec/core/matchers/toEqualSpec.js b/spec/core/matchers/toEqualSpec.js index aaed7759..d1605a4f 100644 --- a/spec/core/matchers/toEqualSpec.js +++ b/spec/core/matchers/toEqualSpec.js @@ -504,6 +504,14 @@ describe("toEqual", function() { expect(compareEquals(actual, expected).message).toEqual(message); }); + it("does not report mismatches when comparing Map key to jasmine.anything()", function() { + jasmine.getEnv().requireFunctioningMaps(); + + var actual = new Map([['a', 1]]), + expected = new Map([[jasmineUnderTest.anything(), 1]]); + expect(compareEquals(actual, expected).pass).toBe(true); + }); + function isNotRunningInBrowser() { return typeof document === 'undefined' } diff --git a/src/core/matchers/matchersUtil.js b/src/core/matchers/matchersUtil.js index a9801910..71fc0047 100644 --- a/src/core/matchers/matchersUtil.js +++ b/src/core/matchers/matchersUtil.js @@ -253,16 +253,34 @@ getJasmineRequireObj().matchersUtil = function(j$) { return false; } - // For both sets of keys, check they map to equal values in both maps + // For both sets of keys, check they map to equal values in both maps. + // Keep track of corresponding keys (in insertion order) in order to handle asymmetric obj keys. var mapKeys = [a.keys(), b.keys()]; - var mapIter, mapKeyIt, mapKey; + var cmpKeys = [b.keys(), a.keys()]; + var mapIter, mapKeyIt, mapKey, mapValueA, mapValueB; + var cmpIter, cmpKeyIt, cmpKey; for (i = 0; result && i < mapKeys.length; i++) { mapIter = mapKeys[i]; + cmpIter = cmpKeys[i]; mapKeyIt = mapIter.next(); + cmpKeyIt = cmpIter.next(); while (result && !mapKeyIt.done) { mapKey = mapKeyIt.value; - result = eq(a.get(mapKey), b.get(mapKey), aStack, bStack, customTesters, j$.NullDiffBuilder()); + cmpKey = cmpKeyIt.value; + mapValueA = a.get(mapKey); + + // 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())) { + mapValueB = b.get(cmpKey); + } else { + mapValueB = b.get(mapKey); + } + result = eq(mapValueA, mapValueB, aStack, bStack, customTesters, j$.NullDiffBuilder()); mapKeyIt = mapIter.next(); + cmpKeyIt = cmpIter.next(); } }