describe('matchersUtil', function() { it('exposes the injected pretty-printer as .pp', function() { const pp = function() {}; const matchersUtil = new privateUnderTest.MatchersUtil({ pp: pp }); expect(matchersUtil.pp).toBe(pp); }); describe('equals', function() { it('passes for literals that are triple-equal', function() { const matchersUtil = new privateUnderTest.MatchersUtil(); expect(matchersUtil.equals(null, null)).toBe(true); expect(matchersUtil.equals(void 0, void 0)).toBe(true); }); it('fails for things that are not equivalent', function() { const matchersUtil = new privateUnderTest.MatchersUtil(); expect(matchersUtil.equals({ a: 'foo' }, 1)).toBe(false); }); it('passes for Strings that are equivalent', function() { const matchersUtil = new privateUnderTest.MatchersUtil(); expect(matchersUtil.equals('foo', 'foo')).toBe(true); }); it('fails for Strings that are not equivalent', function() { const matchersUtil = new privateUnderTest.MatchersUtil(); expect(matchersUtil.equals('foo', 'bar')).toBe(false); }); it('passes for Numbers that are equivalent', function() { const matchersUtil = new privateUnderTest.MatchersUtil(); expect(matchersUtil.equals(123, 123)).toBe(true); }); it('fails for Numbers that are not equivalent', function() { const matchersUtil = new privateUnderTest.MatchersUtil(); expect(matchersUtil.equals(123, 456)).toBe(false); }); it('fails for a Number and a String that have equivalent values', function() { const matchersUtil = new privateUnderTest.MatchersUtil(); expect(matchersUtil.equals(123, '123')).toBe(false); }); it('passes for Dates that are equivalent', function() { const matchersUtil = new privateUnderTest.MatchersUtil(); expect( matchersUtil.equals(new Date('Jan 1, 1970'), new Date('Jan 1, 1970')) ).toBe(true); }); it('fails for Dates that are not equivalent', function() { const matchersUtil = new privateUnderTest.MatchersUtil(); expect( matchersUtil.equals(new Date('Jan 1, 1970'), new Date('Feb 3, 1991')) ).toBe(false); }); it('passes for Booleans that are equivalent', function() { const matchersUtil = new privateUnderTest.MatchersUtil(); expect(matchersUtil.equals(true, true)).toBe(true); }); it('fails for Booleans that are not equivalent', function() { const matchersUtil = new privateUnderTest.MatchersUtil(); expect(matchersUtil.equals(true, false)).toBe(false); }); it('passes for RegExps that are equivalent', function() { const matchersUtil = new privateUnderTest.MatchersUtil(); expect(matchersUtil.equals(/foo/, /foo/)).toBe(true); }); it('fails for RegExps that are not equivalent', function() { const matchersUtil = new privateUnderTest.MatchersUtil(); expect(matchersUtil.equals(/foo/, /bar/)).toBe(false); expect( matchersUtil.equals(new RegExp('foo', 'i'), new RegExp('foo')) ).toBe(false); }); it('passes for Arrays that are equivalent', function() { const matchersUtil = new privateUnderTest.MatchersUtil(); expect(matchersUtil.equals([1, 2], [1, 2])).toBe(true); }); it('passes for Arrays that are equivalent, with elements added by changing length', function() { const foo = []; const matchersUtil = new privateUnderTest.MatchersUtil(); foo.length = 1; expect(matchersUtil.equals(foo, [undefined])).toBe(true); }); it('fails for Arrays that have different lengths', function() { const matchersUtil = new privateUnderTest.MatchersUtil(); expect(matchersUtil.equals([1, 2], [1, 2, 3])).toBe(false); }); it('fails for Arrays that have different elements', function() { const matchersUtil = new privateUnderTest.MatchersUtil(); expect(matchersUtil.equals([1, 2, 3], [1, 5, 3])).toBe(false); }); it('fails for Arrays whose contents are equivalent, but have differing properties', function() { const one = [1, 2, 3]; const two = [1, 2, 3]; const matchersUtil = new privateUnderTest.MatchersUtil(); one.foo = 'bar'; two.foo = 'baz'; expect(matchersUtil.equals(one, two)).toBe(false); }); it('passes for Arrays with equivalent contents and properties', function() { const one = [1, 2, 3]; const two = [1, 2, 3]; const matchersUtil = new privateUnderTest.MatchersUtil(); one.foo = 'bar'; two.foo = 'bar'; expect(matchersUtil.equals(one, two)).toBe(true); }); it('handles symbol keys in Arrays', function() { const matchersUtil = new privateUnderTest.MatchersUtil(); const sym = Symbol('foo'); const arr1 = []; let arr2 = []; arr1[sym] = 'bar'; arr2[sym] = 'bar'; expect(matchersUtil.equals(arr1, arr2)).toBe(true); arr2[sym] = 'baz'; expect(matchersUtil.equals(arr1, arr2)).toBe(false); arr2 = []; arr2[Symbol('foo')] = 'bar'; expect(matchersUtil.equals(arr1, arr2)).toBe(false); arr2 = []; arr2['foo'] = 'bar'; expect(matchersUtil.equals(arr1, arr2)).toBe(false); }); it('passes for Errors that are the same type and have the same message', function() { const matchersUtil = new privateUnderTest.MatchersUtil(); expect(matchersUtil.equals(new Error('foo'), new Error('foo'))).toBe( true ); }); it('fails for Errors that are the same type and have different messages', function() { const matchersUtil = new privateUnderTest.MatchersUtil(); expect(matchersUtil.equals(new Error('foo'), new Error('bar'))).toBe( false ); }); it('fails for objects with different constructors', function() { const matchersUtil = new privateUnderTest.MatchersUtil(); function One() {} function Two() {} expect(matchersUtil.equals(new One(), new Two())).toBe(false); }); it('passes for Objects that are equivalent (simple case)', function() { const matchersUtil = new privateUnderTest.MatchersUtil(); expect(matchersUtil.equals({ a: 'foo' }, { a: 'foo' })).toBe(true); }); it('fails for Objects that are not equivalent (simple case)', function() { const matchersUtil = new privateUnderTest.MatchersUtil(); expect(matchersUtil.equals({ a: 'foo' }, { a: 'bar' })).toBe(false); }); it('passes for Objects that are equivalent (deep case)', function() { const matchersUtil = new privateUnderTest.MatchersUtil(); expect( matchersUtil.equals( { a: 'foo', b: { c: 'bar' } }, { a: 'foo', b: { c: 'bar' } } ) ).toBe(true); }); it('fails for Objects that are not equivalent (deep case)', function() { const matchersUtil = new privateUnderTest.MatchersUtil(); expect( matchersUtil.equals( { a: 'foo', b: { c: 'baz' } }, { a: 'foo', b: { c: 'bar' } } ) ).toBe(false); }); it('passes for Objects that are equivalent (with cycles)', function() { const actual = { a: 'foo' }; const expected = { a: 'foo' }; const matchersUtil = new privateUnderTest.MatchersUtil(); actual.b = actual; expected.b = actual; expect(matchersUtil.equals(actual, expected)).toBe(true); }); it('fails for Objects that are not equivalent (with cycles)', function() { const actual = { a: 'foo' }; const expected = { a: 'bar' }; const matchersUtil = new privateUnderTest.MatchersUtil(); actual.b = actual; expected.b = actual; expect(matchersUtil.equals(actual, expected)).toBe(false); }); it('fails for Objects that have the same number of keys, but different keys/values', function() { const expected = { a: undefined }; const actual = { b: 1 }; const matchersUtil = new privateUnderTest.MatchersUtil(); expect(matchersUtil.equals(actual, expected)).toBe(false); }); it('fails when comparing an empty object to an empty array (issue #114)', function() { const emptyObject = {}; const emptyArray = []; const matchersUtil = new privateUnderTest.MatchersUtil(); expect(matchersUtil.equals(emptyObject, emptyArray)).toBe(false); expect(matchersUtil.equals(emptyArray, emptyObject)).toBe(false); }); it('passes for equivalent frozen objects (GitHub issue #266)', function() { const a = { foo: 1 }; const b = { foo: 1 }; const matchersUtil = new privateUnderTest.MatchersUtil(); Object.freeze(a); Object.freeze(b); expect(matchersUtil.equals(a, b)).toBe(true); }); it('passes for equivalent Promises (GitHub issue #1314)', function() { const p1 = new Promise(function() {}); const p2 = new Promise(function() {}); const matchersUtil = new privateUnderTest.MatchersUtil(); expect(matchersUtil.equals(p1, p1)).toBe(true); expect(matchersUtil.equals(p1, p2)).toBe(false); }); describe('when running in a browser', function() { beforeEach(function() { if (typeof document === 'undefined') { pending('This test only runs in browsers'); } }); it('passes for equivalent DOM nodes', function() { const a = document.createElement('div'); const matchersUtil = new privateUnderTest.MatchersUtil(); a.setAttribute('test-attr', 'attr-value'); a.appendChild(document.createTextNode('test')); const b = document.createElement('div'); b.setAttribute('test-attr', 'attr-value'); b.appendChild(document.createTextNode('test')); expect(matchersUtil.equals(a, b)).toBe(true); }); it('passes for equivalent objects from different frames', function() { const matchersUtil = new privateUnderTest.MatchersUtil(); const iframe = document.createElement('iframe'); document.body.appendChild(iframe); iframe.contentWindow.eval('window.testObject = {}'); expect(matchersUtil.equals({}, iframe.contentWindow.testObject)).toBe( true ); document.body.removeChild(iframe); }); it('fails for DOM nodes with different attributes or child nodes', function() { const matchersUtil = new privateUnderTest.MatchersUtil(); const a = document.createElement('div'); a.setAttribute('test-attr', 'attr-value'); a.appendChild(document.createTextNode('test')); const b = document.createElement('div'); b.setAttribute('test-attr', 'attr-value2'); b.appendChild(document.createTextNode('test')); expect(matchersUtil.equals(a, b)).toBe(false); b.setAttribute('test-attr', 'attr-value'); expect(matchersUtil.equals(a, b)).toBe(true); b.appendChild(document.createTextNode('2')); expect(matchersUtil.equals(a, b)).toBe(false); a.appendChild(document.createTextNode('2')); expect(matchersUtil.equals(a, b)).toBe(true); }); }); describe('when running in Node', function() { beforeEach(function() { if (typeof require !== 'function') { pending('This test only runs in Node'); } }); it('passes for equivalent objects from different vm contexts', function() { const matchersUtil = new privateUnderTest.MatchersUtil(); const vm = require('vm'); const sandbox = { obj: null }; vm.runInNewContext('obj = {a: 1, b: 2}', sandbox); expect(matchersUtil.equals(sandbox.obj, { a: 1, b: 2 })).toBe(true); }); it('passes for equivalent arrays from different vm contexts', function() { const matchersUtil = new privateUnderTest.MatchersUtil(); const vm = require('vm'); const sandbox = { arr: null }; vm.runInNewContext('arr = [1, 2]', sandbox); expect(matchersUtil.equals(sandbox.arr, [1, 2])).toBe(true); }); }); it('passes when Any is used', function() { const number = 3; const anyNumber = new privateUnderTest.Any(Number); const matchersUtil = new privateUnderTest.MatchersUtil(); expect(matchersUtil.equals(number, anyNumber)).toBe(true); expect(matchersUtil.equals(anyNumber, number)).toBe(true); }); it('fails when Any is compared to something unexpected', function() { const number = 3; const anyString = new privateUnderTest.Any(String); const matchersUtil = new privateUnderTest.MatchersUtil(); expect(matchersUtil.equals(number, anyString)).toBe(false); expect(matchersUtil.equals(anyString, number)).toBe(false); }); it('passes when ObjectContaining is used', function() { const obj = { foo: 3, bar: 7 }; const containing = new privateUnderTest.ObjectContaining({ foo: 3 }); const matchersUtil = new privateUnderTest.MatchersUtil(); expect(matchersUtil.equals(obj, containing)).toBe(true); expect(matchersUtil.equals(containing, obj)).toBe(true); }); it('passes when MapContaining is used', function() { const matchersUtil = new privateUnderTest.MatchersUtil(); const obj = new Map(); obj.set(1, 2); obj.set('foo', 'bar'); const containing = new privateUnderTest.MapContaining(new Map()); containing.sample.set('foo', 'bar'); expect(matchersUtil.equals(obj, containing)).toBe(true); expect(matchersUtil.equals(containing, obj)).toBe(true); }); it('passes when SetContaining is used', function() { const matchersUtil = new privateUnderTest.MatchersUtil(); const obj = new Set(); obj.add(1); obj.add('foo'); const containing = new privateUnderTest.SetContaining(new Set()); containing.sample.add(1); expect(matchersUtil.equals(obj, containing)).toBe(true); expect(matchersUtil.equals(containing, obj)).toBe(true); }); it('passes when an asymmetric equality tester returns true', function() { const tester = { asymmetricMatch: function() { return true; } }; const matchersUtil = new privateUnderTest.MatchersUtil(); expect(matchersUtil.equals(false, tester)).toBe(true); expect(matchersUtil.equals(tester, false)).toBe(true); }); it('fails when an asymmetric equality tester returns false', function() { const tester = { asymmetricMatch: function() { return false; } }; const matchersUtil = new privateUnderTest.MatchersUtil(); expect(matchersUtil.equals(true, tester)).toBe(false); expect(matchersUtil.equals(tester, true)).toBe(false); }); it('passes when ArrayContaining is used', function() { const arr = ['foo', 'bar']; const matchersUtil = new privateUnderTest.MatchersUtil(); expect( matchersUtil.equals(arr, new privateUnderTest.ArrayContaining(['bar'])) ).toBe(true); }); it('passes when a custom equality matcher returns true', function() { const tester = function() { return true; }; const matchersUtil = new privateUnderTest.MatchersUtil({ customTesters: [tester], pp: function() {} }); expect(matchersUtil.equals(1, 2)).toBe(true); }); it('passes for two empty Objects', function() { const matchersUtil = new privateUnderTest.MatchersUtil(); expect(matchersUtil.equals({}, {})).toBe(true); }); describe("when a custom equality matcher returns 'undefined'", function() { const tester = function() { return jasmine.undefined; }; it('passes for two empty Objects', function() { const matchersUtil = new privateUnderTest.MatchersUtil({ customTesters: [tester], pp: function() {} }); expect(matchersUtil.equals({}, {})).toBe(true); }); }); it('fails for equivalents when a custom equality matcher returns false', function() { const tester = function() { return false; }; const matchersUtil = new privateUnderTest.MatchersUtil({ customTesters: [tester], pp: function() {} }); expect(matchersUtil.equals(1, 1)).toBe(false); }); it('passes for an asymmetric equality tester that returns true when a custom equality tester return false', function() { const asymmetricTester = { asymmetricMatch: function() { return true; } }; const symmetricTester = function() { return false; }; const matchersUtil = new privateUnderTest.MatchersUtil({ customTesters: [symmetricTester()], pp: function() {} }); expect(matchersUtil.equals(asymmetricTester, true)).toBe(true); expect(matchersUtil.equals(true, asymmetricTester)).toBe(true); }); it('passes when an Any is compared to an Any that checks for the same type', function() { const any1 = new privateUnderTest.Any(Function); const any2 = new privateUnderTest.Any(Function); const matchersUtil = new privateUnderTest.MatchersUtil(); expect(matchersUtil.equals(any1, any2)).toBe(true); }); it('passes for null prototype objects with same properties', function() { const objA = Object.create(null); const objB = Object.create(null); const matchersUtil = new privateUnderTest.MatchersUtil(); objA.name = 'test'; objB.name = 'test'; expect(matchersUtil.equals(objA, objB)).toBe(true); }); it('fails for null prototype objects with different properties', function() { const objA = Object.create(null); const objB = Object.create(null); const matchersUtil = new privateUnderTest.MatchersUtil(); objA.name = 'test'; objB.test = 'name'; expect(matchersUtil.equals(objA, objB)).toBe(false); }); it('passes when comparing two empty sets', function() { const matchersUtil = new privateUnderTest.MatchersUtil(); expect(matchersUtil.equals(new Set(), new Set())).toBe(true); }); it('passes when comparing identical sets', function() { const matchersUtil = new privateUnderTest.MatchersUtil(); const setA = new Set(); setA.add(6); setA.add(5); const setB = new Set(); setB.add(6); setB.add(5); expect(matchersUtil.equals(setA, setB)).toBe(true); }); it('passes when comparing identical sets with different insertion order and simple elements', function() { const matchersUtil = new privateUnderTest.MatchersUtil(); const setA = new Set(); setA.add(3); setA.add(6); const setB = new Set(); setB.add(6); setB.add(3); expect(matchersUtil.equals(setA, setB)).toBe(true); }); it('passes when comparing identical sets with different insertion order and complex elements 1', function() { const matchersUtil = new privateUnderTest.MatchersUtil(); const setA1 = new Set(); setA1.add(['a', 3]); setA1.add([6, 1]); const setA2 = new Set(); setA1.add(['y', 3]); setA1.add([6, 1]); const setA = new Set(); setA.add(setA1); setA.add(setA2); const setB1 = new Set(); setB1.add([6, 1]); setB1.add(['a', 3]); const setB2 = new Set(); setB1.add([6, 1]); setB1.add(['y', 3]); const setB = new Set(); setB.add(setB1); setB.add(setB2); expect(matchersUtil.equals(setA, setB)).toBe(true); }); it('passes when comparing identical sets with different insertion order and complex elements 2', function() { const matchersUtil = new privateUnderTest.MatchersUtil(); const setA = new Set(); setA.add([[1, 2], [3, 4]]); setA.add([[5, 6], [7, 8]]); const setB = new Set(); setB.add([[5, 6], [7, 8]]); setB.add([[1, 2], [3, 4]]); expect(matchersUtil.equals(setA, setB)).toBe(true); }); it('fails for sets with different elements', function() { const matchersUtil = new privateUnderTest.MatchersUtil(); const setA = new Set(); setA.add(6); setA.add(3); setA.add(5); const setB = new Set(); setB.add(6); setB.add(4); setB.add(5); expect(matchersUtil.equals(setA, setB)).toBe(false); }); it('fails for sets of different size', function() { const matchersUtil = new privateUnderTest.MatchersUtil(); const setA = new Set(); setA.add(6); setA.add(3); const setB = new Set(); setB.add(6); setB.add(4); setB.add(5); expect(matchersUtil.equals(setA, setB)).toBe(false); }); it('passes when comparing two empty maps', function() { const matchersUtil = new privateUnderTest.MatchersUtil(); expect(matchersUtil.equals(new Map(), new Map())).toBe(true); }); it('passes when comparing identical maps', function() { const matchersUtil = new privateUnderTest.MatchersUtil(); const mapA = new Map(); mapA.set(6, 5); const mapB = new Map(); mapB.set(6, 5); expect(matchersUtil.equals(mapA, mapB)).toBe(true); }); it('passes when comparing identical maps with different insertion order', function() { const matchersUtil = new privateUnderTest.MatchersUtil(); const mapA = new Map(); mapA.set('a', 3); mapA.set(6, 1); const mapB = new Map(); mapB.set(6, 1); mapB.set('a', 3); expect(matchersUtil.equals(mapA, mapB)).toBe(true); }); it('fails for maps with different elements', function() { const matchersUtil = new privateUnderTest.MatchersUtil(); const mapA = new Map(); mapA.set(6, 3); mapA.set(5, 1); const mapB = new Map(); mapB.set(6, 4); mapB.set(5, 1); expect(matchersUtil.equals(mapA, mapB)).toBe(false); }); it('fails for maps of different size', function() { const matchersUtil = new privateUnderTest.MatchersUtil(); const mapA = new Map(); mapA.set(6, 3); const mapB = new Map(); mapB.set(6, 4); mapB.set(5, 1); expect(matchersUtil.equals(mapA, mapB)).toBe(false); }); it('passes when comparing two identical URLs', function() { const matchersUtil = new privateUnderTest.MatchersUtil(); expect( matchersUtil.equals( new URL('http://localhost/1'), new URL('http://localhost/1') ) ).toBe(true); }); it('fails when comparing two different URLs', function() { const matchersUtil = new privateUnderTest.MatchersUtil(); const url1 = new URL('http://localhost/1'); expect(matchersUtil.equals(url1, new URL('http://localhost/2'))).toBe( false ); expect(matchersUtil.equals(url1, new URL('http://localhost/1?foo'))).toBe( false ); expect(matchersUtil.equals(url1, new URL('http://localhost/1#foo'))).toBe( false ); expect(matchersUtil.equals(url1, new URL('https://localhost/1'))).toBe( false ); expect( matchersUtil.equals(url1, new URL('http://localhost:8080/1')) ).toBe(false); expect(matchersUtil.equals(url1, new URL('http://example.com/1'))).toBe( false ); }); it('passes for ArrayBuffers with same length and content', function() { const buffer1 = new ArrayBuffer(4); const buffer2 = new ArrayBuffer(4); const matchersUtil = new privateUnderTest.MatchersUtil(); expect(matchersUtil.equals(buffer1, buffer2)).toBe(true); }); it('fails for ArrayBuffers with same length but different content', function() { const buffer1 = new ArrayBuffer(4); const buffer2 = new ArrayBuffer(4); const array1 = new Uint8Array(buffer1); array1[0] = 1; const matchersUtil = new privateUnderTest.MatchersUtil(); expect(matchersUtil.equals(buffer1, buffer2)).toBe(false); }); describe('Typed arrays', function() { it('fails for typed arrays of same length and contents but different types', function() { const matchersUtil = new privateUnderTest.MatchersUtil(); const a1 = new Int8Array(1); const a2 = new Uint8Array(1); a1[0] = a2[0] = 0; expect(matchersUtil.equals(a1, a2)).toBe(false); }); [ 'Int8Array', 'Uint8Array', 'Uint8ClampedArray', 'Int16Array', 'Uint16Array', 'Int32Array', 'Uint32Array', 'Float32Array', 'Float64Array' ].forEach(function(typeName) { const TypedArrayCtor = jasmine.getGlobal()[typeName]; it( 'passes for ' + typeName + 's with same length and content', function() { const matchersUtil = new privateUnderTest.MatchersUtil(); const a1 = new TypedArrayCtor(2); const a2 = new TypedArrayCtor(2); a1[0] = a2[0] = 0; a1[1] = a2[1] = 1; const diffBuilder = new privateUnderTest.DiffBuilder(); expect(matchersUtil.equals(a1, a2, diffBuilder)).toBe(true); jasmine.debugLog('Diff: ' + diffBuilder.getMessage()); } ); it('fails for ' + typeName + 's with different length', function() { const matchersUtil = new privateUnderTest.MatchersUtil(); const a1 = new TypedArrayCtor(2); const a2 = new TypedArrayCtor(1); a1[0] = a1[1] = a2[0] = 0; const diffBuilder = new privateUnderTest.DiffBuilder(); expect(matchersUtil.equals(a1, a2, diffBuilder)).toBe(false); jasmine.debugLog('Diff: ' + diffBuilder.getMessage()); }); it( 'fails for ' + typeName + 's with same length but different content', function() { const matchersUtil = new privateUnderTest.MatchersUtil(); const a1 = new TypedArrayCtor(1); const a2 = new TypedArrayCtor(1); a1[0] = 0; a2[0] = 1; const diffBuilder = new privateUnderTest.DiffBuilder(); expect(matchersUtil.equals(a1, a2, diffBuilder)).toBe(false); jasmine.debugLog( 'a1 keys: ' + jasmine.pp(privateUnderTest.MatchersUtil.keys(a1)) ); jasmine.debugLog( 'a2 keys: ' + jasmine.pp(privateUnderTest.MatchersUtil.keys(a2)) ); jasmine.debugLog('a1 length:' + a1.length); jasmine.debugLog('a2 length:' + a2.length); jasmine.debugLog('a1[0]: ' + a1[0]); jasmine.debugLog('a2[0]: ' + a2[0]); } ); it('checks nonstandard properties of ' + typeName, function() { const matchersUtil = new privateUnderTest.MatchersUtil(); const a1 = new TypedArrayCtor(1); const a2 = new TypedArrayCtor(1); a1[0] = a2[0] = 0; a1.extra = 'yes'; expect(matchersUtil.equals(a1, a2)).toBe(false); }); it('works with custom equality testers with ' + typeName, function() { const a1 = new TypedArrayCtor(1); const a2 = new TypedArrayCtor(1); const matchersUtil = new privateUnderTest.MatchersUtil({ customTesters: [ function() { return true; } ] }); a1[0] = 0; a2[0] = 1; expect(matchersUtil.equals(a1, a2)).toBe(true); }); }); ['BigInt64Array', 'BigUint64Array'].forEach(function(typeName) { function requireType() { const TypedArrayCtor = jasmine.getGlobal()[typeName]; if (!TypedArrayCtor) { pending('Browser does not support ' + typeName); } return TypedArrayCtor; } it( 'passes for ' + typeName + 's with same length and content', function() { const TypedArrayCtor = requireType(); const matchersUtil = new privateUnderTest.MatchersUtil(); const a1 = new TypedArrayCtor(2); const a2 = new TypedArrayCtor(2); a1[0] = a2[0] = BigInt(0); a1[1] = a2[1] = BigInt(1); expect(matchersUtil.equals(a1, a2)).toBe(true); } ); it('fails for ' + typeName + 's with different length', function() { const TypedArrayCtor = requireType(); const matchersUtil = new privateUnderTest.MatchersUtil(); const a1 = new TypedArrayCtor(2); const a2 = new TypedArrayCtor(1); a1[0] = a1[1] = a2[0] = BigInt(0); expect(matchersUtil.equals(a1, a2)).toBe(false); }); it( 'fails for ' + typeName + 's with same length but different content', function() { const TypedArrayCtor = requireType(); const matchersUtil = new privateUnderTest.MatchersUtil(); const a1 = new TypedArrayCtor(2); const a2 = new TypedArrayCtor(2); a1[0] = a1[1] = a2[0] = BigInt(0); a2[1] = BigInt(1); expect(matchersUtil.equals(a1, a2)).toBe(false); } ); }); }); describe('when running in an environment with array polyfills', function() { const findIndexDescriptor = Object.getOwnPropertyDescriptor( Array.prototype, 'findIndex' ); beforeEach(function() { if (!findIndexDescriptor) { jasmine .getEnv() .pending( 'Environment does not have a property descriptor for Array.prototype.findIndex' ); } Object.defineProperty(Array.prototype, 'findIndex', { enumerable: true, value: function(predicate) { if (this === null) { throw new TypeError( 'Array.prototype.findIndex called on null or undefined' ); } if (typeof predicate !== 'function') { throw new TypeError('predicate must be a function'); } const list = Object(this); const length = list.length >>> 0; const thisArg = arguments[1]; for (let i = 0; i < length; i++) { const value = list[i]; if (predicate.call(thisArg, value, i, list)) { return i; } } return -1; } }); }); afterEach(function() { Object.defineProperty( Array.prototype, 'findIndex', findIndexDescriptor ); }); it("passes when there's an array polyfill", function() { expect(['foo']).toEqual(['foo']); }); }); describe('Building diffs for asymmetric equality testers', function() { it('diffs the values returned by valuesForDiff_', function() { const tester = { asymmetricMatch: function() { return false; }, valuesForDiff_: function() { return { self: 'asymmetric tester value', other: 'other value' }; } }; const actual = { x: 42 }; const expected = { x: tester }; const diffBuilder = jasmine.createSpyObj('diffBuilder', [ 'recordMismatch', 'withPath', 'setRoots' ]); const matchersUtil = new privateUnderTest.MatchersUtil(); diffBuilder.withPath.and.callFake(function(p, block) { block(); }); matchersUtil.equals(actual, expected, diffBuilder); expect(diffBuilder.setRoots).toHaveBeenCalledWith(actual, expected); expect(diffBuilder.withPath).toHaveBeenCalledWith( 'x', jasmine.any(Function) ); expect(diffBuilder.recordMismatch).toHaveBeenCalledWith(); }); it('records both objects when the tester does not implement valuesForDiff', function() { const tester = { asymmetricMatch: function() { return false; } }; const actual = { x: 42 }; const expected = { x: tester }; const diffBuilder = jasmine.createSpyObj('diffBuilder', [ 'recordMismatch', 'withPath', 'setRoots' ]); const matchersUtil = new privateUnderTest.MatchersUtil(); diffBuilder.withPath.and.callFake(function(p, block) { block(); }); matchersUtil.equals(actual, expected, diffBuilder); expect(diffBuilder.setRoots).toHaveBeenCalledWith(actual, expected); expect(diffBuilder.withPath).toHaveBeenCalledWith( 'x', jasmine.any(Function) ); expect(diffBuilder.recordMismatch).toHaveBeenCalledWith(); }); }); it('uses a diffBuilder if one is provided as the third argument', function() { const diffBuilder = new privateUnderTest.DiffBuilder(); const matchersUtil = new privateUnderTest.MatchersUtil(); spyOn(diffBuilder, 'recordMismatch'); spyOn(diffBuilder, 'withPath').and.callThrough(); matchersUtil.equals([1], [2], diffBuilder); expect(diffBuilder.withPath).toHaveBeenCalledWith( 'length', jasmine.any(Function) ); expect(diffBuilder.withPath).toHaveBeenCalledWith( 0, jasmine.any(Function) ); expect(diffBuilder.recordMismatch).toHaveBeenCalled(); }); }); describe('contains', function() { it('passes when expected is a substring of actual', function() { const matchersUtil = new privateUnderTest.MatchersUtil(); expect(matchersUtil.contains('ABC', 'BC')).toBe(true); }); it('fails when expected is a not substring of actual', function() { const matchersUtil = new privateUnderTest.MatchersUtil(); expect(matchersUtil.contains('ABC', 'X')).toBe(false); }); it('passes when expected is an element in an actual array', function() { const matchersUtil = new privateUnderTest.MatchersUtil(); expect(matchersUtil.contains(['foo', 'bar'], 'foo')).toBe(true); }); it('fails when expected is not an element in an actual array', function() { const matchersUtil = new privateUnderTest.MatchersUtil(); expect(matchersUtil.contains(['foo', 'bar'], 'baz')).toBe(false); }); it('passes with mixed-element arrays', function() { const matchersUtil = new privateUnderTest.MatchersUtil(); expect(matchersUtil.contains(['foo', { some: 'bar' }], 'foo')).toBe(true); expect( matchersUtil.contains(['foo', { some: 'bar' }], { some: 'bar' }) ).toBe(true); }); it('uses custom equality testers if actual is an Array', function() { const customTester = function() { return true; }; const matchersUtil = new privateUnderTest.MatchersUtil({ customTesters: [customTester], pp: function() {} }); expect(matchersUtil.contains([1, 2], 3)).toBe(true); }); it('fails when actual is undefined', function() { const matchersUtil = new privateUnderTest.MatchersUtil(); expect(matchersUtil.contains(undefined, 'A')).toBe(false); }); it('fails when actual is null', function() { const matchersUtil = new privateUnderTest.MatchersUtil(); expect(matchersUtil.contains(null, 'A')).toBe(false); }); it('works with array-like objects that implement iterable', function() { let capturedArgs = null; const matchersUtil = new privateUnderTest.MatchersUtil(); function testFunction() { capturedArgs = arguments; } testFunction('foo', 'bar'); expect(matchersUtil.contains(capturedArgs, 'bar')).toBe(true); expect(matchersUtil.contains(capturedArgs, 'baz')).toBe(false); }); it("passes with array-like objects that don't implement iterable", function() { const arrayLike = { 0: 'a', 1: 'b', length: 2 }; const matchersUtil = new privateUnderTest.MatchersUtil(); expect(matchersUtil.contains(arrayLike, 'b')).toBe(true); expect(matchersUtil.contains(arrayLike, 'c')).toBe(false); }); it('passes for set members', function() { const matchersUtil = new privateUnderTest.MatchersUtil(); const setItem = { foo: 'bar' }; const set = new Set(); set.add(setItem); expect(matchersUtil.contains(set, setItem)).toBe(true); }); it('passes for objects that equal to a set member', function() { const matchersUtil = new privateUnderTest.MatchersUtil(); const set = new Set(); set.add({ foo: 'bar' }); expect(matchersUtil.contains(set, { foo: 'bar' })).toBe(true); }); }); describe('buildFailureMessage', function() { it('builds an English sentence for a failure case', function() { const actual = 'foo'; const name = 'toBar'; const pp = privateUnderTest.makePrettyPrinter(); const matchersUtil = new privateUnderTest.MatchersUtil({ pp: pp }); const message = matchersUtil.buildFailureMessage(name, false, actual); expect(message).toEqual("Expected 'foo' to bar."); }); it("builds an English sentence for a 'not' failure case", function() { const actual = 'foo'; const name = 'toBar'; const isNot = true; const pp = privateUnderTest.makePrettyPrinter(); const matchersUtil = new privateUnderTest.MatchersUtil({ pp: pp }); const message = matchersUtil.buildFailureMessage(name, isNot, actual); expect(message).toEqual("Expected 'foo' not to bar."); }); it('builds an English sentence for an arbitrary array of expected arguments', function() { const actual = 'foo'; const name = 'toBar'; const pp = privateUnderTest.makePrettyPrinter(); const matchersUtil = new privateUnderTest.MatchersUtil({ pp: pp }); const message = matchersUtil.buildFailureMessage( name, false, actual, 'quux', 'corge' ); expect(message).toEqual("Expected 'foo' to bar 'quux', 'corge'."); }); it('uses the injected pretty-printer to format the expecteds and actual', function() { const actual = 'foo'; const expected1 = 'qux'; const expected2 = 'grault'; const name = 'toBar'; const isNot = false; const pp = function(value) { return '<' + value + '>'; }; const matchersUtil = new privateUnderTest.MatchersUtil({ pp: pp }); const message = matchersUtil.buildFailureMessage( name, isNot, actual, expected1, expected2 ); expect(message).toEqual('Expected to bar , .'); }); }); });