From 26c48ab324501bae0b88d1c69d2a8a018ec1d16d Mon Sep 17 00:00:00 2001 From: Steve Gravrock Date: Sat, 3 Sep 2022 09:42:39 -0700 Subject: [PATCH 1/5] Bump version to 4.4.0 --- lib/jasmine-core/jasmine.js | 2 +- package.json | 2 +- release_notes/4.4.0.md | 28 ++++++++++++++++++++++++++++ 3 files changed, 30 insertions(+), 2 deletions(-) create mode 100644 release_notes/4.4.0.md diff --git a/lib/jasmine-core/jasmine.js b/lib/jasmine-core/jasmine.js index 166ffdb6..bcf2d4ca 100644 --- a/lib/jasmine-core/jasmine.js +++ b/lib/jasmine-core/jasmine.js @@ -10448,5 +10448,5 @@ getJasmineRequireObj().UserContext = function(j$) { }; getJasmineRequireObj().version = function() { - return '4.3.0'; + return '4.4.0'; }; diff --git a/package.json b/package.json index fd4eb305..29c3b953 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "jasmine-core", "license": "MIT", - "version": "4.3.0", + "version": "4.4.0", "repository": { "type": "git", "url": "https://github.com/jasmine/jasmine.git" diff --git a/release_notes/4.4.0.md b/release_notes/4.4.0.md new file mode 100644 index 00000000..0674f02b --- /dev/null +++ b/release_notes/4.4.0.md @@ -0,0 +1,28 @@ +# Jasmine 4.4.0 Release Notes + +## Changes + +* Optimized the process of transitioning between specs in Node, Safari, and + Edge. This change reduces the run time of jasmine-core's own test suite by + 50-70% in Node, about 20% in Edge, and 75-90% in Safari. Your results may + vary. In general, suites with many fast specs will see the greatest + performance improvement. + +* Removed old code to support browsers that don't provide + addEventListener/removeEventListener. + +## Supported environments + +jasmine-core 4.4.0 has been tested in the following environments. + +| Environment | Supported versions | +|-------------------|--------------------| +| Node | 12.17+, 14, 16, 18 | +| Safari | 14-15 | +| Chrome | 105 | +| Firefox | 91, 102, 104 | +| Edge | 104 | + +------ + +_Release Notes generated with _[Anchorman](http://github.com/infews/anchorman)_ From c14bfe3e5fe5f4e78482ab8de3a302fb9ddf7f33 Mon Sep 17 00:00:00 2001 From: Steve Gravrock Date: Sat, 3 Sep 2022 11:02:13 -0700 Subject: [PATCH 2/5] Updated release process doc * Fixed description of patch releases * Moved -npm release docmentation to that repo * Refer to -npm specifically rather than "binding libraries" generally, now that we only have one of those that versions in lockstep with core. --- RELEASE.md | 28 ++++++++++------------------ 1 file changed, 10 insertions(+), 18 deletions(-) diff --git a/RELEASE.md b/RELEASE.md index 347c6b3a..517752c2 100644 --- a/RELEASE.md +++ b/RELEASE.md @@ -18,12 +18,11 @@ copied to `jasmine.js` when the distribution is built. When releasing a new version, update `package.json` with the new version and `npm run build` to update the gem version number. -Note that Jasmine should only use the "patch" version number in the following cases: +Note that Jasmine should only use the "patch" version number if the new release +contains only bug fixes. -* Changes related to packaging for a specific binding library (npm or browser-runner) -* Fixes for regressions. - -When jasmine-core revs its major or minor version, the binding libraries should also rev to that version. +When `jasmine-core` revs its major or minor version, the `jasmine` NPM package +should also rev to that version. ## Release @@ -61,20 +60,13 @@ for instructions. 1. `rake release[${version}]` to copy the current edge docs to the new version 1. Commit and push. -### Release the binding libraries +### Release the `jasmine` NPM package -#### NPM +See . -1. Create release notes using Anchorman as above -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. `grunt release `. (Note: This will publish the package by running `npm publish`.) +### Publish the GitHub release -### Finally - -For each of the above GitHub repos: 1. Visit the releases page and find the tag just published. -1. Paste in a link to the correct release notes for this release. The link should reference the blob and tag correctly, and the markdown file for the notes. -1. If it is a pre-release, mark it as such. -1. For core, attach the standalone zipfile. +2. Paste in a link to the correct release notes for this release. +3. If it is a pre-release, mark it as such. +4. Attach the standalone zipfile. From 59848ca1515355ba8eff63a4f263eccff99144b2 Mon Sep 17 00:00:00 2001 From: Steve Gravrock Date: Sat, 3 Sep 2022 12:36:35 -0700 Subject: [PATCH 3/5] Coerce the random string to a seed before sending it to reporters This fixes an error in HTMLReporter when the configured seed is a number rather than a string, which has been allowed since 3.8.0 --- lib/jasmine-core/jasmine.js | 2 +- spec/core/integration/EnvSpec.js | 23 +++++++++++++++++++++++ src/core/Runner.js | 2 +- 3 files changed, 25 insertions(+), 2 deletions(-) diff --git a/lib/jasmine-core/jasmine.js b/lib/jasmine-core/jasmine.js index bcf2d4ca..6b60117c 100644 --- a/lib/jasmine-core/jasmine.js +++ b/lib/jasmine-core/jasmine.js @@ -8433,7 +8433,7 @@ getJasmineRequireObj().Runner = function(j$) { const order = new j$.Order({ random: config.random, - seed: config.seed + seed: j$.isNumber_(config.seed) ? config.seed + '' : config.seed }); const processor = new j$.TreeProcessor({ diff --git a/spec/core/integration/EnvSpec.js b/spec/core/integration/EnvSpec.js index b61322d2..f2433403 100644 --- a/spec/core/integration/EnvSpec.js +++ b/spec/core/integration/EnvSpec.js @@ -2026,6 +2026,29 @@ describe('Env integration', function() { expect(doneArg.order.seed).toEqual('123456'); }); + it('coerces the random seed to a string if it is a number', async function() { + const reporter = jasmine.createSpyObj('fakeReporter', [ + 'jasmineStarted', + 'jasmineDone', + 'suiteStarted', + 'suiteDone', + 'specStarted', + 'specDone' + ]); + env.configure({ random: true, seed: 123456 }); + + env.addReporter(reporter); + env.configure({ random: true }); + await env.execute(); + + expect(reporter.jasmineStarted).toHaveBeenCalled(); + const startedArg = reporter.jasmineStarted.calls.argsFor(0)[0]; + expect(startedArg.order.seed).toEqual('123456'); + + const doneArg = reporter.jasmineDone.calls.argsFor(0)[0]; + expect(doneArg.order.seed).toEqual('123456'); + }); + it('should report pending spec messages', async function() { const reporter = jasmine.createSpyObj('fakeReporter', ['specDone']); diff --git a/src/core/Runner.js b/src/core/Runner.js index 10df5195..0f743c7f 100644 --- a/src/core/Runner.js +++ b/src/core/Runner.js @@ -51,7 +51,7 @@ getJasmineRequireObj().Runner = function(j$) { const order = new j$.Order({ random: config.random, - seed: config.seed + seed: j$.isNumber_(config.seed) ? config.seed + '' : config.seed }); const processor = new j$.TreeProcessor({ From 44f331f43d1b8b5c46578fba3ee22ebbd6c2ced5 Mon Sep 17 00:00:00 2001 From: Steve Gravrock Date: Sat, 17 Sep 2022 12:00:20 -0700 Subject: [PATCH 4/5] Updated the style of the examples * const/let instead of var * classes * pass our own eslint checks --- .../lib/jasmine_examples/Player.js | 38 +++++++++---------- .../node_example/lib/jasmine_examples/Song.js | 11 +++--- .../helpers/jasmine_examples/SpecHelper.js | 4 +- .../spec/jasmine_examples/PlayerSpec.js | 29 +++++++------- lib/jasmine-core/example/spec/PlayerSpec.js | 24 ++++++------ lib/jasmine-core/example/spec/SpecHelper.js | 2 +- lib/jasmine-core/example/src/Player.js | 38 +++++++++---------- lib/jasmine-core/example/src/Song.js | 11 +++--- 8 files changed, 78 insertions(+), 79 deletions(-) diff --git a/lib/jasmine-core/example/node_example/lib/jasmine_examples/Player.js b/lib/jasmine-core/example/node_example/lib/jasmine_examples/Player.js index fe95f894..8a7bfbdc 100644 --- a/lib/jasmine-core/example/node_example/lib/jasmine_examples/Player.js +++ b/lib/jasmine-core/example/node_example/lib/jasmine_examples/Player.js @@ -1,24 +1,24 @@ -function Player() { -} -Player.prototype.play = function(song) { - this.currentlyPlayingSong = song; - this.isPlaying = true; -}; - -Player.prototype.pause = function() { - this.isPlaying = false; -}; - -Player.prototype.resume = function() { - if (this.isPlaying) { - throw new Error("song is already playing"); +class Player { + play(song) { + this.currentlyPlayingSong = song; + this.isPlaying = true; } - this.isPlaying = true; -}; + pause() { + this.isPlaying = false; + } -Player.prototype.makeFavorite = function() { - this.currentlyPlayingSong.persistFavoriteStatus(true); -}; + resume() { + if (this.isPlaying) { + throw new Error('song is already playing'); + } + + this.isPlaying = true; + } + + makeFavorite() { + this.currentlyPlayingSong.persistFavoriteStatus(true); + } +} module.exports = Player; diff --git a/lib/jasmine-core/example/node_example/lib/jasmine_examples/Song.js b/lib/jasmine-core/example/node_example/lib/jasmine_examples/Song.js index 3415bb82..f2757786 100644 --- a/lib/jasmine-core/example/node_example/lib/jasmine_examples/Song.js +++ b/lib/jasmine-core/example/node_example/lib/jasmine_examples/Song.js @@ -1,9 +1,8 @@ -function Song() { +class Song { + persistFavoriteStatus(value) { + // something complicated + throw new Error('not yet implemented'); + } } -Song.prototype.persistFavoriteStatus = function(value) { - // something complicated - throw new Error("not yet implemented"); -}; - module.exports = Song; diff --git a/lib/jasmine-core/example/node_example/spec/helpers/jasmine_examples/SpecHelper.js b/lib/jasmine-core/example/node_example/spec/helpers/jasmine_examples/SpecHelper.js index 578b3e86..cc04bd8d 100644 --- a/lib/jasmine-core/example/node_example/spec/helpers/jasmine_examples/SpecHelper.js +++ b/lib/jasmine-core/example/node_example/spec/helpers/jasmine_examples/SpecHelper.js @@ -3,11 +3,11 @@ beforeEach(function () { toBePlaying: function () { return { compare: function (actual, expected) { - var player = actual; + const player = actual; return { pass: player.currentlyPlayingSong === expected && player.isPlaying - } + }; } }; } diff --git a/lib/jasmine-core/example/node_example/spec/jasmine_examples/PlayerSpec.js b/lib/jasmine-core/example/node_example/spec/jasmine_examples/PlayerSpec.js index 80f149e3..fa96ccc4 100644 --- a/lib/jasmine-core/example/node_example/spec/jasmine_examples/PlayerSpec.js +++ b/lib/jasmine-core/example/node_example/spec/jasmine_examples/PlayerSpec.js @@ -1,36 +1,37 @@ -describe("Player", function() { - var Player = require('../../lib/jasmine_examples/Player'); - var Song = require('../../lib/jasmine_examples/Song'); - var player; - var song; +const Player = require('../../lib/jasmine_examples/Player'); +const Song = require('../../lib/jasmine_examples/Song'); + +describe('Player', function() { + let player; + let song; beforeEach(function() { player = new Player(); song = new Song(); }); - it("should be able to play a Song", function() { + it('should be able to play a Song', function() { player.play(song); expect(player.currentlyPlayingSong).toEqual(song); - //demonstrates use of custom matcher + // demonstrates use of custom matcher expect(player).toBePlaying(song); }); - describe("when song has been paused", function() { + describe('when song has been paused', function() { beforeEach(function() { player.play(song); player.pause(); }); - it("should indicate that the song is currently paused", function() { + it('should indicate that the song is currently paused', function() { expect(player.isPlaying).toBeFalsy(); // demonstrates use of 'not' with a custom matcher expect(player).not.toBePlaying(song); }); - it("should be possible to resume", function() { + it('should be possible to resume', function() { player.resume(); expect(player.isPlaying).toBeTruthy(); expect(player.currentlyPlayingSong).toEqual(song); @@ -38,7 +39,7 @@ describe("Player", function() { }); // demonstrates use of spies to intercept and test method calls - it("tells the current song if the user has made it a favorite", function() { + it('tells the current song if the user has made it a favorite', function() { spyOn(song, 'persistFavoriteStatus'); player.play(song); @@ -48,13 +49,13 @@ describe("Player", function() { }); //demonstrates use of expected exceptions - describe("#resume", function() { - it("should throw an exception if song is already playing", function() { + describe('#resume', function() { + it('should throw an exception if song is already playing', function() { player.play(song); expect(function() { player.resume(); - }).toThrowError("song is already playing"); + }).toThrowError('song is already playing'); }); }); }); diff --git a/lib/jasmine-core/example/spec/PlayerSpec.js b/lib/jasmine-core/example/spec/PlayerSpec.js index f17521fd..9617c4f4 100644 --- a/lib/jasmine-core/example/spec/PlayerSpec.js +++ b/lib/jasmine-core/example/spec/PlayerSpec.js @@ -1,34 +1,34 @@ -describe("Player", function() { - var player; - var song; +describe('Player', function() { + let player; + let song; beforeEach(function() { player = new Player(); song = new Song(); }); - it("should be able to play a Song", function() { + it('should be able to play a Song', function() { player.play(song); expect(player.currentlyPlayingSong).toEqual(song); - //demonstrates use of custom matcher + // demonstrates use of custom matcher expect(player).toBePlaying(song); }); - describe("when song has been paused", function() { + describe('when song has been paused', function() { beforeEach(function() { player.play(song); player.pause(); }); - it("should indicate that the song is currently paused", function() { + it('should indicate that the song is currently paused', function() { expect(player.isPlaying).toBeFalsy(); // demonstrates use of 'not' with a custom matcher expect(player).not.toBePlaying(song); }); - it("should be possible to resume", function() { + it('should be possible to resume', function() { player.resume(); expect(player.isPlaying).toBeTruthy(); expect(player.currentlyPlayingSong).toEqual(song); @@ -36,7 +36,7 @@ describe("Player", function() { }); // demonstrates use of spies to intercept and test method calls - it("tells the current song if the user has made it a favorite", function() { + it('tells the current song if the user has made it a favorite', function() { spyOn(song, 'persistFavoriteStatus'); player.play(song); @@ -46,13 +46,13 @@ describe("Player", function() { }); //demonstrates use of expected exceptions - describe("#resume", function() { - it("should throw an exception if song is already playing", function() { + describe('#resume', function() { + it('should throw an exception if song is already playing', function() { player.play(song); expect(function() { player.resume(); - }).toThrowError("song is already playing"); + }).toThrowError('song is already playing'); }); }); }); diff --git a/lib/jasmine-core/example/spec/SpecHelper.js b/lib/jasmine-core/example/spec/SpecHelper.js index 126752d1..cc04bd8d 100644 --- a/lib/jasmine-core/example/spec/SpecHelper.js +++ b/lib/jasmine-core/example/spec/SpecHelper.js @@ -3,7 +3,7 @@ beforeEach(function () { toBePlaying: function () { return { compare: function (actual, expected) { - var player = actual; + const player = actual; return { pass: player.currentlyPlayingSong === expected && player.isPlaying diff --git a/lib/jasmine-core/example/src/Player.js b/lib/jasmine-core/example/src/Player.js index 11851966..f2fae6f7 100644 --- a/lib/jasmine-core/example/src/Player.js +++ b/lib/jasmine-core/example/src/Player.js @@ -1,22 +1,22 @@ -function Player() { -} -Player.prototype.play = function(song) { - this.currentlyPlayingSong = song; - this.isPlaying = true; -}; - -Player.prototype.pause = function() { - this.isPlaying = false; -}; - -Player.prototype.resume = function() { - if (this.isPlaying) { - throw new Error("song is already playing"); +class Player { + play(song) { + this.currentlyPlayingSong = song; + this.isPlaying = true; } - this.isPlaying = true; -}; + pause() { + this.isPlaying = false; + } -Player.prototype.makeFavorite = function() { - this.currentlyPlayingSong.persistFavoriteStatus(true); -}; + resume() { + if (this.isPlaying) { + throw new Error('song is already playing'); + } + + this.isPlaying = true; + } + + makeFavorite() { + this.currentlyPlayingSong.persistFavoriteStatus(true); + } +} diff --git a/lib/jasmine-core/example/src/Song.js b/lib/jasmine-core/example/src/Song.js index 02527cb1..8914793b 100644 --- a/lib/jasmine-core/example/src/Song.js +++ b/lib/jasmine-core/example/src/Song.js @@ -1,7 +1,6 @@ -function Song() { +class Song { + persistFavoriteStatus(value) { + // something complicated + throw new Error('not yet implemented'); + } } - -Song.prototype.persistFavoriteStatus = function(value) { - // something complicated - throw new Error("not yet implemented"); -}; From d4025999b7bc9e60456e3f5b21865c3fdaeec6f8 Mon Sep 17 00:00:00 2001 From: Steve Gravrock Date: Sat, 17 Sep 2022 13:23:36 -0700 Subject: [PATCH 5/5] Report exceptions thrown by a describe before any it calls Previously, these were masked by the "describe with no children" error. Now they're reported as suite level errors on an empty suite. --- lib/jasmine-core/jasmine.js | 13 ++++++----- spec/core/integration/EnvSpec.js | 38 ++++++++++++++++++++++++++++++++ src/core/SuiteBuilder.js | 13 ++++++----- 3 files changed, 54 insertions(+), 10 deletions(-) diff --git a/lib/jasmine-core/jasmine.js b/lib/jasmine-core/jasmine.js index 6b60117c..c2741948 100644 --- a/lib/jasmine-core/jasmine.js +++ b/lib/jasmine-core/jasmine.js @@ -9872,11 +9872,6 @@ getJasmineRequireObj().SuiteBuilder = function(j$) { suite.exclude(); } this.addSpecsToSuite_(suite, definitionFn); - if (suite.parentSuite && !suite.children.length) { - throw new Error( - `describe with no children (describe() or it()): ${suite.getFullName()}` - ); - } return suite; } @@ -10023,11 +10018,19 @@ getJasmineRequireObj().SuiteBuilder = function(j$) { const parentSuite = this.currentDeclarationSuite_; parentSuite.addChild(suite); this.currentDeclarationSuite_ = suite; + let threw = false; try { definitionFn(); } catch (e) { suite.handleException(e); + threw = true; + } + + if (suite.parentSuite && !suite.children.length && !threw) { + throw new Error( + `describe with no children (describe() or it()): ${suite.getFullName()}` + ); } this.currentDeclarationSuite_ = parentSuite; diff --git a/spec/core/integration/EnvSpec.js b/spec/core/integration/EnvSpec.js index f2433403..8f2990ad 100644 --- a/spec/core/integration/EnvSpec.js +++ b/spec/core/integration/EnvSpec.js @@ -3966,6 +3966,44 @@ describe('Env integration', function() { }); }); + it('reports a suite level error when a describe fn throws', async function() { + const reporter = jasmine.createSpyObj('reporter', ['suiteDone']); + env.addReporter(reporter); + + env.describe('throws before defining specs', function() { + throw new Error('nope'); + }); + + env.describe('throws after defining specs', function() { + env.it('is a spec'); + throw new Error('nope'); + }); + + await env.execute(); + + expect(reporter.suiteDone).toHaveBeenCalledWith( + jasmine.objectContaining({ + fullName: 'throws after defining specs', + failedExpectations: [ + jasmine.objectContaining({ + message: jasmine.stringContaining('Error: nope') + }) + ] + }) + ); + + expect(reporter.suiteDone).toHaveBeenCalledWith( + jasmine.objectContaining({ + fullName: 'throws after defining specs', + failedExpectations: [ + jasmine.objectContaining({ + message: jasmine.stringContaining('Error: nope') + }) + ] + }) + ); + }); + function browserEventMethods() { return { addEventListener() {}, diff --git a/src/core/SuiteBuilder.js b/src/core/SuiteBuilder.js index 0fe85925..c29b2f40 100644 --- a/src/core/SuiteBuilder.js +++ b/src/core/SuiteBuilder.js @@ -32,11 +32,6 @@ getJasmineRequireObj().SuiteBuilder = function(j$) { suite.exclude(); } this.addSpecsToSuite_(suite, definitionFn); - if (suite.parentSuite && !suite.children.length) { - throw new Error( - `describe with no children (describe() or it()): ${suite.getFullName()}` - ); - } return suite; } @@ -183,11 +178,19 @@ getJasmineRequireObj().SuiteBuilder = function(j$) { const parentSuite = this.currentDeclarationSuite_; parentSuite.addChild(suite); this.currentDeclarationSuite_ = suite; + let threw = false; try { definitionFn(); } catch (e) { suite.handleException(e); + threw = true; + } + + if (suite.parentSuite && !suite.children.length && !threw) { + throw new Error( + `describe with no children (describe() or it()): ${suite.getFullName()}` + ); } this.currentDeclarationSuite_ = parentSuite;