From 89331bb1bbda9a86f8eea7db43ee8605b414bff1 Mon Sep 17 00:00:00 2001 From: Steve Gravrock Date: Sat, 21 Nov 2020 13:35:10 -0800 Subject: [PATCH] Fixed comparison between URL objects * Fixes #1866 --- lib/jasmine-core/jasmine.js | 13 +++++++ spec/core/baseSpec.js | 13 +++++++ spec/core/matchers/matchersUtilSpec.js | 48 ++++++++++++++++++++++++++ spec/helpers/checkForUrl.js | 17 +++++++++ spec/support/jasmine-browser.js | 1 + spec/support/jasmine.json | 1 + src/core/base.js | 9 +++++ src/core/matchers/matchersUtil.js | 4 +++ 8 files changed, 106 insertions(+) create mode 100644 spec/helpers/checkForUrl.js diff --git a/lib/jasmine-core/jasmine.js b/lib/jasmine-core/jasmine.js index 4cc2b45f..527f3fb4 100644 --- a/lib/jasmine-core/jasmine.js +++ b/lib/jasmine-core/jasmine.js @@ -329,6 +329,15 @@ getJasmineRequireObj().base = function(j$, jasmineGlobal) { ); }; + j$.isURL = function(obj) { + return ( + obj !== null && + typeof obj !== 'undefined' && + typeof jasmineGlobal.URL !== 'undefined' && + obj.constructor === jasmineGlobal.URL + ); + }; + j$.isDataView = function(obj) { return ( obj !== null && @@ -5094,6 +5103,10 @@ getJasmineRequireObj().MatchersUtil = function(j$) { diffBuilder.recordMismatch(); return false; } + } else if (j$.isURL(a) && j$.isURL(b)) { + // URLs have no enumrable properties, so the default object comparison + // would consider any two URLs to be equal. + return a.toString() === b.toString(); } else { // Objects with different constructors are not equivalent, but `Object`s // or `Array`s from different frames are. diff --git a/spec/core/baseSpec.js b/spec/core/baseSpec.js index fbdf9d9d..99a0e668 100644 --- a/spec/core/baseSpec.js +++ b/spec/core/baseSpec.js @@ -62,4 +62,17 @@ describe('base helpers', function() { expect(jasmineUnderTest.isSet({})).toBe(false); }); }); + + describe('isURL', function() { + it('returns true when the object is a URL', function() { + jasmine.getEnv().requireUrls(); + // eslint-disable-next-line compat/compat + expect(jasmineUnderTest.isURL(new URL('http://localhost/'))).toBe(true); + }); + + it('returns false when the object is not a URL', function() { + jasmine.getEnv().requireUrls(); + expect(jasmineUnderTest.isURL({})).toBe(false); + }); + }); }); diff --git a/spec/core/matchers/matchersUtilSpec.js b/spec/core/matchers/matchersUtilSpec.js index ed03f38e..839b7e67 100644 --- a/spec/core/matchers/matchersUtilSpec.js +++ b/spec/core/matchers/matchersUtilSpec.js @@ -837,6 +837,54 @@ describe('matchersUtil', function() { expect(matchersUtil.equals(mapA, mapB)).toBe(false); }); + it('passes when comparing two identical URLs', function() { + jasmine.getEnv().requireUrls(); + + var matchersUtil = new jasmineUnderTest.MatchersUtil(); + + expect( + matchersUtil.equals( + // eslint-disable-next-line compat/compat + new URL('http://localhost/1'), + // eslint-disable-next-line compat/compat + new URL('http://localhost/1') + ) + ).toBe(true); + }); + + it('fails when comparing two different URLs', function() { + jasmine.getEnv().requireUrls(); + + var matchersUtil = new jasmineUnderTest.MatchersUtil(), + // eslint-disable-next-line compat/compat + url1 = new URL('http://localhost/1'); + + // eslint-disable-next-line compat/compat + expect(matchersUtil.equals(url1, new URL('http://localhost/2'))).toBe( + false + ); + // eslint-disable-next-line compat/compat + expect(matchersUtil.equals(url1, new URL('http://localhost/1?foo'))).toBe( + false + ); + // eslint-disable-next-line compat/compat + expect(matchersUtil.equals(url1, new URL('http://localhost/1#foo'))).toBe( + false + ); + // eslint-disable-next-line compat/compat + expect(matchersUtil.equals(url1, new URL('https://localhost/1'))).toBe( + false + ); + expect( + // eslint-disable-next-line compat/compat + matchersUtil.equals(url1, new URL('http://localhost:8080/1')) + ).toBe(false); + // eslint-disable-next-line compat/compat + expect(matchersUtil.equals(url1, new URL('http://example.com/1'))).toBe( + false + ); + }); + describe('when running in an environment with array polyfills', function() { var findIndexDescriptor = Object.getOwnPropertyDescriptor( Array.prototype, diff --git a/spec/helpers/checkForUrl.js b/spec/helpers/checkForUrl.js new file mode 100644 index 00000000..f8401fbb --- /dev/null +++ b/spec/helpers/checkForUrl.js @@ -0,0 +1,17 @@ +/* eslint-disable compat/compat */ +(function(env) { + function hasUrlConstructor() { + try { + new URL('http://localhost/'); + return true; + } catch (e) { + return false; + } + } + + env.requireUrls = function() { + if (!hasUrlConstructor()) { + env.pending('Environment does not support URLs'); + } + }; +})(jasmine.getEnv()); diff --git a/spec/support/jasmine-browser.js b/spec/support/jasmine-browser.js index 2f5f7146..1f1e8bef 100644 --- a/spec/support/jasmine-browser.js +++ b/spec/support/jasmine-browser.js @@ -24,6 +24,7 @@ module.exports = { 'helpers/checkForSet.js', 'helpers/checkForSymbol.js', 'helpers/checkForTypedArrays.js', + 'helpers/checkForUrl.js', 'helpers/domHelpers.js', 'helpers/integrationMatchers.js', 'helpers/promises.js', diff --git a/spec/support/jasmine.json b/spec/support/jasmine.json index 0276ca92..56ed17c0 100644 --- a/spec/support/jasmine.json +++ b/spec/support/jasmine.json @@ -11,6 +11,7 @@ "helpers/checkForSet.js", "helpers/checkForSymbol.js", "helpers/checkForTypedArrays.js", + "helpers/checkForUrl.js", "helpers/domHelpers.js", "helpers/integrationMatchers.js", "helpers/promises.js", diff --git a/src/core/base.js b/src/core/base.js index d02606d5..5ff9441e 100644 --- a/src/core/base.js +++ b/src/core/base.js @@ -161,6 +161,15 @@ getJasmineRequireObj().base = function(j$, jasmineGlobal) { ); }; + j$.isURL = function(obj) { + return ( + obj !== null && + typeof obj !== 'undefined' && + typeof jasmineGlobal.URL !== 'undefined' && + obj.constructor === jasmineGlobal.URL + ); + }; + j$.isDataView = function(obj) { return ( obj !== null && diff --git a/src/core/matchers/matchersUtil.js b/src/core/matchers/matchersUtil.js index 6a74d89d..5f75a4ed 100644 --- a/src/core/matchers/matchersUtil.js +++ b/src/core/matchers/matchersUtil.js @@ -475,6 +475,10 @@ getJasmineRequireObj().MatchersUtil = function(j$) { diffBuilder.recordMismatch(); return false; } + } else if (j$.isURL(a) && j$.isURL(b)) { + // URLs have no enumrable properties, so the default object comparison + // would consider any two URLs to be equal. + return a.toString() === b.toString(); } else { // Objects with different constructors are not equivalent, but `Object`s // or `Array`s from different frames are.