From 5ee03f02edb8f18a857673d7a9b96aeee685415d Mon Sep 17 00:00:00 2001 From: rmehlinger Date: Sat, 29 Apr 2017 17:58:14 -0700 Subject: [PATCH] Add ES6 map support to Jasmine --- lib/jasmine-core/jasmine.js | 27 ++++++++++++++++++-- spec/core/PrettyPrintSpec.js | 20 +++++++++++++++ spec/core/matchers/matchersUtilSpec.js | 34 ++++++++++++++++++++++++++ spec/core/matchers/toEqualSpec.js | 31 +++++++++++++++++++++++ spec/helpers/checkForMap.js | 22 +++++++++++++++++ spec/support/jasmine.json | 1 + spec/support/jasmine.yml | 1 + src/core/PrettyPrinter.js | 23 +++++++++++++++++ src/core/matchers/matchersUtil.js | 4 +-- 9 files changed, 159 insertions(+), 4 deletions(-) create mode 100644 spec/helpers/checkForMap.js diff --git a/lib/jasmine-core/jasmine.js b/lib/jasmine-core/jasmine.js index 8820a67e..d4d5d961 100644 --- a/lib/jasmine-core/jasmine.js +++ b/lib/jasmine-core/jasmine.js @@ -2482,12 +2482,12 @@ getJasmineRequireObj().matchersUtil = function(j$) { if (!result) { return false; } - } else if (className == '[object Set]') { + } else if (className == '[object Set]' || className == '[object Map]') { if (a.size != b.size) { diffBuilder.record(a, b); return false; } - var iterA = a.values(), iterB = b.values(); + var iterA = a.entries(), iterB = b.entries(); var valA, valB; do { valA = iterA.next(); @@ -3639,6 +3639,8 @@ getJasmineRequireObj().pp = function(j$) { this.emitScalar('Date(' + value + ')'); } else if (value.toString && value.toString() == '[object Set]') { this.emitSet(value); + } else if (value.toString && value.toString() == '[object Map]') { + this.emitMap(value); } else if (value.toString && typeof value === 'object' && !j$.isArray_(value) && hasCustomToString(value)) { this.emitScalar(value.toString()); } else if (j$.util.arrayContains(this.seen, value)) { @@ -3669,6 +3671,7 @@ getJasmineRequireObj().pp = function(j$) { PrettyPrinter.prototype.emitArray = j$.unimplementedMethod_; PrettyPrinter.prototype.emitSet = j$.unimplementedMethod_; + PrettyPrinter.prototype.emitMap = j$.unimplementedMethod_; PrettyPrinter.prototype.emitObject = j$.unimplementedMethod_; PrettyPrinter.prototype.emitScalar = j$.unimplementedMethod_; PrettyPrinter.prototype.emitString = j$.unimplementedMethod_; @@ -3745,6 +3748,26 @@ getJasmineRequireObj().pp = function(j$) { this.append(' )'); }; + StringPrettyPrinter.prototype.emitMap = function(set) { + if (this.ppNestLevel_ > j$.MAX_PRETTY_PRINT_DEPTH) { + this.append('Map'); + return; + } + this.append('Map( '); + var size = Math.min(set.size, j$.MAX_PRETTY_PRINT_ARRAY_LENGTH); + var iter = set.entries(); + for (var i = 0; i < size; i++) { + if (i > 0) { + this.append(', '); + } + this.format(iter.next().value); + } + if (set.size > size){ + this.append(', ...'); + } + this.append(' )'); + }; + StringPrettyPrinter.prototype.emitObject = function(obj) { var ctor = obj.constructor, constructorName; diff --git a/spec/core/PrettyPrintSpec.js b/spec/core/PrettyPrintSpec.js index 0656f902..980ae736 100644 --- a/spec/core/PrettyPrintSpec.js +++ b/spec/core/PrettyPrintSpec.js @@ -33,6 +33,26 @@ describe("jasmineUnderTest.pp", function () { }) }); + describe('stringify maps', function() { + it("should stringify maps properly", function() { + jasmine.getEnv().requireFunctioningMaps(); + expect(jasmineUnderTest.pp(new Map([[1, 2]]))).toEqual("Map( [ 1, 2 ] )"); + }); + + it("should truncate maps with more elments than jasmineUnderTest.MAX_PRETTY_PRINT_ARRAY_LENGTH", function() { + jasmine.getEnv().requireFunctioningMaps(); + var originalMaxSize = jasmineUnderTest.MAX_PRETTY_PRINT_ARRAY_LENGTH; + + try { + jasmineUnderTest.MAX_PRETTY_PRINT_ARRAY_LENGTH = 2; + expect(jasmineUnderTest.pp(new Map([["a", 1], ["b", 2], ["c", 3]]))).toEqual("Map( [ 'a', 1 ], [ 'b', 2 ], ... )"); + } finally { + jasmineUnderTest.MAX_PRETTY_PRINT_ARRAY_LENGTH = originalMaxSize; + } + }) + }); + + describe('stringify arrays', function() { it("should stringify arrays properly", function() { expect(jasmineUnderTest.pp([1, 2])).toEqual("[ 1, 2 ]"); diff --git a/spec/core/matchers/matchersUtilSpec.js b/spec/core/matchers/matchersUtilSpec.js index 1e1140e2..f30621a0 100644 --- a/spec/core/matchers/matchersUtilSpec.js +++ b/spec/core/matchers/matchersUtilSpec.js @@ -414,6 +414,40 @@ describe("matchersUtil", function() { expect(jasmineUnderTest.matchersUtil.equals(setA, setB)).toBe(false); }); + it("passes when comparing two empty maps", function() { + jasmine.getEnv().requireFunctioningMaps(); + expect(jasmineUnderTest.matchersUtil.equals(new Map(), new Map())).toBe(true); + }); + + it("passes when comparing identical maps", function() { + jasmine.getEnv().requireFunctioningMaps(); + var mapA = new Map([[6, 5]]); + var mapB = new Map(); + mapB.set(6, 5); + expect(jasmineUnderTest.matchersUtil.equals(mapA, mapB)).toBe(true); + }); + + it("fails for maps with different elements", function() { + jasmine.getEnv().requireFunctioningMaps(); + var mapA = new Map([[6, 3], [5, 1]]); + var mapB = new Map([[6, 4], [5, 1]]); + expect(jasmineUnderTest.matchersUtil.equals(mapA, mapB)).toBe(false); + }); + + it("fails for maps of different size", function() { + jasmine.getEnv().requireFunctioningMaps(); + var mapA = new Map([[6, 3]]); + var mapB = new Map([[6, 4], [5, 1]]); + expect(jasmineUnderTest.matchersUtil.equals(mapA, mapB)).toBe(false); + }); + + it("fails for maps with different insertion order", function() { + jasmine.getEnv().requireFunctioningMaps(); + var mapA = new Map([['a', 3], [6, 1]]); + var mapB = new Map([[6, 1], ['a', 3]]); + expect(jasmineUnderTest.matchersUtil.equals(mapA, mapB)).toBe(false); + }); + describe("when running in an environment with array polyfills", function() { // IE 8 doesn't support `definePropery` on non-DOM nodes if (jasmine.getEnv().ieVersion < 9) { return; } diff --git a/spec/core/matchers/toEqualSpec.js b/spec/core/matchers/toEqualSpec.js index 88ca9aec..0cf455cd 100644 --- a/spec/core/matchers/toEqualSpec.js +++ b/spec/core/matchers/toEqualSpec.js @@ -390,6 +390,37 @@ describe("toEqual", function() { expect(compareEquals(actual, expected).message).toEqual(message); }); + it("does not report deep mismatches within Maps", function() { + // TODO: implement deep comparison of Map elements + jasmine.getEnv().requireFunctioningMaps(); + + var actual = new Map([['a', 1]]), + expected = new Map([['a', 2]]), + message = "Expected Map( [ 'a', 1 ] ) to equal Map( [ 'a', 2 ] )."; + + expect(compareEquals(actual, expected).message).toEqual(message); + }); + + it("reports mismatches between Maps nested in objects", function() { + jasmine.getEnv().requireFunctioningMaps(); + + var actual = {Maps: [new Map([['a', 1]])]}, + expected = {Maps: [new Map([['a', 2], ['b', 1]])]}, + message = "Expected $.Maps[0] = Map( [ 'a', 1 ] ) to equal Map( [ 'a', 2 ], [ 'b', 1 ] )."; + + expect(compareEquals(actual, expected).message).toEqual(message); + }); + + it("reports mismatches between Maps of different lengths", function() { + jasmine.getEnv().requireFunctioningMaps(); + + var actual = new Map([['a', 1]]), + expected = new Map([['a', 2]]), + message = "Expected Map( [ 'a', 1 ] ) to equal Map( [ 'a', 2 ] )."; + + expect(compareEquals(actual, expected).message).toEqual(message); + }); + function isNotRunningInBrowser() { return typeof document === 'undefined' } diff --git a/spec/helpers/checkForMap.js b/spec/helpers/checkForMap.js new file mode 100644 index 00000000..b924e1a1 --- /dev/null +++ b/spec/helpers/checkForMap.js @@ -0,0 +1,22 @@ +(function(env) { + function hasFunctioningMaps() { + if (typeof Map === 'undefined') { return false; } + + try { + var s = new Map([['a', 4]]); + if (s.size !== 1) { return false; } + if (s.keys().next().value !== 'a') { return false; } + if (s.values().next().value !== 4) { return false; } + return true; + } catch(e) { + return false; + } + } + + env.requireFunctioningMaps = function() { + if (!hasFunctioningMaps()) { + env.pending("Browser has incomplete or missing support for Maps"); + } + }; + +})(jasmine.getEnv()); diff --git a/spec/support/jasmine.json b/spec/support/jasmine.json index 83b19dbb..2e28fae6 100644 --- a/spec/support/jasmine.json +++ b/spec/support/jasmine.json @@ -7,6 +7,7 @@ ], "helpers": [ "helpers/checkForSet.js", + "helpers/checkForMap.js", "helpers/nodeDefineJasmineUnderTest.js" ], "random": true diff --git a/spec/support/jasmine.yml b/spec/support/jasmine.yml index da959031..2fe96718 100644 --- a/spec/support/jasmine.yml +++ b/spec/support/jasmine.yml @@ -18,6 +18,7 @@ stylesheets: helpers: - 'helpers/BrowserFlags.js' - 'helpers/checkForSet.js' + - 'helpers/checkForMap.js' - 'helpers/defineJasmineUnderTest.js' spec_files: - '**/*[Ss]pec.js' diff --git a/src/core/PrettyPrinter.js b/src/core/PrettyPrinter.js index d945d0c7..0a6da994 100644 --- a/src/core/PrettyPrinter.js +++ b/src/core/PrettyPrinter.js @@ -38,6 +38,8 @@ getJasmineRequireObj().pp = function(j$) { this.emitScalar('Date(' + value + ')'); } else if (value.toString && value.toString() == '[object Set]') { this.emitSet(value); + } else if (value.toString && value.toString() == '[object Map]') { + this.emitMap(value); } else if (value.toString && typeof value === 'object' && !j$.isArray_(value) && hasCustomToString(value)) { this.emitScalar(value.toString()); } else if (j$.util.arrayContains(this.seen, value)) { @@ -68,6 +70,7 @@ getJasmineRequireObj().pp = function(j$) { PrettyPrinter.prototype.emitArray = j$.unimplementedMethod_; PrettyPrinter.prototype.emitSet = j$.unimplementedMethod_; + PrettyPrinter.prototype.emitMap = j$.unimplementedMethod_; PrettyPrinter.prototype.emitObject = j$.unimplementedMethod_; PrettyPrinter.prototype.emitScalar = j$.unimplementedMethod_; PrettyPrinter.prototype.emitString = j$.unimplementedMethod_; @@ -144,6 +147,26 @@ getJasmineRequireObj().pp = function(j$) { this.append(' )'); }; + StringPrettyPrinter.prototype.emitMap = function(map) { + if (this.ppNestLevel_ > j$.MAX_PRETTY_PRINT_DEPTH) { + this.append('Map'); + return; + } + this.append('Map( '); + var size = Math.min(map.size, j$.MAX_PRETTY_PRINT_ARRAY_LENGTH); + var iter = map.entries(); + for (var i = 0; i < size; i++) { + if (i > 0) { + this.append(', '); + } + this.format(iter.next().value); + } + if (map.size > size){ + this.append(', ...'); + } + this.append(' )'); + }; + StringPrettyPrinter.prototype.emitObject = function(obj) { var ctor = obj.constructor, constructorName; diff --git a/src/core/matchers/matchersUtil.js b/src/core/matchers/matchersUtil.js index 6516c33a..1ed2ee34 100644 --- a/src/core/matchers/matchersUtil.js +++ b/src/core/matchers/matchersUtil.js @@ -239,12 +239,12 @@ getJasmineRequireObj().matchersUtil = function(j$) { if (!result) { return false; } - } else if (className == '[object Set]') { + } else if (className == '[object Set]' || className == '[object Map]') { if (a.size != b.size) { diffBuilder.record(a, b); return false; } - var iterA = a.values(), iterB = b.values(); + var iterA = a.entries(), iterB = b.entries(); var valA, valB; do { valA = iterA.next();