From 9065b4c3b783e35667f68046667399d652932433 Mon Sep 17 00:00:00 2001 From: Steve Gravrock Date: Fri, 21 May 2021 17:13:07 -0700 Subject: [PATCH 01/23] Added a jsdoc cross-reference from Configuration to its usage --- lib/jasmine-core/jasmine.js | 3 ++- src/core/Env.js | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/jasmine-core/jasmine.js b/lib/jasmine-core/jasmine.js index 380829bd..ab22c86b 100644 --- a/lib/jasmine-core/jasmine.js +++ b/lib/jasmine-core/jasmine.js @@ -1014,7 +1014,8 @@ getJasmineRequireObj().Env = function(j$) { /** * This represents the available options to configure Jasmine. - * Options that are not provided will use their default values + * Options that are not provided will use their default values. + * @see Env#configure * @interface Configuration * @since 3.3.0 */ diff --git a/src/core/Env.js b/src/core/Env.js index 0dbf0a1c..741f0e16 100644 --- a/src/core/Env.js +++ b/src/core/Env.js @@ -35,7 +35,8 @@ getJasmineRequireObj().Env = function(j$) { /** * This represents the available options to configure Jasmine. - * Options that are not provided will use their default values + * Options that are not provided will use their default values. + * @see Env#configure * @interface Configuration * @since 3.3.0 */ From a4ef3687ee8d8d2975bc9e9014a1831f98502680 Mon Sep 17 00:00:00 2001 From: Darius Keeley Date: Thu, 22 Apr 2021 18:24:20 +0100 Subject: [PATCH 02/23] Add optional param to spyOnAllFunctions to include non-enumerable properties --- spec/core/SpyRegistrySpec.js | 117 +++++++++++++++++++++++++++++++++++ src/core/SpyRegistry.js | 83 ++++++++++++++++++------- src/core/requireInterface.js | 5 +- 3 files changed, 182 insertions(+), 23 deletions(-) diff --git a/spec/core/SpyRegistrySpec.js b/spec/core/SpyRegistrySpec.js index f3c81a47..11d1e5dd 100644 --- a/spec/core/SpyRegistrySpec.js +++ b/spec/core/SpyRegistrySpec.js @@ -407,6 +407,123 @@ describe('SpyRegistry', function() { expect(subject.toString).not.toBe('I am a spy'); expect(subject.hasOwnProperty).not.toBe('I am a spy'); }); + describe('when includeNonEnumerable is true', function() { + it('does not override Object.prototype methods', function() { + var spyRegistry = new jasmineUnderTest.SpyRegistry({ + createSpy: function() { + return 'I am a spy'; + } + }); + var subject = { + spied1: function() {} + }; + + spyRegistry.spyOnAllFunctions(subject, true); + + expect(subject.spied1).toBe('I am a spy'); + expect(subject.toString).not.toBe('I am a spy'); + expect(subject.hasOwnProperty).not.toBe('I am a spy'); + }); + + it('overrides non-enumerable properties', function() { + var spyRegistry = new jasmineUnderTest.SpyRegistry({ + createSpy: function() { + return 'I am a spy'; + } + }); + var subject = { + spied1: function() {}, + spied2: function() {} + }; + + Object.defineProperty(subject, 'spied2', { + enumerable: false, + writable: true, + configurable: true + }); + + spyRegistry.spyOnAllFunctions(subject, true); + + expect(subject.spied1).toBe('I am a spy'); + expect(subject.spied2).toBe('I am a spy'); + }); + + it('should not spy on non-enumerable functions named constructor', function() { + var spyRegistry = new jasmineUnderTest.SpyRegistry({ + createSpy: function() { + return 'I am a spy'; + } + }); + var subject = { + constructor: function() {} + }; + + Object.defineProperty(subject, 'constructor', { + enumerable: false, + writable: true, + configurable: true + }); + + spyRegistry.spyOnAllFunctions(subject, true); + + expect(subject.constructor).not.toBe('I am a spy'); + }); + + it('should spy on enumerable functions named constructor', function() { + var spyRegistry = new jasmineUnderTest.SpyRegistry({ + createSpy: function() { + return 'I am a spy'; + } + }); + var subject = { + constructor: function() {} + }; + + spyRegistry.spyOnAllFunctions(subject, true); + + expect(subject.constructor).toBe('I am a spy'); + }); + + it('should not throw an exception if we try and access strict mode restricted properties', function() { + var spyRegistry = new jasmineUnderTest.SpyRegistry({ + createSpy: function() { + return 'I am a spy'; + } + }); + var subject = function() {}; + var fn = function() { + spyRegistry.spyOnAllFunctions(subject, true); + }; + + expect(fn).not.toThrow(); + }); + + it('should not spy on properties which are more permissable further up the prototype chain', function() { + var spyRegistry = new jasmineUnderTest.SpyRegistry({ + createSpy: function() { + return 'I am a spy'; + } + }); + var subjectParent = Object.defineProperty({}, 'sharedProp', { + value: function() {}, + writable: true, + configurable: true + }); + + var subject = Object.create(subjectParent); + + Object.defineProperty(subject, 'sharedProp', { + value: function() {} + }); + + var fn = function() { + spyRegistry.spyOnAllFunctions(subject, true); + }; + + expect(fn).not.toThrow(); + expect(subject).not.toBe('I am a spy'); + }); + }); }); describe('#clearSpies', function() { diff --git a/src/core/SpyRegistry.js b/src/core/SpyRegistry.js index 6e0bb33e..140bd990 100644 --- a/src/core/SpyRegistry.js +++ b/src/core/SpyRegistry.js @@ -163,7 +163,7 @@ getJasmineRequireObj().SpyRegistry = function(j$) { return spy; }; - this.spyOnAllFunctions = function(obj) { + this.spyOnAllFunctions = function(obj, includeNonEnumerable) { if (j$.util.isUndefined(obj)) { throw new Error( 'spyOnAllFunctions could not find an object to spy upon' @@ -171,30 +171,27 @@ getJasmineRequireObj().SpyRegistry = function(j$) { } var pointer = obj, - props = [], - prop, - descriptor; + propsToSpyOn = [], + properties, + propertiesToSkip = []; - while (pointer) { - for (prop in pointer) { - if ( - Object.prototype.hasOwnProperty.call(pointer, prop) && - pointer[prop] instanceof Function - ) { - descriptor = Object.getOwnPropertyDescriptor(pointer, prop); - if ( - (descriptor.writable || descriptor.set) && - descriptor.configurable - ) { - props.push(prop); - } - } - } + while ( + pointer && + (!includeNonEnumerable || pointer !== Object.prototype) + ) { + properties = getProps(pointer, includeNonEnumerable); + properties = properties.filter(function(prop) { + return propertiesToSkip.indexOf(prop) === -1; + }); + propertiesToSkip = propertiesToSkip.concat(properties); + propsToSpyOn = propsToSpyOn.concat( + getSpyableFunctionProps(pointer, properties) + ); pointer = Object.getPrototypeOf(pointer); } - for (var i = 0; i < props.length; i++) { - this.spyOn(obj, props[i]); + for (var i = 0; i < propsToSpyOn.length; i++) { + this.spyOn(obj, propsToSpyOn[i]); } return obj; @@ -209,5 +206,49 @@ getJasmineRequireObj().SpyRegistry = function(j$) { }; } + function getProps(obj, includeNonEnumerable) { + var enumerableProperties = Object.keys(obj); + + if (!includeNonEnumerable) { + return enumerableProperties; + } + + return Object.getOwnPropertyNames(obj).filter(function(prop) { + return ( + prop !== 'constructor' || + enumerableProperties.indexOf('constructor') > -1 + ); + }); + } + + function getSpyableFunctionProps(obj, propertiesToCheck) { + var props = [], + prop; + for (var i = 0; i < propertiesToCheck.length; i++) { + prop = propertiesToCheck[i]; + if ( + Object.prototype.hasOwnProperty.call(obj, prop) && + isSpyableProp(obj, prop) + ) { + props.push(prop); + } + } + return props; + } + + function isSpyableProp(obj, prop) { + var value, descriptor; + try { + value = obj[prop]; + } catch (e) { + return false; + } + if (value instanceof Function) { + descriptor = Object.getOwnPropertyDescriptor(obj, prop); + return (descriptor.writable || descriptor.set) && descriptor.configurable; + } + return false; + } + return SpyRegistry; }; diff --git a/src/core/requireInterface.js b/src/core/requireInterface.js index 6473ffaa..71f7d276 100644 --- a/src/core/requireInterface.js +++ b/src/core/requireInterface.js @@ -285,10 +285,11 @@ getJasmineRequireObj().interface = function(jasmine, env) { * @function * @global * @param {Object} obj - The object upon which to install the {@link Spy}s + * @param {boolean} includeNonEnumerable - Whether or not to add spies to non-enumerable properties * @returns {Object} the spied object */ - spyOnAllFunctions: function(obj) { - return env.spyOnAllFunctions(obj); + spyOnAllFunctions: function(obj, includeNonEnumerable) { + return env.spyOnAllFunctions(obj, includeNonEnumerable); }, jsApiReporter: new jasmine.JsApiReporter({ From f2de1be96ab793d9a905e2888a8dac887b6f2d1a Mon Sep 17 00:00:00 2001 From: Steve Gravrock Date: Tue, 1 Jun 2021 08:47:55 -0700 Subject: [PATCH 03/23] Fixed "stop spec on expectation failure" checkbox in standalone Fixes [#178248968]. --- lib/jasmine-core/jasmine-html.js | 2 +- spec/html/HtmlReporterSpec.js | 7 +++++-- src/html/HtmlReporter.js | 2 +- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/lib/jasmine-core/jasmine-html.js b/lib/jasmine-core/jasmine-html.js index 8539c76c..ebe94049 100644 --- a/lib/jasmine-core/jasmine-html.js +++ b/lib/jasmine-core/jasmine-html.js @@ -565,7 +565,7 @@ jasmineRequire.HtmlReporter = function(j$) { ); throwCheckbox.checked = config.oneFailurePerSpec; throwCheckbox.onclick = function() { - navigateWithNewParam('throwFailures', !config.oneFailurePerSpec); + navigateWithNewParam('oneFailurePerSpec', !config.oneFailurePerSpec); }; var randomCheckbox = optionsMenuDom.querySelector( diff --git a/spec/html/HtmlReporterSpec.js b/spec/html/HtmlReporterSpec.js index 2db0e160..07b5d59f 100644 --- a/spec/html/HtmlReporterSpec.js +++ b/spec/html/HtmlReporterSpec.js @@ -802,7 +802,7 @@ describe('HtmlReporter', function() { var throwingExpectationsUI = container.querySelector('.jasmine-throw'); throwingExpectationsUI.click(); - expect(navigateHandler).toHaveBeenCalledWith('throwFailures', true); + expect(navigateHandler).toHaveBeenCalledWith('oneFailurePerSpec', true); }); it('should navigate and change the setting to off', function() { @@ -831,7 +831,10 @@ describe('HtmlReporter', function() { var throwingExpectationsUI = container.querySelector('.jasmine-throw'); throwingExpectationsUI.click(); - expect(navigateHandler).toHaveBeenCalledWith('throwFailures', false); + expect(navigateHandler).toHaveBeenCalledWith( + 'oneFailurePerSpec', + false + ); }); }); describe('UI for hiding disabled specs', function() { diff --git a/src/html/HtmlReporter.js b/src/html/HtmlReporter.js index e591ccac..1c34b260 100644 --- a/src/html/HtmlReporter.js +++ b/src/html/HtmlReporter.js @@ -534,7 +534,7 @@ jasmineRequire.HtmlReporter = function(j$) { ); throwCheckbox.checked = config.oneFailurePerSpec; throwCheckbox.onclick = function() { - navigateWithNewParam('throwFailures', !config.oneFailurePerSpec); + navigateWithNewParam('oneFailurePerSpec', !config.oneFailurePerSpec); }; var randomCheckbox = optionsMenuDom.querySelector( From 6aecf16cde3ff624ac8b902e626c31725a7fd15b Mon Sep 17 00:00:00 2001 From: Steve Gravrock Date: Sat, 5 Jun 2021 09:30:21 -0700 Subject: [PATCH 04/23] Test coverage for MatchersUtil#equals with typed arrays --- spec/core/matchers/matchersUtilSpec.js | 138 +++++++++++++++++++++++++ 1 file changed, 138 insertions(+) diff --git a/spec/core/matchers/matchersUtilSpec.js b/spec/core/matchers/matchersUtilSpec.js index dab04308..2f61991a 100644 --- a/spec/core/matchers/matchersUtilSpec.js +++ b/spec/core/matchers/matchersUtilSpec.js @@ -904,6 +904,144 @@ describe('matchersUtil', function() { ); }); + describe('Typed arrays', function() { + it('fails for typed arrays of same length and contents but different types', function() { + jasmine.getEnv().requireFunctioningTypedArrays(); + // eslint-disable-next-line compat/compat + var a1 = new Int8Array(1); + // eslint-disable-next-line compat/compat + var a2 = new Uint8Array(1); + a1[0] = a2[0] = 0; + expect(jasmineUnderTest.matchersUtil.equals(a1, a2)).toBe(false); + }); + + // eslint-disable-next-line compat/compat + [ + Int8Array, + Uint8Array, + Uint8ClampedArray, + Int16Array, + Uint16Array, + Int32Array, + Uint32Array, + Float32Array, + Float64Array + ].forEach(function(TypedArrayCtor) { + it( + 'passes for ' + + TypedArrayCtor.name + + 's with same length and content', + function() { + jasmine.getEnv().requireFunctioningTypedArrays(); + var a1 = new TypedArrayCtor(2); + var a2 = new TypedArrayCtor(2); + a1[0] = a2[0] = 0; + a1[1] = a2[1] = 1; + expect(jasmineUnderTest.matchersUtil.equals(a1, a2)).toBe(true); + } + ); + + it( + 'fails for ' + TypedArrayCtor.name + 's with different length', + function() { + jasmine.getEnv().requireFunctioningTypedArrays(); + var a1 = new TypedArrayCtor(2); + var a2 = new TypedArrayCtor(1); + a1[0] = a1[1] = a2[0] = 0; + expect(jasmineUnderTest.matchersUtil.equals(a1, a2)).toBe(false); + } + ); + + it( + 'fails for ' + + TypedArrayCtor.name + + 's with same length but different content', + function() { + jasmine.getEnv().requireFunctioningTypedArrays(); + var a1 = new TypedArrayCtor(1); + var a2 = new TypedArrayCtor(1); + a1[0] = 0; + a2[0] = 1; + expect(jasmineUnderTest.matchersUtil.equals(a1, a2)).toBe(false); + } + ); + + it('checks nonstandard properties', function() { + jasmine.getEnv().requireFunctioningTypedArrays(); + var a1 = new TypedArrayCtor(1); + var a2 = new TypedArrayCtor(1); + a1[0] = a2[0] = 0; + a1.extra = 'yes'; + expect(jasmineUnderTest.matchersUtil.equals(a1, a2)).toBe(false); + }); + + it('works with custom equality testers', function() { + jasmine.getEnv().requireFunctioningTypedArrays(); + var a1 = new TypedArrayCtor(1); + var a2 = new TypedArrayCtor(1); + var matchersUtil = new jasmineUnderTest.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() { + var 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() { + var TypedArrayCtor = requireType(); + var a1 = new TypedArrayCtor(2); + var a2 = new TypedArrayCtor(2); + // eslint-disable-next-line compat/compat + a1[0] = a2[0] = BigInt(0); + // eslint-disable-next-line compat/compat + a1[1] = a2[1] = BigInt(1); + expect(jasmineUnderTest.matchersUtil.equals(a1, a2)).toBe(true); + } + ); + + it('fails for ' + typeName + 's with different length', function() { + var TypedArrayCtor = requireType(); + var a1 = new TypedArrayCtor(2); + var a2 = new TypedArrayCtor(1); + // eslint-disable-next-line compat/compat + a1[0] = a1[1] = a2[0] = BigInt(0); + expect(jasmineUnderTest.matchersUtil.equals(a1, a2)).toBe(false); + }); + + it( + 'fails for ' + typeName + 's with same length but different content', + function() { + var TypedArrayCtor = requireType(); + var a1 = new TypedArrayCtor(2); + var a2 = new TypedArrayCtor(2); + // eslint-disable-next-line compat/compat + a1[0] = a1[1] = a2[0] = BigInt(0); + // eslint-disable-next-line compat/compat + a2[1] = BigInt(1); + expect(jasmineUnderTest.matchersUtil.equals(a1, a2)).toBe(false); + } + ); + }); + }); + describe('when running in an environment with array polyfills', function() { var findIndexDescriptor = Object.getOwnPropertyDescriptor( Array.prototype, From 1e50b49092d9804ad38610afdf70c0c3a55f7a93 Mon Sep 17 00:00:00 2001 From: Steve Gravrock Date: Sat, 5 Jun 2021 13:13:46 -0700 Subject: [PATCH 05/23] Fixed test failures on IE 10 --- spec/core/matchers/matchersUtilSpec.js | 67 ++++++++++++++------------ 1 file changed, 35 insertions(+), 32 deletions(-) diff --git a/spec/core/matchers/matchersUtilSpec.js b/spec/core/matchers/matchersUtilSpec.js index 2f61991a..627deddc 100644 --- a/spec/core/matchers/matchersUtilSpec.js +++ b/spec/core/matchers/matchersUtilSpec.js @@ -917,22 +917,30 @@ describe('matchersUtil', function() { // eslint-disable-next-line compat/compat [ - Int8Array, - Uint8Array, - Uint8ClampedArray, - Int16Array, - Uint16Array, - Int32Array, - Uint32Array, - Float32Array, - Float64Array - ].forEach(function(TypedArrayCtor) { + 'Int8Array', + 'Uint8Array', + 'Uint8ClampedArray', + 'Int16Array', + 'Uint16Array', + 'Int32Array', + 'Uint32Array', + 'Float32Array', + 'Float64Array' + ].forEach(function(typeName) { + function requireType() { + var TypedArrayCtor = jasmine.getGlobal()[typeName]; + + if (!TypedArrayCtor) { + pending('Browser does not support ' + typeName); + } + + return TypedArrayCtor; + } + it( - 'passes for ' + - TypedArrayCtor.name + - 's with same length and content', + 'passes for ' + typeName + 's with same length and content', function() { - jasmine.getEnv().requireFunctioningTypedArrays(); + var TypedArrayCtor = requireType(); var a1 = new TypedArrayCtor(2); var a2 = new TypedArrayCtor(2); a1[0] = a2[0] = 0; @@ -941,23 +949,18 @@ describe('matchersUtil', function() { } ); - it( - 'fails for ' + TypedArrayCtor.name + 's with different length', - function() { - jasmine.getEnv().requireFunctioningTypedArrays(); - var a1 = new TypedArrayCtor(2); - var a2 = new TypedArrayCtor(1); - a1[0] = a1[1] = a2[0] = 0; - expect(jasmineUnderTest.matchersUtil.equals(a1, a2)).toBe(false); - } - ); + it('fails for ' + typeName + 's with different length', function() { + var TypedArrayCtor = requireType(); + var a1 = new TypedArrayCtor(2); + var a2 = new TypedArrayCtor(1); + a1[0] = a1[1] = a2[0] = 0; + expect(jasmineUnderTest.matchersUtil.equals(a1, a2)).toBe(false); + }); it( - 'fails for ' + - TypedArrayCtor.name + - 's with same length but different content', + 'fails for ' + typeName + 's with same length but different content', function() { - jasmine.getEnv().requireFunctioningTypedArrays(); + var TypedArrayCtor = requireType(); var a1 = new TypedArrayCtor(1); var a2 = new TypedArrayCtor(1); a1[0] = 0; @@ -966,8 +969,8 @@ describe('matchersUtil', function() { } ); - it('checks nonstandard properties', function() { - jasmine.getEnv().requireFunctioningTypedArrays(); + it('checks nonstandard properties of ' + typeName, function() { + var TypedArrayCtor = requireType(); var a1 = new TypedArrayCtor(1); var a2 = new TypedArrayCtor(1); a1[0] = a2[0] = 0; @@ -975,8 +978,8 @@ describe('matchersUtil', function() { expect(jasmineUnderTest.matchersUtil.equals(a1, a2)).toBe(false); }); - it('works with custom equality testers', function() { - jasmine.getEnv().requireFunctioningTypedArrays(); + it('works with custom equality testers with ' + typeName, function() { + var TypedArrayCtor = requireType(); var a1 = new TypedArrayCtor(1); var a2 = new TypedArrayCtor(1); var matchersUtil = new jasmineUnderTest.MatchersUtil({ From 0aee81cfb9b79b2bdee1a6cbc435caaa29594076 Mon Sep 17 00:00:00 2001 From: Steve Gravrock Date: Sat, 5 Jun 2021 13:31:26 -0700 Subject: [PATCH 06/23] Remvoed checks for typed array support in the test suite All supported browsers have all typed arrays except for Uint8ClampedArray, BigInt64Array, and BigUint64Array. --- spec/core/asymmetric_equality/AnySpec.js | 4 +--- spec/core/asymmetric_equality/AnythingSpec.js | 2 -- spec/core/asymmetric_equality/EmptySpec.js | 1 - spec/core/asymmetric_equality/NotEmptySpec.js | 1 - spec/core/matchers/matchersUtilSpec.js | 2 -- spec/core/matchers/toEqualSpec.js | 2 -- spec/helpers/checkForArrayBuffer.js | 1 - spec/helpers/checkForTypedArrays.js | 24 ------------------- spec/support/jasmine-browser.js | 1 - spec/support/jasmine.json | 1 - 10 files changed, 1 insertion(+), 38 deletions(-) delete mode 100644 spec/helpers/checkForTypedArrays.js diff --git a/spec/core/asymmetric_equality/AnySpec.js b/spec/core/asymmetric_equality/AnySpec.js index 0903cf84..4db56f3b 100644 --- a/spec/core/asymmetric_equality/AnySpec.js +++ b/spec/core/asymmetric_equality/AnySpec.js @@ -46,9 +46,7 @@ describe('Any', function() { }); it('matches a TypedArray', function() { - jasmine.getEnv().requireFunctioningTypedArrays(); - - var any = new jasmineUnderTest.Any(Uint32Array); // eslint-disable-line compat/compat + var any = new jasmineUnderTest.Any(Uint32Array); expect(any.asymmetricMatch(new Uint32Array([]))).toBe(true); // eslint-disable-line compat/compat }); diff --git a/spec/core/asymmetric_equality/AnythingSpec.js b/spec/core/asymmetric_equality/AnythingSpec.js index 61ac14e5..1463c1a8 100644 --- a/spec/core/asymmetric_equality/AnythingSpec.js +++ b/spec/core/asymmetric_equality/AnythingSpec.js @@ -40,8 +40,6 @@ describe('Anything', function() { }); it('matches a TypedArray', function() { - jasmine.getEnv().requireFunctioningTypedArrays(); - var anything = new jasmineUnderTest.Anything(); expect(anything.asymmetricMatch(new Uint32Array([]))).toBe(true); // eslint-disable-line compat/compat diff --git a/spec/core/asymmetric_equality/EmptySpec.js b/spec/core/asymmetric_equality/EmptySpec.js index 4ef48a86..03d822be 100644 --- a/spec/core/asymmetric_equality/EmptySpec.js +++ b/spec/core/asymmetric_equality/EmptySpec.js @@ -42,7 +42,6 @@ describe('Empty', function() { }); it('matches an empty typed array', function() { - jasmine.getEnv().requireFunctioningTypedArrays(); var empty = new jasmineUnderTest.Empty(); expect(empty.asymmetricMatch(new Int16Array())).toBe(true); // eslint-disable-line compat/compat diff --git a/spec/core/asymmetric_equality/NotEmptySpec.js b/spec/core/asymmetric_equality/NotEmptySpec.js index fdb0a99e..82bcf014 100644 --- a/spec/core/asymmetric_equality/NotEmptySpec.js +++ b/spec/core/asymmetric_equality/NotEmptySpec.js @@ -44,7 +44,6 @@ describe('NotEmpty', function() { }); it('matches a non empty typed array', function() { - jasmine.getEnv().requireFunctioningTypedArrays(); var notEmpty = new jasmineUnderTest.NotEmpty(); expect(notEmpty.asymmetricMatch(new Int16Array([1, 2, 3]))).toBe(true); // eslint-disable-line compat/compat diff --git a/spec/core/matchers/matchersUtilSpec.js b/spec/core/matchers/matchersUtilSpec.js index 627deddc..369f6b5a 100644 --- a/spec/core/matchers/matchersUtilSpec.js +++ b/spec/core/matchers/matchersUtilSpec.js @@ -893,7 +893,6 @@ describe('matchersUtil', function() { }); it('fails for ArrayBuffers with same length but different content', function() { - jasmine.getEnv().requireFunctioningTypedArrays(); jasmine.getEnv().requireFunctioningArrayBuffers(); var buffer1 = new ArrayBuffer(4); // eslint-disable-line compat/compat var buffer2 = new ArrayBuffer(4); // eslint-disable-line compat/compat @@ -906,7 +905,6 @@ describe('matchersUtil', function() { describe('Typed arrays', function() { it('fails for typed arrays of same length and contents but different types', function() { - jasmine.getEnv().requireFunctioningTypedArrays(); // eslint-disable-next-line compat/compat var a1 = new Int8Array(1); // eslint-disable-next-line compat/compat diff --git a/spec/core/matchers/toEqualSpec.js b/spec/core/matchers/toEqualSpec.js index b2fb9f9c..06a4f982 100644 --- a/spec/core/matchers/toEqualSpec.js +++ b/spec/core/matchers/toEqualSpec.js @@ -257,8 +257,6 @@ describe('toEqual', function() { }); it('reports mismatches between arrays of different types', function() { - jasmine.getEnv().requireFunctioningTypedArrays(); - var actual = new Uint32Array([1, 2, 3]), // eslint-disable-line compat/compat expected = new Uint16Array([1, 2, 3]), // eslint-disable-line compat/compat message = diff --git a/spec/helpers/checkForArrayBuffer.js b/spec/helpers/checkForArrayBuffer.js index a04a94e9..bf49a531 100644 --- a/spec/helpers/checkForArrayBuffer.js +++ b/spec/helpers/checkForArrayBuffer.js @@ -17,7 +17,6 @@ } env.requireFunctioningArrayBuffers = function() { - env.requireFunctioningTypedArrays(); if (!hasFunctioningArrayBuffers()) { env.pending('Browser has incomplete or missing support for ArrayBuffer'); } diff --git a/spec/helpers/checkForTypedArrays.js b/spec/helpers/checkForTypedArrays.js deleted file mode 100644 index 6902120b..00000000 --- a/spec/helpers/checkForTypedArrays.js +++ /dev/null @@ -1,24 +0,0 @@ -/* eslint-disable compat/compat */ -(function(env) { - function hasFunctioningTypedArrays() { - if (typeof Uint32Array === 'undefined') { - return false; - } - - try { - var a = new Uint32Array([1, 2, 3]); - if (a.length !== 3) { - return false; - } - return true; - } catch (e) { - return false; - } - } - - env.requireFunctioningTypedArrays = function() { - if (!hasFunctioningTypedArrays()) { - env.pending('Browser has incomplete or missing support for typed arrays'); - } - }; -})(jasmine.getEnv()); diff --git a/spec/support/jasmine-browser.js b/spec/support/jasmine-browser.js index 59560423..4d52f913 100644 --- a/spec/support/jasmine-browser.js +++ b/spec/support/jasmine-browser.js @@ -24,7 +24,6 @@ module.exports = { 'helpers/checkForMap.js', 'helpers/checkForSet.js', 'helpers/checkForSymbol.js', - 'helpers/checkForTypedArrays.js', 'helpers/checkForUrl.js', 'helpers/domHelpers.js', 'helpers/integrationMatchers.js', diff --git a/spec/support/jasmine.json b/spec/support/jasmine.json index b9985275..f87d05dd 100644 --- a/spec/support/jasmine.json +++ b/spec/support/jasmine.json @@ -11,7 +11,6 @@ "helpers/checkForMap.js", "helpers/checkForSet.js", "helpers/checkForSymbol.js", - "helpers/checkForTypedArrays.js", "helpers/checkForUrl.js", "helpers/domHelpers.js", "helpers/integrationMatchers.js", From 4e1f36cbb0e5cfbaf938a8d51570105f604500b6 Mon Sep 17 00:00:00 2001 From: Ikko Ashimine Date: Sat, 12 Jun 2021 10:00:36 +0900 Subject: [PATCH 07/23] Fix typo in SpySpec.js ommitted -> omitted --- spec/core/SpySpec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/core/SpySpec.js b/spec/core/SpySpec.js index f7a2c9ec..97fc9544 100644 --- a/spec/core/SpySpec.js +++ b/spec/core/SpySpec.js @@ -213,7 +213,7 @@ describe('Spies', function() { ).toBe(1); }); - it('allows base name to be ommitted when assigning methods and properties', function() { + it('allows base name to be omitted when assigning methods and properties', function() { var spyObj = env.createSpyObj({ m: 3 }, { p: 4 }); expect(spyObj.m()).toEqual(3); From 6e097528f5aacc6915bc371da05c69eea522fd5b Mon Sep 17 00:00:00 2001 From: Steve Gravrock Date: Sun, 13 Jun 2021 09:45:27 -0700 Subject: [PATCH 08/23] Include only specified files in the NPM package --- .gitignore | 1 + .npmignore | 30 ------------------ package.json | 7 +++++ spec/npmPackage/npmPackageSpec.js | 52 +++++++++++++++++++++++++++++++ 4 files changed, 60 insertions(+), 30 deletions(-) delete mode 100644 .npmignore diff --git a/.gitignore b/.gitignore index 4536a071..644ebecd 100644 --- a/.gitignore +++ b/.gitignore @@ -25,3 +25,4 @@ build/ dist nbproject/ *.iml +.envrc diff --git a/.npmignore b/.npmignore deleted file mode 100644 index 2f53304a..00000000 --- a/.npmignore +++ /dev/null @@ -1,30 +0,0 @@ -dist/ -grunt/ -node_modules -pkg/ -release_notes/ -spec/ -src/ -Gemfile -Gemfile.lock -Rakefile -jasmine-core.gemspec -.bundle/ -.gitignore -.gitmodules -.idea -.jshintrc -.rspec -.sass-cache/ -.circleci -scripts/ -*.sh -*.py -Gruntfile.js -lib/jasmine-core.rb -lib/jasmine-core/boot/ -lib/jasmine-core/spec -lib/jasmine-core/version.rb -lib/jasmine-core/*.py -sauce_connect.log -ci.js diff --git a/package.json b/package.json index da5d4461..9b074f99 100644 --- a/package.json +++ b/package.json @@ -25,6 +25,13 @@ "description": "Official packaging of Jasmine's core files for use by Node.js projects.", "homepage": "https://jasmine.github.io", "main": "./lib/jasmine-core.js", + "files": [ + "MIT.LICENSE", + "README.md", + "images/*.{png,svg}", + "lib/**/*.{js,css}", + "package.json" + ], "devDependencies": { "ejs": "^2.5.5", "eslint": "^6.8.0", diff --git a/spec/npmPackage/npmPackageSpec.js b/spec/npmPackage/npmPackageSpec.js index 70af391b..ada121fa 100644 --- a/spec/npmPackage/npmPackageSpec.js +++ b/spec/npmPackage/npmPackageSpec.js @@ -108,4 +108,56 @@ describe('npm package', function() { false ); }); + + it('does not have any unexpected files in the root directory', function() { + var files = fs.readdirSync(this.tmpDir); + expect(files).toEqual(['package']); + }); + + it('does not have any unexpected files in the package directory', function() { + var files = fs.readdirSync(path.resolve(this.tmpDir, 'package')); + files.sort(); + expect(files).toEqual([ + 'MIT.LICENSE', + 'README.md', + 'images', + 'lib', + 'package.json' + ]); + }); + + it('only has images in the images dir', function() { + var files = fs.readdirSync(path.resolve(this.tmpDir, 'package/images')), + i; + + for (i = 0; i < files.length; i++) { + expect(files[i]).toMatch(/\.(svg|png)$/); + } + }); + + it('only has JS and CSS files in the lib dir', function() { + var files = [], + i; + + function getFiles(dir) { + var dirents = fs.readdirSync(dir, { withFileTypes: true }), + j; + + for (j = 0; j < dirents.length; j++) { + dirent = dirents[j]; + + if (dirent.isDirectory()) { + getFiles(path.resolve(dir, dirent.name)); + } else { + files.push(path.resolve(dir, dirent.name)); + } + } + } + + getFiles(path.resolve(this.tmpDir, 'package/lib')); + + for (i = 0; i < files.length; i++) { + expect(files[i]).toMatch(/\.(js|css)$/); + } + }); }); From 1e4f0d1545c4e0a582391821d1ffa52f544bdfb5 Mon Sep 17 00:00:00 2001 From: Joris Stolwijk Date: Thu, 3 Jun 2021 10:30:36 +0200 Subject: [PATCH 09/23] add url pathName in toQueryString function - fixes (https://github.com/jasmine/jasmine/issues/1906 ) + comment + test --- spec/html/QueryStringSpec.js | 15 +++++++++++++++ src/html/QueryString.js | 5 ++++- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/spec/html/QueryStringSpec.js b/spec/html/QueryStringSpec.js index de2f5721..b9303be2 100644 --- a/spec/html/QueryStringSpec.js +++ b/spec/html/QueryStringSpec.js @@ -48,6 +48,21 @@ describe('QueryString', function() { expect(result).toMatch(/foo=bar/); expect(result).toMatch(/baz=quux/); }); + + it('includes url pathname with the query string including the given key/value pair', function() { + var windowLocation = { + pathname: 'debug.html', + search: '?foo=bar' + }, + queryString = new jasmineUnderTest.QueryString({ + getWindowLocation: function() { + return windowLocation; + } + }); + + var result = queryString.fullStringWithNewParam('baz', 'quux'); + expect(result).toBe('debug.html?foo=bar&baz=quux'); + }); }); describe('#getParam', function() { diff --git a/src/html/QueryString.js b/src/html/QueryString.js index c4ce950e..736a691c 100644 --- a/src/html/QueryString.js +++ b/src/html/QueryString.js @@ -26,7 +26,10 @@ jasmineRequire.QueryString = function() { encodeURIComponent(prop) + '=' + encodeURIComponent(paramMap[prop]) ); } - return '?' + qStrPairs.join('&'); + // include getWindowLocation() to fix issue with karma-jasmine-html-reporter in angular: see https://github.com/jasmine/jasmine/issues/1906 + return ( + (options.getWindowLocation().pathname || '') + '?' + qStrPairs.join('&') + ); } function queryStringToParamMap() { From 095745ab12144ee27147d962d690cfd49104e7fa Mon Sep 17 00:00:00 2001 From: Steve Gravrock Date: Mon, 21 Jun 2021 13:31:28 -0700 Subject: [PATCH 10/23] Removed gulp-jasmine-browser mention from README --- README.md | 3 --- 1 file changed, 3 deletions(-) diff --git a/README.md b/README.md index 2881cbb7..f26bb82c 100644 --- a/README.md +++ b/README.md @@ -26,9 +26,6 @@ For the Jasmine browser runner:
For the Jasmine Ruby Gem:
[https://github.com/jasmine/jasmine-gem](https://github.com/jasmine/jasmine-gem). -For the Jasmine headless browser gulp plugin:
-[https://github.com/jasmine/gulp-jasmine-browser](https://github.com/jasmine/gulp-jasmine-browser). - To install Jasmine standalone on your local box (where **_{#.#.#}_** below is substituted by the release number downloaded): * Download the standalone distribution for your desired release from the [releases page](https://github.com/jasmine/jasmine/releases). From 1893bf6c16cdefe85f0405e0e593eb53b5187f9f Mon Sep 17 00:00:00 2001 From: Steve Gravrock Date: Mon, 21 Jun 2021 14:09:14 -0700 Subject: [PATCH 11/23] Added jsdocs for asymmetric equality testers --- lib/jasmine-core/jasmine.js | 25 +++++++++++++++++++++++++ src/core/matchers/matchersUtil.js | 25 +++++++++++++++++++++++++ 2 files changed, 50 insertions(+) diff --git a/lib/jasmine-core/jasmine.js b/lib/jasmine-core/jasmine.js index e7eda7f7..07a14493 100644 --- a/lib/jasmine-core/jasmine.js +++ b/lib/jasmine-core/jasmine.js @@ -5388,6 +5388,31 @@ getJasmineRequireObj().MatchersUtil = function(j$) { return MatchersUtil; }; +/** + * @interface AsymmetricEqualityTester + * @classdesc An asymmetric equality tester is an object that can match multiple + * objects. Examples include jasmine.any() and jasmine.stringMatching(). + * User-defined asymmetric equality testers can also be defined and used in + * expectations. + * @see custom_asymmetric_equality_testers + * @since 2.0.0 + */ +/** + * Determines whether a value matches this tester + * @function + * @name AsymmetricEqualityTester#asymmetricMatch + * @param value {any} The value to test + * @param matchersUtil {MatchersUtil} utilities for testing equality, etc + * @return {Boolean} + */ +/** + * Returns a string representation of this tester to use in matcher failure messages + * @function + * @name AsymmetricEqualityTester#jasmineToString + * @param pp {function} Function that takes a value and returns a pretty-printed representation + * @return {String} + */ + getJasmineRequireObj().MismatchTree = function(j$) { /* To be able to apply custom object formatters at all possible levels of an diff --git a/src/core/matchers/matchersUtil.js b/src/core/matchers/matchersUtil.js index 8ff30593..05fedac8 100644 --- a/src/core/matchers/matchersUtil.js +++ b/src/core/matchers/matchersUtil.js @@ -655,3 +655,28 @@ getJasmineRequireObj().MatchersUtil = function(j$) { return MatchersUtil; }; + +/** + * @interface AsymmetricEqualityTester + * @classdesc An asymmetric equality tester is an object that can match multiple + * objects. Examples include jasmine.any() and jasmine.stringMatching(). + * User-defined asymmetric equality testers can also be defined and used in + * expectations. + * @see custom_asymmetric_equality_testers + * @since 2.0.0 + */ +/** + * Determines whether a value matches this tester + * @function + * @name AsymmetricEqualityTester#asymmetricMatch + * @param value {any} The value to test + * @param matchersUtil {MatchersUtil} utilities for testing equality, etc + * @return {Boolean} + */ +/** + * Returns a string representation of this tester to use in matcher failure messages + * @function + * @name AsymmetricEqualityTester#jasmineToString + * @param pp {function} Function that takes a value and returns a pretty-printed representation + * @return {String} + */ From 163f93d6ffe905a7d2f907f83cea934706b69e64 Mon Sep 17 00:00:00 2001 From: Steve Gravrock Date: Mon, 21 Jun 2021 14:19:31 -0700 Subject: [PATCH 12/23] Removed constructors from jsdocs of classes that aren't user-constructable --- lib/jasmine-core/jasmine.js | 29 +++++++++++++++++------------ src/core/Clock.js | 6 ++++-- src/core/Env.js | 9 +++++---- src/core/Spy.js | 8 +++++--- src/core/matchers/matchersUtil.js | 6 +++--- 5 files changed, 34 insertions(+), 24 deletions(-) diff --git a/lib/jasmine-core/jasmine.js b/lib/jasmine-core/jasmine.js index 07a14493..9fd69021 100644 --- a/lib/jasmine-core/jasmine.js +++ b/lib/jasmine-core/jasmine.js @@ -979,11 +979,12 @@ getJasmineRequireObj().Order = function() { getJasmineRequireObj().Env = function(j$) { /** - * _Note:_ Do not construct this directly, Jasmine will make one during booting. - * @name Env + * @class Env * @since 2.0.0 - * @classdesc The Jasmine environment - * @constructor + * @classdesc The Jasmine environment.
+ * _Note:_ Do not construct this directly. You can obtain the Env instance by + * calling {@link jasmine.getEnv}. + * @hideconstructor */ function Env(options) { options = options || {}; @@ -3165,9 +3166,11 @@ getJasmineRequireObj().Clock = function() { typeof process.versions.node === 'string'; /** - * _Note:_ Do not construct this directly, Jasmine will make one during booting. You can get the current clock with {@link jasmine.clock}. * @class Clock - * @classdesc Jasmine's mock clock is used when testing time dependent code. + * @classdesc Jasmine's mock clock is used when testing time dependent code.
+ * _Note:_ Do not construct this directly. You can get the current clock with + * {@link jasmine.clock}. + * @hideconstructor */ function Clock(global, delayedFunctionSchedulerFactory, mockDate) { var self = this, @@ -4734,11 +4737,11 @@ getJasmineRequireObj().MatchersUtil = function(j$) { // TODO: convert all uses of j$.pp to use the injected pp /** + * @class MatchersUtil + * @classdesc Utilities for use in implementing matchers.
* _Note:_ Do not construct this directly. Jasmine will construct one and * pass it to matchers and asymmetric equality testers. - * @name MatchersUtil - * @classdesc Utilities for use in implementing matchers - * @constructor + * @hideconstructor */ function MatchersUtil(options) { options = options || {}; @@ -8291,9 +8294,11 @@ getJasmineRequireObj().Spy = function(j$) { }); /** - * _Note:_ Do not construct this directly, use {@link spyOn}, {@link spyOnProperty}, {@link jasmine.createSpy}, or {@link jasmine.createSpyObj} - * @constructor - * @name Spy + * @classdesc _Note:_ Do not construct this directly. Use {@link spyOn}, + * {@link spyOnProperty}, {@link jasmine.createSpy}, or + * {@link jasmine.createSpyObj} instead. + * @class Spy + * @hideconstructor */ function Spy( name, diff --git a/src/core/Clock.js b/src/core/Clock.js index 0ec89e23..30c1ca7a 100644 --- a/src/core/Clock.js +++ b/src/core/Clock.js @@ -6,9 +6,11 @@ getJasmineRequireObj().Clock = function() { typeof process.versions.node === 'string'; /** - * _Note:_ Do not construct this directly, Jasmine will make one during booting. You can get the current clock with {@link jasmine.clock}. * @class Clock - * @classdesc Jasmine's mock clock is used when testing time dependent code. + * @classdesc Jasmine's mock clock is used when testing time dependent code.
+ * _Note:_ Do not construct this directly. You can get the current clock with + * {@link jasmine.clock}. + * @hideconstructor */ function Clock(global, delayedFunctionSchedulerFactory, mockDate) { var self = this, diff --git a/src/core/Env.js b/src/core/Env.js index 741f0e16..4d775d08 100644 --- a/src/core/Env.js +++ b/src/core/Env.js @@ -1,10 +1,11 @@ getJasmineRequireObj().Env = function(j$) { /** - * _Note:_ Do not construct this directly, Jasmine will make one during booting. - * @name Env + * @class Env * @since 2.0.0 - * @classdesc The Jasmine environment - * @constructor + * @classdesc The Jasmine environment.
+ * _Note:_ Do not construct this directly. You can obtain the Env instance by + * calling {@link jasmine.getEnv}. + * @hideconstructor */ function Env(options) { options = options || {}; diff --git a/src/core/Spy.js b/src/core/Spy.js index 03ed46a3..877673d4 100644 --- a/src/core/Spy.js +++ b/src/core/Spy.js @@ -13,9 +13,11 @@ getJasmineRequireObj().Spy = function(j$) { }); /** - * _Note:_ Do not construct this directly, use {@link spyOn}, {@link spyOnProperty}, {@link jasmine.createSpy}, or {@link jasmine.createSpyObj} - * @constructor - * @name Spy + * @classdesc _Note:_ Do not construct this directly. Use {@link spyOn}, + * {@link spyOnProperty}, {@link jasmine.createSpy}, or + * {@link jasmine.createSpyObj} instead. + * @class Spy + * @hideconstructor */ function Spy( name, diff --git a/src/core/matchers/matchersUtil.js b/src/core/matchers/matchersUtil.js index 05fedac8..8d77cb3f 100644 --- a/src/core/matchers/matchersUtil.js +++ b/src/core/matchers/matchersUtil.js @@ -2,11 +2,11 @@ getJasmineRequireObj().MatchersUtil = function(j$) { // TODO: convert all uses of j$.pp to use the injected pp /** + * @class MatchersUtil + * @classdesc Utilities for use in implementing matchers.
* _Note:_ Do not construct this directly. Jasmine will construct one and * pass it to matchers and asymmetric equality testers. - * @name MatchersUtil - * @classdesc Utilities for use in implementing matchers - * @constructor + * @hideconstructor */ function MatchersUtil(options) { options = options || {}; From 5862b22aefd3229d70639526a796b3cfd4e40fd8 Mon Sep 17 00:00:00 2001 From: Steve Gravrock Date: Mon, 21 Jun 2021 16:43:51 -0700 Subject: [PATCH 13/23] Include rejection details in failure messages for toBeResolved and toBeResolvedWith [#178559119] --- lib/jasmine-core/jasmine.js | 21 ++++++++++++++----- spec/core/AsyncExpectationSpec.js | 10 +++++---- spec/core/matchers/async/toBeResolvedSpec.js | 13 +++++++++--- .../core/matchers/async/toBeResolvedToSpec.js | 5 +++-- src/core/matchers/async/toBeResolved.js | 13 +++++++++--- src/core/matchers/async/toBeResolvedTo.js | 8 +++++-- 6 files changed, 51 insertions(+), 19 deletions(-) diff --git a/lib/jasmine-core/jasmine.js b/lib/jasmine-core/jasmine.js index 9fd69021..41b88f0b 100644 --- a/lib/jasmine-core/jasmine.js +++ b/lib/jasmine-core/jasmine.js @@ -4535,7 +4535,7 @@ getJasmineRequireObj().toBeResolved = function(j$) { * @example * return expectAsync(aPromise).toBeResolved(); */ - return function toBeResolved() { + return function toBeResolved(matchersUtil) { return { compare: function(actual) { if (!j$.isPromiseLike(actual)) { @@ -4546,8 +4546,15 @@ getJasmineRequireObj().toBeResolved = function(j$) { function() { return { pass: true }; }, - function() { - return { pass: false }; + function(e) { + return { + pass: false, + message: + 'Expected a promise to be resolved but it was ' + + 'rejected with ' + + matchersUtil.pp(e) + + '.' + }; } ); } @@ -4602,10 +4609,14 @@ getJasmineRequireObj().toBeResolvedTo = function(j$) { }; } }, - function() { + function(e) { return { pass: false, - message: prefix(false) + ' but it was rejected.' + message: + prefix(false) + + ' but it was rejected with ' + + matchersUtil.pp(e) + + '.' }; } ); diff --git a/spec/core/AsyncExpectationSpec.js b/spec/core/AsyncExpectationSpec.js index 94e2438c..8772a9c8 100644 --- a/spec/core/AsyncExpectationSpec.js +++ b/spec/core/AsyncExpectationSpec.js @@ -96,8 +96,8 @@ describe('AsyncExpectation', function() { jasmine.getEnv().requirePromises(); var matchersUtil = { - buildFailureMessage: function() { - return 'failure message'; + pp: function(val) { + return val.toString(); } }, addExpectationResult = jasmine.createSpy('addExpectationResult'), @@ -114,7 +114,8 @@ describe('AsyncExpectation', function() { expect(addExpectationResult).toHaveBeenCalledWith( false, jasmine.objectContaining({ - message: 'Some context: failure message' + message: + 'Some context: Expected a promise to be resolved but it was rejected with rejected.' }) ); }); @@ -144,7 +145,8 @@ describe('AsyncExpectation', function() { false, jasmine.objectContaining({ message: - "Some context: Expected a promise to be resolved to 'a' but it was rejected." + "Some context: Expected a promise to be resolved to 'a' " + + "but it was rejected with 'b'." }) ); }); diff --git a/spec/core/matchers/async/toBeResolvedSpec.js b/spec/core/matchers/async/toBeResolvedSpec.js index 668b1bb3..b90a537d 100644 --- a/spec/core/matchers/async/toBeResolvedSpec.js +++ b/spec/core/matchers/async/toBeResolvedSpec.js @@ -15,12 +15,19 @@ describe('toBeResolved', function() { it('fails if the actual is rejected', function() { jasmine.getEnv().requirePromises(); - var matchersUtil = new jasmineUnderTest.MatchersUtil(), + var matchersUtil = new jasmineUnderTest.MatchersUtil({ + pp: jasmineUnderTest.makePrettyPrinter([]) + }), matcher = jasmineUnderTest.asyncMatchers.toBeResolved(matchersUtil), - actual = Promise.reject('AsyncExpectationSpec rejection'); + actual = Promise.reject(new Error('AsyncExpectationSpec rejection')); return matcher.compare(actual).then(function(result) { - expect(result).toEqual(jasmine.objectContaining({ pass: false })); + expect(result).toEqual({ + pass: false, + message: + 'Expected a promise to be resolved but it was rejected ' + + 'with Error: AsyncExpectationSpec rejection.' + }); }); }); diff --git a/spec/core/matchers/async/toBeResolvedToSpec.js b/spec/core/matchers/async/toBeResolvedToSpec.js index 459e93ac..734b780e 100644 --- a/spec/core/matchers/async/toBeResolvedToSpec.js +++ b/spec/core/matchers/async/toBeResolvedToSpec.js @@ -19,14 +19,15 @@ describe('#toBeResolvedTo', function() { pp: jasmineUnderTest.makePrettyPrinter() }), matcher = jasmineUnderTest.asyncMatchers.toBeResolvedTo(matchersUtil), - actual = Promise.reject('AsyncExpectationSpec error'); + actual = Promise.reject(new Error('AsyncExpectationSpec error')); return matcher.compare(actual, '').then(function(result) { expect(result).toEqual( jasmine.objectContaining({ pass: false, message: - "Expected a promise to be resolved to '' but it was rejected." + "Expected a promise to be resolved to '' but it was rejected " + + 'with Error: AsyncExpectationSpec error.' }) ); }); diff --git a/src/core/matchers/async/toBeResolved.js b/src/core/matchers/async/toBeResolved.js index b9dda27a..0ef192be 100644 --- a/src/core/matchers/async/toBeResolved.js +++ b/src/core/matchers/async/toBeResolved.js @@ -10,7 +10,7 @@ getJasmineRequireObj().toBeResolved = function(j$) { * @example * return expectAsync(aPromise).toBeResolved(); */ - return function toBeResolved() { + return function toBeResolved(matchersUtil) { return { compare: function(actual) { if (!j$.isPromiseLike(actual)) { @@ -21,8 +21,15 @@ getJasmineRequireObj().toBeResolved = function(j$) { function() { return { pass: true }; }, - function() { - return { pass: false }; + function(e) { + return { + pass: false, + message: + 'Expected a promise to be resolved but it was ' + + 'rejected with ' + + matchersUtil.pp(e) + + '.' + }; } ); } diff --git a/src/core/matchers/async/toBeResolvedTo.js b/src/core/matchers/async/toBeResolvedTo.js index f95e7b10..235c3a22 100644 --- a/src/core/matchers/async/toBeResolvedTo.js +++ b/src/core/matchers/async/toBeResolvedTo.js @@ -45,10 +45,14 @@ getJasmineRequireObj().toBeResolvedTo = function(j$) { }; } }, - function() { + function(e) { return { pass: false, - message: prefix(false) + ' but it was rejected.' + message: + prefix(false) + + ' but it was rejected with ' + + matchersUtil.pp(e) + + '.' }; } ); From dbc1a0aa56c574b8fe6ecbf032e15490497d5f07 Mon Sep 17 00:00:00 2001 From: Steve Gravrock Date: Wed, 23 Jun 2021 20:13:01 -0700 Subject: [PATCH 14/23] Added expectAsync(...).already * Causes async matchers to immediately fail if the promise is pending * Fixes #1845 --- lib/jasmine-core/jasmine.js | 57 ++++++++++++++++++ spec/core/baseSpec.js | 29 +++++++++ spec/core/integration/MatchersSpec.js | 85 +++++++++++++++++++++++++++ src/core/Expectation.js | 44 ++++++++++++++ src/core/base.js | 13 ++++ 5 files changed, 228 insertions(+) diff --git a/lib/jasmine-core/jasmine.js b/lib/jasmine-core/jasmine.js index 41b88f0b..1ad0935a 100644 --- a/lib/jasmine-core/jasmine.js +++ b/lib/jasmine-core/jasmine.js @@ -371,6 +371,19 @@ getJasmineRequireObj().base = function(j$, jasmineGlobal) { return matches ? matches[1] : ''; }; + j$.isPending_ = function(promise) { + var sentinel = {}; + // eslint-disable-next-line compat/compat + return Promise.race([promise, Promise.resolve(sentinel)]).then( + function(result) { + return result === sentinel; + }, + function() { + return false; + } + ); + }; + /** * 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 being compared is an instance of the specified class/constructor. @@ -3729,6 +3742,12 @@ getJasmineRequireObj().Expectation = function(j$) { * Asynchronous matchers that operate on an actual value which is a promise, * and return a promise. * + * Most async matchers will wait indefinitely for the promise to be resolved + * or rejected, resulting in a spec timeout if that never happens. If you + * expect that the promise will already be resolved or rejected at the time + * the matcher is called, you can use the {@link async-matchers#already} + * modifier to get a faster failure with a more helpful message. + * * Note: Specs must await the result of each async matcher, return the * promise returned by the matcher, or return a promise that's derived from * the one returned by the matcher. Otherwise the matcher will not be @@ -3798,6 +3817,23 @@ getJasmineRequireObj().Expectation = function(j$) { } }); + /** + * Fail as soon as possible if the actual is pending. + * Otherwise evaluate the matcher. + * @member + * @name async-matchers#already + * @type {async-matchers} + * @example + * await expectAsync(myPromise).already.toBeResolved(); + * @example + * return expectAsync(myPromise).already.toBeResolved(); + */ + Object.defineProperty(AsyncExpectation.prototype, 'already', { + get: function() { + return addFilter(this, expectSettledPromiseFilter); + } + }); + function wrapSyncCompare(name, matcherFactory) { return function() { var result = this.expector.compare(name, matcherFactory, arguments); @@ -3876,6 +3912,27 @@ getJasmineRequireObj().Expectation = function(j$) { buildFailureMessage: negatedFailureMessage }; + var expectSettledPromiseFilter = { + selectComparisonFunc: function(matcher) { + return function(actual) { + var matcherArgs = arguments; + + return j$.isPending_(actual).then(function(isPending) { + if (isPending) { + return { + pass: false, + message: + 'Expected a promise to be settled (via ' + + 'expectAsync(...).already) but it was pending.' + }; + } else { + return matcher.compare.apply(null, matcherArgs); + } + }); + }; + } + }; + function ContextAddingFilter(message) { this.message = message; } diff --git a/spec/core/baseSpec.js b/spec/core/baseSpec.js index 99a0e668..51b90063 100644 --- a/spec/core/baseSpec.js +++ b/spec/core/baseSpec.js @@ -75,4 +75,33 @@ describe('base helpers', function() { expect(jasmineUnderTest.isURL({})).toBe(false); }); }); + + describe('isPending_', function() { + it('returns a promise that resolves to true when the promise is pending', function() { + jasmine.getEnv().requirePromises(); + // eslint-disable-next-line compat/compat + var promise = new Promise(function() {}); + return expectAsync(jasmineUnderTest.isPending_(promise)).toBeResolvedTo( + true + ); + }); + + it('returns a promise that resolves to false when the promise is resolved', function() { + jasmine.getEnv().requirePromises(); + // eslint-disable-next-line compat/compat + var promise = Promise.resolve(); + return expectAsync(jasmineUnderTest.isPending_(promise)).toBeResolvedTo( + false + ); + }); + + it('returns a promise that resolves to false when the promise is rejected', function() { + jasmine.getEnv().requirePromises(); + // eslint-disable-next-line compat/compat + var promise = Promise.reject(); + return expectAsync(jasmineUnderTest.isPending_(promise)).toBeResolvedTo( + false + ); + }); + }); }); diff --git a/spec/core/integration/MatchersSpec.js b/spec/core/integration/MatchersSpec.js index ac9f5e90..86fe77c4 100644 --- a/spec/core/integration/MatchersSpec.js +++ b/spec/core/integration/MatchersSpec.js @@ -754,4 +754,89 @@ describe('Matchers (Integration)', function() { 'a predicate, but it threw Error with message |nope|.' }); }); + + describe('When an async matcher is used with .already()', function() { + it('propagates the matcher result when the promise is resolved', function(done) { + jasmine.getEnv().requirePromises(); + + env.it('a spec', function() { + // eslint-disable-next-line compat/compat + return env.expectAsync(Promise.resolve()).already.toBeRejected(); + }); + + var specExpectations = function(result) { + expect(result.status).toEqual('failed'); + expect(result.failedExpectations.length) + .withContext('Number of failed expectations') + .toEqual(1); + expect(result.failedExpectations[0].message).toEqual( + 'Expected [object Promise] to be rejected.' + ); + expect(result.failedExpectations[0].matcherName) + .withContext('Matcher name') + .not.toEqual(''); + }; + + env.addReporter({ specDone: specExpectations }); + env.execute(null, done); + }); + + it('propagates the matcher result when the promise is rejected', function(done) { + jasmine.getEnv().requirePromises(); + + env.it('a spec', function() { + return ( + env + // eslint-disable-next-line compat/compat + .expectAsync(Promise.reject(new Error('nope'))) + .already.toBeResolved() + ); + }); + + var specExpectations = function(result) { + expect(result.status).toEqual('failed'); + expect(result.failedExpectations.length) + .withContext('Number of failed expectations') + .toEqual(1); + expect(result.failedExpectations[0].message).toEqual( + 'Expected a promise to be resolved but it was ' + + 'rejected with Error: nope.' + ); + expect(result.failedExpectations[0].matcherName) + .withContext('Matcher name') + .not.toEqual(''); + }; + + env.addReporter({ specDone: specExpectations }); + env.execute(null, done); + }); + + it('fails when the promise is pending', function(done) { + jasmine.getEnv().requirePromises(); + + // eslint-disable-next-line compat/compat + var promise = new Promise(function() {}); + + env.it('a spec', function() { + return env.expectAsync(promise).already.toBeResolved(); + }); + + var specExpectations = function(result) { + expect(result.status).toEqual('failed'); + expect(result.failedExpectations.length) + .withContext('Number of failed expectations') + .toEqual(1); + expect(result.failedExpectations[0].message).toEqual( + 'Expected a promise to be settled ' + + '(via expectAsync(...).already) but it was pending.' + ); + expect(result.failedExpectations[0].matcherName) + .withContext('Matcher name') + .not.toEqual(''); + }; + + env.addReporter({ specDone: specExpectations }); + env.execute(null, done); + }); + }); }); diff --git a/src/core/Expectation.js b/src/core/Expectation.js index fb2bcf9e..0309ec34 100644 --- a/src/core/Expectation.js +++ b/src/core/Expectation.js @@ -46,6 +46,12 @@ getJasmineRequireObj().Expectation = function(j$) { * Asynchronous matchers that operate on an actual value which is a promise, * and return a promise. * + * Most async matchers will wait indefinitely for the promise to be resolved + * or rejected, resulting in a spec timeout if that never happens. If you + * expect that the promise will already be resolved or rejected at the time + * the matcher is called, you can use the {@link async-matchers#already} + * modifier to get a faster failure with a more helpful message. + * * Note: Specs must await the result of each async matcher, return the * promise returned by the matcher, or return a promise that's derived from * the one returned by the matcher. Otherwise the matcher will not be @@ -115,6 +121,23 @@ getJasmineRequireObj().Expectation = function(j$) { } }); + /** + * Fail as soon as possible if the actual is pending. + * Otherwise evaluate the matcher. + * @member + * @name async-matchers#already + * @type {async-matchers} + * @example + * await expectAsync(myPromise).already.toBeResolved(); + * @example + * return expectAsync(myPromise).already.toBeResolved(); + */ + Object.defineProperty(AsyncExpectation.prototype, 'already', { + get: function() { + return addFilter(this, expectSettledPromiseFilter); + } + }); + function wrapSyncCompare(name, matcherFactory) { return function() { var result = this.expector.compare(name, matcherFactory, arguments); @@ -193,6 +216,27 @@ getJasmineRequireObj().Expectation = function(j$) { buildFailureMessage: negatedFailureMessage }; + var expectSettledPromiseFilter = { + selectComparisonFunc: function(matcher) { + return function(actual) { + var matcherArgs = arguments; + + return j$.isPending_(actual).then(function(isPending) { + if (isPending) { + return { + pass: false, + message: + 'Expected a promise to be settled (via ' + + 'expectAsync(...).already) but it was pending.' + }; + } else { + return matcher.compare.apply(null, matcherArgs); + } + }); + }; + } + }; + function ContextAddingFilter(message) { this.message = message; } diff --git a/src/core/base.js b/src/core/base.js index 3c1da322..c40c6559 100644 --- a/src/core/base.js +++ b/src/core/base.js @@ -203,6 +203,19 @@ getJasmineRequireObj().base = function(j$, jasmineGlobal) { return matches ? matches[1] : ''; }; + j$.isPending_ = function(promise) { + var sentinel = {}; + // eslint-disable-next-line compat/compat + return Promise.race([promise, Promise.resolve(sentinel)]).then( + function(result) { + return result === sentinel; + }, + function() { + return false; + } + ); + }; + /** * 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 being compared is an instance of the specified class/constructor. From c5bdd79a1e0db580d2a5d18524675bb028f93223 Mon Sep 17 00:00:00 2001 From: Steve Gravrock Date: Sat, 26 Jun 2021 11:21:19 -0700 Subject: [PATCH 15/23] Added TS typings and jasmine-browser-runner to issue template --- .github/ISSUE_TEMPLATE.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index db4e6437..7070a58e 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -3,9 +3,12 @@ - When in doubt, create an issue here. - If you have an issue with the Jasmine docs, file an issue in the docs repo here: https://github.com/jasmine/jasmine.github.io +- If you have an issue with TypeScript typings, start a discussion at + [DefinitelyTpyed](https://github.com/DefinitelyTyped/DefinitelyTyped/discussions/new?category=issues-with-a-types-package) - This repository is for the core Jasmine framework - If you are using a test runner that wraps Jasmine, consider filing an issue with that library if appropriate: - [Jasmine npm](https://github.com/jasmine/jasmine-npm/issues) + - [Jasmine browser runner](https://github.com/jasmine/jasmine-browser/issues) - [Jasmine gem](https://github.com/jasmine/jasmine-gem/issues) - [Jasmine py](https://github.com/jasmine/jasmine-py/issues) - [Gulp Jasmine Browser](https://github.com/jasmine/gulp-jasmine-browser/issues) From ec038273f1a05278fc79632ed48bf57be33d2598 Mon Sep 17 00:00:00 2001 From: Steve Gravrock Date: Sat, 26 Jun 2021 11:33:03 -0700 Subject: [PATCH 16/23] Updated contributing guide * Removed outdated steps * Replaced list of environments with a pointer to the README * Misc copy edits --- .github/CONTRIBUTING.md | 58 ++++++++++++++++++++--------------------- 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 6c961b54..3c8ecdbe 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -1,6 +1,12 @@ # Developing for Jasmine Core -We welcome your contributions! Thanks for helping make Jasmine a better project for everyone. Please review the backlog and discussion lists before starting work. What you're looking for may already have been done. If it hasn't, the community can help make your contribution better. If you want to contribute but don't know what to work on, [issues tagged ready for work](https://github.com/jasmine/jasmine/labels/ready%20for%20work) should have enough detail to get started. +We welcome your contributions! Thanks for helping make Jasmine a better project +for everyone. Please review the backlog and discussion lists before starting +work. What you're looking for may already have been done. If it hasn't, the +community can help make your contribution better. If you want to contribute but +don't know what to work on, +[issues tagged help needed](https://github.com/jasmine/jasmine/labels/help%20needed) +should have enough detail to get started. ## Links @@ -35,8 +41,8 @@ Once you've pushed a feature branch to your forked repo, you're ready to open a * `/spec` contains all of the tests * mirrors the source directory * there are some additional files -* `/dist` contains the standalone distributions as zip files -* `/lib` contains the generated files for distribution as the Jasmine Rubygem and the Python package +* `/lib` contains the compiled copy of Jasmine. This is used to self-test and + distributed as the `jasmine-core` Node, Ruby, and Python packages. ### Self-testing @@ -48,35 +54,23 @@ The tests should always use `jasmineUnderTest` to refer to the objects and funct This file does all of the setup necessary for Jasmine to work. It loads all of the code, creates an `Env`, attaches the global functions, and builds the reporter. It also sets up the execution of the `Env` - for browsers this is in `window.onload`. While the default in `lib` is appropriate for browsers, projects may wish to customize this file. -For example, for Jasmine development there is a different `dev_boot.js` for Jasmine development that does more work. - ### Compatibility -Jasmine supports the following environments: - -* Browsers - * IE10+ - * Edge latest - * Firefox latest, 78, and 68 - * Chrome latest - * Safari 8+ - -* Node.js - * 10 - * 12 - * 14 +Jasmine runs in both Node and browsers, including some older browsers that do +not support the latest JavaScript features. See the README for the list of +currently supported environments. ## Development All source code belongs in `src/`. The `core/` directory contains the bulk of Jasmine's functionality. This code should remain browser- and environment-agnostic. If your feature or fix cannot be, as mentioned above, please degrade gracefully. Any code that depends on a browser (specifically, it expects `window` to be the global or `document` is present) should live in `src/html/`. -### Install Dependencies +### Install Dev Dependencies Jasmine Core relies on Node.js. -To install the Node dependencies, you will need Node.js, Npm, and [Grunt](http://gruntjs.com/), the [grunt-cli](https://github.com/gruntjs/grunt-cli) and ensure that `grunt` is on your path. +To install the Node dependencies, you will need Node.js and npm. - $ npm install --local + $ npm install ...will install all of the node modules locally. Now run @@ -88,12 +82,18 @@ To install the Node dependencies, you will need Node.js, Npm, and [Grunt](http:/ Or, How to make a successful pull request -* _Do not change the public interface_. Lots of projects depend on Jasmine and if you aren't careful you'll break them -* _Be environment agnostic_ - server-side developers are just as important as browser developers -* _Be browser agnostic_ - if you must rely on browser-specific functionality, please write it in a way that degrades gracefully -* _Write specs_ - Jasmine's a testing framework; don't add functionality without test-driving it -* _Write code in the style of the rest of the repo_ - Jasmine should look like a cohesive whole -* _Ensure the *entire* test suite is green_ in all the big browsers, Node, and ESLint - your contribution shouldn't break Jasmine for other users +* _Do not change the public interface_. Lots of projects depend on Jasmine and + if you aren't careful you'll break them. +* _Be environment agnostic_ - server-side developers are just as important as + browser developers. +* _Be browser agnostic_ - if you must rely on browser-specific functionality, + please write it in a way that degrades gracefully. +* _Write specs_ - Jasmine's a testing framework. Don't add functionality + without test-driving it. +* _Write code in the style of the rest of the repo_ - Jasmine should look like + a cohesive whole. +* _Ensure the *entire* test suite is green_ in all the big browsers, Node, and + ESLint. Your contribution shouldn't break Jasmine for other users. Follow these tips and your pull request, patch, or suggestion is much more likely to be integrated. @@ -123,9 +123,9 @@ The easiest way to run the tests in **Internet Explorer** is to run a VM that ha ## Before Committing or Submitting a Pull Request -1. Ensure all specs are green in browser *and* node +1. Ensure all specs are green in browser *and* node. 1. Ensure eslint and prettier are clean as part of your `npm test` command. You can run `npm run cleanup` to have prettier re-write the files. -1. Build `jasmine.js` with `npm run build` and run all specs again - this ensures that your changes self-test well +1. Build `jasmine.js` with `npm run build` and run all specs again - this ensures that your changes self-test well. 1. Revert your changes to `jasmine.js` and `jasmine-html.js` * We do this because `jasmine.js` and `jasmine-html.js` are auto-generated (as you've seen in the previous steps) and accepting multiple pull requests when this auto-generated file changes causes lots of headaches * When we accept your pull request, we will generate these files as a separate commit and merge the entire branch into main From ee88ecc61417816f5ebd84edcbb40eb62e4954c9 Mon Sep 17 00:00:00 2001 From: Steve Gravrock Date: Mon, 28 Jun 2021 12:39:36 -0700 Subject: [PATCH 17/23] Revert "add url pathName in toQueryString function - fixes (https://github.com/jasmine/jasmine/issues/1906 ) + comment + test" Reverted because it breaks the option checkboxes in the HTML reporter, both in the standalone distribution and in jasmine-browser-runner. Reopens #1906. This reverts commit 1e4f0d1545c4e0a582391821d1ffa52f544bdfb5. --- lib/jasmine-core/jasmine-html.js | 5 +---- spec/html/QueryStringSpec.js | 15 --------------- src/html/QueryString.js | 5 +---- 3 files changed, 2 insertions(+), 23 deletions(-) diff --git a/lib/jasmine-core/jasmine-html.js b/lib/jasmine-core/jasmine-html.js index 6320c9dc..ebe94049 100644 --- a/lib/jasmine-core/jasmine-html.js +++ b/lib/jasmine-core/jasmine-html.js @@ -810,10 +810,7 @@ jasmineRequire.QueryString = function() { encodeURIComponent(prop) + '=' + encodeURIComponent(paramMap[prop]) ); } - // include getWindowLocation() to fix issue with karma-jasmine-html-reporter in angular: see https://github.com/jasmine/jasmine/issues/1906 - return ( - (options.getWindowLocation().pathname || '') + '?' + qStrPairs.join('&') - ); + return '?' + qStrPairs.join('&'); } function queryStringToParamMap() { diff --git a/spec/html/QueryStringSpec.js b/spec/html/QueryStringSpec.js index b9303be2..de2f5721 100644 --- a/spec/html/QueryStringSpec.js +++ b/spec/html/QueryStringSpec.js @@ -48,21 +48,6 @@ describe('QueryString', function() { expect(result).toMatch(/foo=bar/); expect(result).toMatch(/baz=quux/); }); - - it('includes url pathname with the query string including the given key/value pair', function() { - var windowLocation = { - pathname: 'debug.html', - search: '?foo=bar' - }, - queryString = new jasmineUnderTest.QueryString({ - getWindowLocation: function() { - return windowLocation; - } - }); - - var result = queryString.fullStringWithNewParam('baz', 'quux'); - expect(result).toBe('debug.html?foo=bar&baz=quux'); - }); }); describe('#getParam', function() { diff --git a/src/html/QueryString.js b/src/html/QueryString.js index 736a691c..c4ce950e 100644 --- a/src/html/QueryString.js +++ b/src/html/QueryString.js @@ -26,10 +26,7 @@ jasmineRequire.QueryString = function() { encodeURIComponent(prop) + '=' + encodeURIComponent(paramMap[prop]) ); } - // include getWindowLocation() to fix issue with karma-jasmine-html-reporter in angular: see https://github.com/jasmine/jasmine/issues/1906 - return ( - (options.getWindowLocation().pathname || '') + '?' + qStrPairs.join('&') - ); + return '?' + qStrPairs.join('&'); } function queryStringToParamMap() { From e4c7d8af45b12cee27c25ac0b45e305fa71262c1 Mon Sep 17 00:00:00 2001 From: Joris Stolwijk Date: Tue, 29 Jun 2021 13:55:21 +0200 Subject: [PATCH 18/23] new attempt for fixing #1906: add window.location.pathname to hrefs + comments + fixed tests --- spec/html/HtmlReporterSpec.js | 8 ++++---- src/html/HtmlReporter.js | 23 +++++++++++++++++++---- 2 files changed, 23 insertions(+), 8 deletions(-) diff --git a/spec/html/HtmlReporterSpec.js b/spec/html/HtmlReporterSpec.js index 07b5d59f..0e8e8e3d 100644 --- a/spec/html/HtmlReporterSpec.js +++ b/spec/html/HtmlReporterSpec.js @@ -484,7 +484,7 @@ describe('HtmlReporter', function() { var suiteDetail = outerSuite.childNodes[0]; var suiteLink = suiteDetail.childNodes[0]; expect(suiteLink.innerHTML).toEqual('A Suite'); - expect(suiteLink.getAttribute('href')).toEqual('?foo=bar&spec=A Suite'); + expect(suiteLink.getAttribute('href')).toEqual('/?foo=bar&spec=A Suite'); var specs = outerSuite.childNodes[1]; var spec = specs.childNodes[0]; @@ -494,7 +494,7 @@ describe('HtmlReporter', function() { var specLink = spec.childNodes[0]; expect(specLink.innerHTML).toEqual('with a spec'); expect(specLink.getAttribute('href')).toEqual( - '?foo=bar&spec=A Suite with a spec' + '/?foo=bar&spec=A Suite with a spec' ); }); @@ -1050,7 +1050,7 @@ describe('HtmlReporter', function() { var seedBar = container.querySelector('.jasmine-seed-bar'); expect(seedBar.textContent).toBe(', randomized with seed 424242'); var seedLink = container.querySelector('.jasmine-seed-bar a'); - expect(seedLink.getAttribute('href')).toBe('?seed=424242'); + expect(seedLink.getAttribute('href')).toBe('/?seed=424242'); }); it('should not show the current seed bar if not randomizing', function() { @@ -1099,7 +1099,7 @@ describe('HtmlReporter', function() { reporter.jasmineDone({ order: { random: true } }); var skippedLink = container.querySelector('.jasmine-skipped a'); - expect(skippedLink.getAttribute('href')).toEqual('?foo=bar&spec='); + expect(skippedLink.getAttribute('href')).toEqual('/?foo=bar&spec='); }); }); diff --git a/src/html/HtmlReporter.js b/src/html/HtmlReporter.js index 1c34b260..77e345cb 100644 --- a/src/html/HtmlReporter.js +++ b/src/html/HtmlReporter.js @@ -177,7 +177,10 @@ jasmineRequire.HtmlReporter = function(j$) { ' of ' + totalSpecsDefined + ' specs - run all'; - var skippedLink = addToExistingQueryString('spec', ''); + // include window.location.pathname to fix issue with karma-jasmine-html-reporter in angular: see https://github.com/jasmine/jasmine/issues/1906 + var skippedLink = + (window.location.pathname || '') + + addToExistingQueryString('spec', ''); alert.appendChild( createDom( 'span', @@ -604,7 +607,11 @@ jasmineRequire.HtmlReporter = function(j$) { suite = suite.parent; } - return addToExistingQueryString('spec', els.join(' ')); + // include window.location.pathname to fix issue with karma-jasmine-html-reporter in angular: see https://github.com/jasmine/jasmine/issues/1906 + return ( + (window.location.pathname || '') + + addToExistingQueryString('spec', els.join(' ')) + ); } function addDeprecationWarnings(result, runnableType) { @@ -668,11 +675,19 @@ jasmineRequire.HtmlReporter = function(j$) { } function specHref(result) { - return addToExistingQueryString('spec', result.fullName); + // include window.location.pathname to fix issue with karma-jasmine-html-reporter in angular: see https://github.com/jasmine/jasmine/issues/1906 + return ( + (window.location.pathname || '') + + addToExistingQueryString('spec', result.fullName) + ); } function seedHref(seed) { - return addToExistingQueryString('seed', seed); + // include window.location.pathname to fix issue with karma-jasmine-html-reporter in angular: see https://github.com/jasmine/jasmine/issues/1906 + return ( + (window.location.pathname || '') + + addToExistingQueryString('seed', seed) + ); } function defaultQueryString(key, value) { From 27c650ec08b54f23c5b5d5b97f6e13c009a6c7b3 Mon Sep 17 00:00:00 2001 From: Steve Gravrock Date: Wed, 30 Jun 2021 17:10:51 -0700 Subject: [PATCH 19/23] Updated package description --- package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 9b074f99..51b323f6 100644 --- a/package.json +++ b/package.json @@ -8,6 +8,7 @@ }, "keywords": [ "test", + "testing", "jasmine", "tdd", "bdd" @@ -22,7 +23,7 @@ "ci": "node spec/support/ci.js", "ci:performance": "node spec/support/ci.js jasmine-browser-performance.json" }, - "description": "Official packaging of Jasmine's core files for use by Node.js projects.", + "description": "Simple JavaScript testing framework for browsers and node.js", "homepage": "https://jasmine.github.io", "main": "./lib/jasmine-core.js", "files": [ From 00586e50e018909157c1943b5eff1056f71ae22a Mon Sep 17 00:00:00 2001 From: Steve Gravrock Date: Thu, 1 Jul 2021 17:06:14 -0700 Subject: [PATCH 20/23] Bump version to 3.8.0 --- RELEASE.md | 3 +- lib/jasmine-core/jasmine.js | 2 +- lib/jasmine-core/version.rb | 2 +- package.json | 2 +- release_notes/3.8.0.md | 132 ++++++++++++++++++++++++++++++++++++ 5 files changed, 137 insertions(+), 4 deletions(-) create mode 100644 release_notes/3.8.0.md diff --git a/RELEASE.md b/RELEASE.md index bb527c62..7e3d959b 100644 --- a/RELEASE.md +++ b/RELEASE.md @@ -28,7 +28,7 @@ When jasmine-core revs its major or minor version, the binding libraries should When ready to release - specs are all green and the stories are done: -1. Update the release notes in `release_notes` - use the Anchorman gem to generate the markdown file and edit accordingly +1. Update the release notes in `release_notes` - use the Anchorman gem to generate the markdown file and edit accordingly. Include a list of supported environments. 1. Update the version in `package.json` 1. Run `npm run build`. 1. Copy version to the Ruby gem with `grunt build:copyVersionToGem` @@ -80,6 +80,7 @@ Probably only need to do this when releasing a minor version, and not a patch ve 1. In `package.json`, update both the package version and the jasmine-core dependency version 1. Commit and push. 1. Wait for Circle CI to go green again. +1. Run the tests on Windows locally. 1. `grunt release `. (Note: This will publish the package by running `npm publish`.) #### Gem diff --git a/lib/jasmine-core/jasmine.js b/lib/jasmine-core/jasmine.js index 1ad0935a..40207e6a 100644 --- a/lib/jasmine-core/jasmine.js +++ b/lib/jasmine-core/jasmine.js @@ -9757,5 +9757,5 @@ getJasmineRequireObj().UserContext = function(j$) { }; getJasmineRequireObj().version = function() { - return '3.7.1'; + return '3.8.0'; }; diff --git a/lib/jasmine-core/version.rb b/lib/jasmine-core/version.rb index 9f6452ff..f879e1d5 100644 --- a/lib/jasmine-core/version.rb +++ b/lib/jasmine-core/version.rb @@ -4,6 +4,6 @@ # module Jasmine module Core - VERSION = "3.7.1" + VERSION = "3.8.0" end end diff --git a/package.json b/package.json index 51b323f6..354d5c6f 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "jasmine-core", "license": "MIT", - "version": "3.7.1", + "version": "3.8.0", "repository": { "type": "git", "url": "https://github.com/jasmine/jasmine.git" diff --git a/release_notes/3.8.0.md b/release_notes/3.8.0.md new file mode 100644 index 00000000..b40a13ab --- /dev/null +++ b/release_notes/3.8.0.md @@ -0,0 +1,132 @@ +# Jasmine Core 3.8 Release Notes + +## Summary + +This is a maintenance release of Jasmine with a number of new features and fixes. + +## Python deprecation + +The Jasmine packages for Python are deprecated. We intend to continue releasing +them through the end of the 3.x series, but after that they will be +discontinued. We recommend migrating to the following alternatives: + +* The [jasmine-browser-runner](https://github.com/jasmine/jasmine-browser) + npm package to run specs in browsers, including headless Chrome and + Saucelabs. This is the most direct replacement for the `jasmine server` + and `jasmine ci` commands provided by the `jasmine` Python package. +* The [jasmine](https://github.com/jasmine/jasmine-npm) npm package ( + `npm install jasmine`) to run specs under Node.js. +* The standalone distribution from the + [latest Jasmine release](https://github.com/jasmine/jasmine/releases) to + run specs in browsers with no additional tools. +* The [jasmine-core](https://github.com/jasmine/jasmine) npm package if all + you need is the Jasmine assets. This is the direct equivalent of the + jasmine-core Python package. + + +## New features and bug fixes + +* Fixed spec filtering in Karma + * Merges [#1920](https://github.com/jasmine/jasmine/pull/1920) from @jlpstolwijk + * Fixes [#1906](https://github.com/jasmine/jasmine/issues/1906) + +* Added expectAsync(...).already + * Causes async matchers to immediately fail if the promise is pending + * See https://jasmine.github.io/api/3.8/async-matchers.html#already + * Fixes [#1845](https://github.com/jasmine/jasmine/issues/1845) + +* Include rejection details in failure messages for toBeResolved and toBeResolvedWith + +* Fixed "stop spec on expectation failure" checkbox in standalone + +* Added option for spyOnAllFunctions to include non-enumerable props + * Makes spyOnAllFunctions work on instance methods of ES6 classes + * Merges [#1909](https://github.com/jasmine/jasmine/pull/1909) from @Dari-k + * Fixes [#1897](https://github.com/jasmine/jasmine/issues/1897) + +* Added Spy#calls#thisFor + * Provides the `this` value for a given spy call + * Merges [#1903](https://github.com/jasmine/jasmine/pull/1903) from @ajvincent + +* Improved handling of unhandled promise rejections with no error in Node + * Fixes [#1759](https://github.com/jasmine/jasmine/issues/1759) + + +## Documentation updates + +* Updated package description + +* Updated contributing guide + +* Added TypeScript typings and jasmine-browser-runner to issue template + +* Removed constructors from jsdocs of classes that aren't user-constructable + +* Fixed config.seed type in jsdocs + * Merges [#1892](https://github.com/jasmine/jasmine/pull/1892) from @UziTech + +* Added jsdocs for the following: + * asymmetric equality testers + * Env#execute + * Env#allowRespy + * The public portion of Spec + * Spy.callData.returnValue + * Env#topSuite and Suite + +* Added a jsdoc cross-reference from Configuration to its usage + +* Added a note about correct usage of async matchers + +* Added support for ArrayBuffers to matchersUtil.equals + * Merges [#1891](https://github.com/jasmine/jasmine/pull/1892) from @Finesse + * Merges [#1689](https://github.com/jasmine/jasmine/pull/1892) from @dankurka + * Fixes [#1687](https://github.com/jasmine/jasmine/issues/1687) + + +## Internal notes + +* Fixed typo in spec name + * Merges [#1918](https://github.com/jasmine/jasmine/pull/1918) from @eltociear + +* Specify files to include in the NPM package rather than files to exclude + +* Added test coverage for MatchersUtil#equals with typed arrays + +* Removed checks for typed array support in the test suite + * All supported browsers have all typed arrays except for Uint8ClampedArray, + BigInt64Array, and BigUint64Array. + +* Fixed test failures on IE 10 + +* Test matrix updates + * Added Node 16 + * Added Safari 14 + * Added Firefox 78 (closest match to current ESR) + * Removed Safari 10-12 to speed up CI. The newer and older versions we test + provide a good measure of safety. + +* Replaced node-sass dev dependency that isn't compatible with Node 16 + +* Removed unused dev dependencies + +* Migrated CI from Travis to Circle + +* Compensate for clock jitter in specs + + +## Supported environments + +jasmine-core 3.8.0 has been tested in the following environments. + +| Environment | Supported versions | +|-------------------|--------------------| +| Node | 10, 12, 14, 16 | +| Safari | 8-14 | +| Chrome | 91 | +| Firefox | 89, 68, 78 | +| Edge | 91 | +| Internet Explorer | 10, 11 | + +------ + +_Release Notes generated with _[Anchorman](http://github.com/infews/anchorman)_ From 3e64ce331031391c161cd1aaebd22e947d72986e Mon Sep 17 00:00:00 2001 From: Steve Gravrock Date: Sat, 3 Jul 2021 08:32:55 -0700 Subject: [PATCH 21/23] Removed property tests for MatchersUtil#equals These might be useful for a function with a more restricted domain. But for equals, which accepts two of literally anything, the short run was too short to catch any problems and the long run tended to exceed the CircleCi timeout. --- .circleci/config.yml | 11 ---- package.json | 1 - spec/core/matchers/matchersUtilSpec.js | 72 -------------------------- spec/helpers/requireFastCheck.js | 16 ------ spec/support/jasmine-browser.js | 1 - spec/support/jasmine.json | 1 - 6 files changed, 102 deletions(-) delete mode 100644 spec/helpers/requireFastCheck.js diff --git a/.circleci/config.yml b/.circleci/config.yml index 5eee1065..f2ac69ba 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -58,13 +58,6 @@ jobs: name: Run tests command: npm test - # Warning: Sometimes takes a very long time (>25 minutes) on Circle. - # Probably not a good idea to run it from anything but a nightly. - test_node_with_long_property_tests: - <<: *test_node - environment: - JASMINE_LONG_PROPERTY_TESTS: y - test_browsers: &test_browsers executor: node14 environment: @@ -133,10 +126,6 @@ workflows: name: test_node_16 requires: - build_node_16 - - test_node_with_long_property_tests: - executor: node14 - requires: - - build_node_14 - test_node: executor: node12 name: test_node_12 diff --git a/package.json b/package.json index 354d5c6f..521249e7 100644 --- a/package.json +++ b/package.json @@ -37,7 +37,6 @@ "ejs": "^2.5.5", "eslint": "^6.8.0", "eslint-plugin-compat": "^3.8.0", - "fast-check": "^1.21.0", "fast-glob": "^2.2.6", "grunt": "^1.0.4", "grunt-cli": "^1.3.2", diff --git a/spec/core/matchers/matchersUtilSpec.js b/spec/core/matchers/matchersUtilSpec.js index 369f6b5a..465ac7c5 100644 --- a/spec/core/matchers/matchersUtilSpec.js +++ b/spec/core/matchers/matchersUtilSpec.js @@ -7,78 +7,6 @@ describe('matchersUtil', function() { }); describe('equals', function() { - describe('Properties', function() { - var fc; - - beforeEach(function() { - fc = jasmine.getEnv().requireFastCheck(); - }); - - function basicAnythingSettings() { - return { - key: fc.oneof(fc.string(), fc.constantFrom('k1', 'k2', 'k3')), - // Limiting depth & number of keys allows fast-check to try - // a lot more scalar values. - maxDepth: 2, - maxKeys: 5, - withBoxedValues: true, - withMap: true, - withSet: true - }; - } - - function numRuns() { - var many = 5000000; - - // Be thorough but very slow when specified (usually on CI). - if (process.env.JASMINE_LONG_PROPERTY_TESTS) { - /* eslint-disable-next-line no-console */ - console.log( - 'Using', - many, - 'runs of fc.assert because JASMINE_LONG_PROPERTY_TESTS was set. This may take several minutes.' - ); - return many; - } else { - return undefined; - } - } - - it('is symmetric', function() { - fc.assert( - fc.property( - fc.anything(basicAnythingSettings()), - fc.anything(basicAnythingSettings()), - function(a, b) { - return ( - jasmineUnderTest.matchersUtil.equals(a, b) === - jasmineUnderTest.matchersUtil.equals(b, a) - ); - } - ), - { - numRuns: numRuns(), - examples: [[0, 5e-324]] - } - ); - }); - - it('is reflexive', function() { - var anythingSettings = basicAnythingSettings(); - anythingSettings.withMap = false; - fc.assert( - fc.property(fc.dedup(fc.anything(anythingSettings), 2), function( - values - ) { - return jasmineUnderTest.matchersUtil.equals(values[0], values[1]); - }), - { - numRuns: numRuns() - } - ); - }); - }); - it('passes for literals that are triple-equal', function() { var matchersUtil = new jasmineUnderTest.MatchersUtil(); expect(matchersUtil.equals(null, null)).toBe(true); diff --git a/spec/helpers/requireFastCheck.js b/spec/helpers/requireFastCheck.js deleted file mode 100644 index 140b8e80..00000000 --- a/spec/helpers/requireFastCheck.js +++ /dev/null @@ -1,16 +0,0 @@ -(function(env) { - var NODE_JS = - typeof process !== 'undefined' && - process.versions && - typeof process.versions.node === 'string'; - - env.requireFastCheck = function() { - if (!NODE_JS) { - env.pending( - "Property tests don't run in the browser. Use `npm test` to run them." - ); - } - - return require('fast-check'); - }; -})(jasmine.getEnv()); diff --git a/spec/support/jasmine-browser.js b/spec/support/jasmine-browser.js index 4d52f913..ef717010 100644 --- a/spec/support/jasmine-browser.js +++ b/spec/support/jasmine-browser.js @@ -28,7 +28,6 @@ module.exports = { 'helpers/domHelpers.js', 'helpers/integrationMatchers.js', 'helpers/promises.js', - 'helpers/requireFastCheck.js', 'helpers/defineJasmineUnderTest.js' ], random: true, diff --git a/spec/support/jasmine.json b/spec/support/jasmine.json index f87d05dd..5dc9aad6 100644 --- a/spec/support/jasmine.json +++ b/spec/support/jasmine.json @@ -15,7 +15,6 @@ "helpers/domHelpers.js", "helpers/integrationMatchers.js", "helpers/promises.js", - "helpers/requireFastCheck.js", "helpers/overrideConsoleLogForCircleCi.js", "helpers/nodeDefineJasmineUnderTest.js" ], From 50c88e77741e547a7354b7232e9c1611f8e7591e Mon Sep 17 00:00:00 2001 From: Steve Gravrock Date: Thu, 8 Jul 2021 18:53:21 -0700 Subject: [PATCH 22/23] Mark Env#hideDisabled deprecated in jsdocs --- src/core/Env.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/core/Env.js b/src/core/Env.js index 4d775d08..5ffc52b8 100644 --- a/src/core/Env.js +++ b/src/core/Env.js @@ -583,6 +583,7 @@ getJasmineRequireObj().Env = function(j$) { * @name Env#hideDisabled * @since 3.2.0 * @function + * @deprecated Use the `hideDisabled` option with {@link Env#configure} */ this.hideDisabled = function(value) { this.deprecated( From 17826cd0440e929e79db278b08241c9a8ed361ba Mon Sep 17 00:00:00 2001 From: Steve Gravrock Date: Sat, 10 Jul 2021 09:11:10 -0700 Subject: [PATCH 23/23] Fixed deprecations in matchersUtilSpec --- spec/core/matchers/matchersUtilSpec.js | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/spec/core/matchers/matchersUtilSpec.js b/spec/core/matchers/matchersUtilSpec.js index bbd33ceb..f3ce7797 100644 --- a/spec/core/matchers/matchersUtilSpec.js +++ b/spec/core/matchers/matchersUtilSpec.js @@ -842,12 +842,13 @@ describe('matchersUtil', function() { describe('Typed arrays', function() { it('fails for typed arrays of same length and contents but different types', function() { + var matchersUtil = new jasmineUnderTest.MatchersUtil(); // eslint-disable-next-line compat/compat var a1 = new Int8Array(1); // eslint-disable-next-line compat/compat var a2 = new Uint8Array(1); a1[0] = a2[0] = 0; - expect(jasmineUnderTest.matchersUtil.equals(a1, a2)).toBe(false); + expect(matchersUtil.equals(a1, a2)).toBe(false); }); // eslint-disable-next-line compat/compat @@ -876,41 +877,45 @@ describe('matchersUtil', function() { 'passes for ' + typeName + 's with same length and content', function() { var TypedArrayCtor = requireType(); + var matchersUtil = new jasmineUnderTest.MatchersUtil(); var a1 = new TypedArrayCtor(2); var a2 = new TypedArrayCtor(2); a1[0] = a2[0] = 0; a1[1] = a2[1] = 1; - expect(jasmineUnderTest.matchersUtil.equals(a1, a2)).toBe(true); + expect(matchersUtil.equals(a1, a2)).toBe(true); } ); it('fails for ' + typeName + 's with different length', function() { var TypedArrayCtor = requireType(); + var matchersUtil = new jasmineUnderTest.MatchersUtil(); var a1 = new TypedArrayCtor(2); var a2 = new TypedArrayCtor(1); a1[0] = a1[1] = a2[0] = 0; - expect(jasmineUnderTest.matchersUtil.equals(a1, a2)).toBe(false); + expect(matchersUtil.equals(a1, a2)).toBe(false); }); it( 'fails for ' + typeName + 's with same length but different content', function() { var TypedArrayCtor = requireType(); + var matchersUtil = new jasmineUnderTest.MatchersUtil(); var a1 = new TypedArrayCtor(1); var a2 = new TypedArrayCtor(1); a1[0] = 0; a2[0] = 1; - expect(jasmineUnderTest.matchersUtil.equals(a1, a2)).toBe(false); + expect(matchersUtil.equals(a1, a2)).toBe(false); } ); it('checks nonstandard properties of ' + typeName, function() { var TypedArrayCtor = requireType(); + var matchersUtil = new jasmineUnderTest.MatchersUtil(); var a1 = new TypedArrayCtor(1); var a2 = new TypedArrayCtor(1); a1[0] = a2[0] = 0; a1.extra = 'yes'; - expect(jasmineUnderTest.matchersUtil.equals(a1, a2)).toBe(false); + expect(matchersUtil.equals(a1, a2)).toBe(false); }); it('works with custom equality testers with ' + typeName, function() { @@ -945,36 +950,39 @@ describe('matchersUtil', function() { 'passes for ' + typeName + 's with same length and content', function() { var TypedArrayCtor = requireType(); + var matchersUtil = new jasmineUnderTest.MatchersUtil(); var a1 = new TypedArrayCtor(2); var a2 = new TypedArrayCtor(2); // eslint-disable-next-line compat/compat a1[0] = a2[0] = BigInt(0); // eslint-disable-next-line compat/compat a1[1] = a2[1] = BigInt(1); - expect(jasmineUnderTest.matchersUtil.equals(a1, a2)).toBe(true); + expect(matchersUtil.equals(a1, a2)).toBe(true); } ); it('fails for ' + typeName + 's with different length', function() { var TypedArrayCtor = requireType(); + var matchersUtil = new jasmineUnderTest.MatchersUtil(); var a1 = new TypedArrayCtor(2); var a2 = new TypedArrayCtor(1); // eslint-disable-next-line compat/compat a1[0] = a1[1] = a2[0] = BigInt(0); - expect(jasmineUnderTest.matchersUtil.equals(a1, a2)).toBe(false); + expect(matchersUtil.equals(a1, a2)).toBe(false); }); it( 'fails for ' + typeName + 's with same length but different content', function() { var TypedArrayCtor = requireType(); + var matchersUtil = new jasmineUnderTest.MatchersUtil(); var a1 = new TypedArrayCtor(2); var a2 = new TypedArrayCtor(2); // eslint-disable-next-line compat/compat a1[0] = a1[1] = a2[0] = BigInt(0); // eslint-disable-next-line compat/compat a2[1] = BigInt(1); - expect(jasmineUnderTest.matchersUtil.equals(a1, a2)).toBe(false); + expect(matchersUtil.equals(a1, a2)).toBe(false); } ); });