From 8ad9abb19a7fe4e686a86b7896cd20c554a4830a Mon Sep 17 00:00:00 2001 From: Gregg Van Hove Date: Fri, 4 Aug 2017 12:07:09 -0700 Subject: [PATCH] Add arrayWithExactContents asymmetric matcher - Fixes #817 --- lib/jasmine-core/jasmine.js | 57 +++++++++++++++++-- .../ArrayWithExactContentsSpec.js | 47 +++++++++++++++ spec/core/integration/CustomMatchersSpec.js | 5 +- .../asymmetric_equality/ArrayContaining.js | 5 +- .../ArrayWithExactContents.js | 31 ++++++++++ src/core/base.js | 11 ++++ src/core/requireCore.js | 1 + 7 files changed, 149 insertions(+), 8 deletions(-) create mode 100644 spec/core/asymmetric_equality/ArrayWithExactContentsSpec.js create mode 100644 src/core/asymmetric_equality/ArrayWithExactContents.js diff --git a/lib/jasmine-core/jasmine.js b/lib/jasmine-core/jasmine.js index b0cbab5e..a2af4239 100644 --- a/lib/jasmine-core/jasmine.js +++ b/lib/jasmine-core/jasmine.js @@ -63,6 +63,7 @@ var getJasmineRequireObj = (function (jasmineGlobal) { j$.matchersUtil = jRequire.matchersUtil(j$); j$.ObjectContaining = jRequire.ObjectContaining(j$); j$.ArrayContaining = jRequire.ArrayContaining(j$); + j$.ArrayWithExactContents = jRequire.ArrayWithExactContents(j$); j$.pp = jRequire.pp(j$); j$.QueueRunner = jRequire.QueueRunner(j$); j$.ReportDispatcher = jRequire.ReportDispatcher(); @@ -280,6 +281,17 @@ getJasmineRequireObj().base = function(j$, jasmineGlobal) { return new j$.ArrayContaining(sample); }; + /** + * Get a matcher, usable in any {@link matchers|matcher} that uses Jasmine's equality (e.g. {@link matchers#toEqual|toEqual}, {@link matchers#toContain|toContain}, or {@link matchers#toHaveBeenCalledWith|toHaveBeenCalledWith}), + * that will succeed if the actual value is an `Array` that contains all of the elements in the sample in any order. + * @name jasmine.arrayWithExactContents + * @function + * @param {Array} sample + */ + j$.arrayWithExactContents = function(sample) { + return new j$.ArrayWithExactContents(sample); + }; + /** * Create a bare {@link Spy} object. This won't be installed anywhere and will not have any implementation behind it. * @name jasmine.createSpy @@ -1417,8 +1429,9 @@ getJasmineRequireObj().ArrayContaining = function(j$) { } 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 + '\'.'); } + if (!j$.isArray_(this.sample)) { + throw new Error('You must provide an array to arrayContaining, not ' + j$.pp(this.sample) + '.'); + } for (var i = 0; i < this.sample.length; i++) { var item = this.sample[i]; @@ -1437,6 +1450,38 @@ getJasmineRequireObj().ArrayContaining = function(j$) { return ArrayContaining; }; +getJasmineRequireObj().ArrayWithExactContents = function(j$) { + + function ArrayWithExactContents(sample) { + this.sample = sample; + } + + ArrayWithExactContents.prototype.asymmetricMatch = function(other, customTesters) { + if (!j$.isArray_(this.sample)) { + throw new Error('You must provide an array to arrayWithExactContents, not ' + j$.pp(this.sample) + '.'); + } + + if (this.sample.length !== other.length) { + return false; + } + + for (var i = 0; i < this.sample.length; i++) { + var item = this.sample[i]; + if (!j$.matchersUtil.contains(other, item, customTesters)) { + return false; + } + } + + return true; + }; + + ArrayWithExactContents.prototype.jasmineToString = function() { + return ''; + }; + + return ArrayWithExactContents; +}; + getJasmineRequireObj().ObjectContaining = function(j$) { function ObjectContaining(sample) { @@ -2391,13 +2436,17 @@ getJasmineRequireObj().matchersUtil = function(j$) { if (asymmetricA) { result = a.asymmetricMatch(b, customTesters); - diffBuilder.record(a, b); + if (!result) { + diffBuilder.record(a, b); + } return result; } if (asymmetricB) { result = b.asymmetricMatch(a, customTesters); - diffBuilder.record(a, b); + if (!result) { + diffBuilder.record(a, b); + } return result; } } diff --git a/spec/core/asymmetric_equality/ArrayWithExactContentsSpec.js b/spec/core/asymmetric_equality/ArrayWithExactContentsSpec.js new file mode 100644 index 00000000..1cae0a0b --- /dev/null +++ b/spec/core/asymmetric_equality/ArrayWithExactContentsSpec.js @@ -0,0 +1,47 @@ +describe("ArrayWithExactContents", function() { + it("matches an array with the same items in a different order", function() { + var matcher = new jasmineUnderTest.ArrayWithExactContents(['a', 2, /a/]); + + expect(matcher.asymmetricMatch([2, 'a', /a/])).toBe(true); + }); + + it("does not work when not passed an array", function() { + var matcher = new jasmineUnderTest.ArrayWithExactContents("foo"); + + expect(function() { + matcher.asymmetricMatch([]); + }).toThrowError(/not 'foo'/); + }); + + it("does not match when an item is missing", function() { + var matcher = new jasmineUnderTest.ArrayWithExactContents(['a', 2, /a/]); + + expect(matcher.asymmetricMatch(['a', 2])).toBe(false); + expect(matcher.asymmetricMatch(['a', 2, undefined])).toBe(false); + }); + + it("does not match when there is an extra item", function() { + var matcher = new jasmineUnderTest.ArrayWithExactContents(['a']); + + expect(matcher.asymmetricMatch(['a', 2])).toBe(false); + }); + + it("jasmineToStrings itself", function() { + var matcher = new jasmineUnderTest.ArrayWithExactContents([]); + + expect(matcher.jasmineToString()).toMatch("'; + }; + + return ArrayWithExactContents; +}; diff --git a/src/core/base.js b/src/core/base.js index 98ff3e91..bfd9958f 100644 --- a/src/core/base.js +++ b/src/core/base.js @@ -152,6 +152,17 @@ getJasmineRequireObj().base = function(j$, jasmineGlobal) { return new j$.ArrayContaining(sample); }; + /** + * Get a matcher, usable in any {@link matchers|matcher} that uses Jasmine's equality (e.g. {@link matchers#toEqual|toEqual}, {@link matchers#toContain|toContain}, or {@link matchers#toHaveBeenCalledWith|toHaveBeenCalledWith}), + * that will succeed if the actual value is an `Array` that contains all of the elements in the sample in any order. + * @name jasmine.arrayWithExactContents + * @function + * @param {Array} sample + */ + j$.arrayWithExactContents = function(sample) { + return new j$.ArrayWithExactContents(sample); + }; + /** * Create a bare {@link Spy} object. This won't be installed anywhere and will not have any implementation behind it. * @name jasmine.createSpy diff --git a/src/core/requireCore.js b/src/core/requireCore.js index b72caac7..c89d3ae1 100644 --- a/src/core/requireCore.js +++ b/src/core/requireCore.js @@ -41,6 +41,7 @@ var getJasmineRequireObj = (function (jasmineGlobal) { j$.matchersUtil = jRequire.matchersUtil(j$); j$.ObjectContaining = jRequire.ObjectContaining(j$); j$.ArrayContaining = jRequire.ArrayContaining(j$); + j$.ArrayWithExactContents = jRequire.ArrayWithExactContents(j$); j$.pp = jRequire.pp(j$); j$.QueueRunner = jRequire.QueueRunner(j$); j$.ReportDispatcher = jRequire.ReportDispatcher();