Compare commits
14 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
481f1e7c5c | ||
|
|
5e650953cd | ||
|
|
87f9ab29df | ||
|
|
b831e81074 | ||
|
|
4c13c2b00b | ||
|
|
4cd190b232 | ||
|
|
d4025999b7 | ||
|
|
44f331f43d | ||
|
|
59848ca151 | ||
|
|
c14bfe3e5f | ||
|
|
b3d9435dbb | ||
|
|
fec8dd37b0 | ||
|
|
5f3475342e | ||
|
|
21f25972bb |
@@ -33,7 +33,7 @@ Microsoft Edge) as well as Node.
|
||||
| Environment | Supported versions |
|
||||
|-------------------|--------------------|
|
||||
| Node | 12.17+, 14, 16, 18 |
|
||||
| Safari | 14-15 |
|
||||
| Safari | 14-16 |
|
||||
| Chrome | Evergreen |
|
||||
| Firefox | Evergreen, 91 |
|
||||
| Edge | Evergreen |
|
||||
|
||||
28
RELEASE.md
28
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 <https://github.com/jasmine/jasmine-npm/blob/main/RELEASE.md>.
|
||||
|
||||
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.
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
};
|
||||
|
||||
@@ -776,7 +776,7 @@ getJasmineRequireObj().Spec = function(j$) {
|
||||
|
||||
/**
|
||||
* @typedef SpecResult
|
||||
* @property {Int} id - The unique id of this spec.
|
||||
* @property {String} id - The unique id of this spec.
|
||||
* @property {String} description - The description passed to the {@link it} that created this spec.
|
||||
* @property {String} fullName - The full description including all ancestors of this spec.
|
||||
* @property {Expectation[]} failedExpectations - The list of expectations that failed during execution of this spec.
|
||||
@@ -1843,12 +1843,12 @@ getJasmineRequireObj().Env = function(j$) {
|
||||
function specStarted(spec, suite, next) {
|
||||
runner.currentSpec = spec;
|
||||
runableResources.initForRunable(spec.id, suite.id);
|
||||
reporter.specStarted(spec.result, next);
|
||||
reporter.specStarted(spec.result).then(next);
|
||||
}
|
||||
|
||||
function reportSpecDone(spec, result, next) {
|
||||
spec.reportedDone = true;
|
||||
reporter.specDone(result, next);
|
||||
reporter.specDone(result).then(next);
|
||||
}
|
||||
|
||||
this.it = function(description, fn, timeout) {
|
||||
@@ -2868,7 +2868,8 @@ getJasmineRequireObj().clearStack = function(j$) {
|
||||
SAFARI ||
|
||||
j$.util.isUndefined(global.MessageChannel) /* tests */
|
||||
) {
|
||||
// queueMicrotask is dramatically faster than MessageChannel in Safari.
|
||||
// queueMicrotask is dramatically faster than MessageChannel in Safari,
|
||||
// at least through version 16.
|
||||
// Some of our own integration tests provide a mock queueMicrotask in all
|
||||
// environments because it's simpler to mock than MessageChannel.
|
||||
return browserQueueMicrotaskImpl(global);
|
||||
@@ -3494,18 +3495,38 @@ getJasmineRequireObj().ExceptionFormatter = function(j$) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const stackTrace = new j$.StackTrace(error);
|
||||
const lines = filterJasmine(stackTrace);
|
||||
let result = '';
|
||||
const lines = this.stack_(error, {
|
||||
messageHandling: omitMessage ? 'omit' : undefined
|
||||
});
|
||||
return lines.join('\n');
|
||||
};
|
||||
|
||||
if (stackTrace.message && !omitMessage) {
|
||||
// messageHandling can be falsy (unspecified), 'omit', or 'require'
|
||||
this.stack_ = function(error, { messageHandling }) {
|
||||
let lines = formatProperties(error).split('\n');
|
||||
|
||||
if (lines[lines.length - 1] === '') {
|
||||
lines.pop();
|
||||
}
|
||||
|
||||
const stackTrace = new j$.StackTrace(error);
|
||||
lines = lines.concat(filterJasmine(stackTrace));
|
||||
|
||||
if (messageHandling === 'require') {
|
||||
lines.unshift(stackTrace.message || 'Error: ' + error.message);
|
||||
} else if (messageHandling !== 'omit' && stackTrace.message) {
|
||||
lines.unshift(stackTrace.message);
|
||||
}
|
||||
|
||||
result += formatProperties(error);
|
||||
result += lines.join('\n');
|
||||
if (error.cause) {
|
||||
const substack = this.stack_(error.cause, {
|
||||
messageHandling: 'require'
|
||||
});
|
||||
substack[0] = 'Caused by: ' + substack[0];
|
||||
lines = lines.concat(substack);
|
||||
}
|
||||
|
||||
return result;
|
||||
return lines;
|
||||
};
|
||||
|
||||
function filterJasmine(stackTrace) {
|
||||
@@ -5241,7 +5262,7 @@ getJasmineRequireObj().MatchersUtil = function(j$) {
|
||||
* };
|
||||
* }
|
||||
*
|
||||
* var actual = {
|
||||
* const actual = {
|
||||
* n: 2,
|
||||
* otherFields: "don't care"
|
||||
* };
|
||||
@@ -6387,7 +6408,7 @@ getJasmineRequireObj().toHaveClass = function(j$) {
|
||||
* @since 3.0.0
|
||||
* @param {Object} expected - The class name to test for
|
||||
* @example
|
||||
* var el = document.createElement('div');
|
||||
* const el = document.createElement('div');
|
||||
* el.className = 'foo bar baz';
|
||||
* expect(el).toHaveClass('bar');
|
||||
*/
|
||||
@@ -7719,7 +7740,7 @@ getJasmineRequireObj().ReportDispatcher = function(j$) {
|
||||
for (const method of dispatchedMethods) {
|
||||
this[method] = (function(m) {
|
||||
return function() {
|
||||
dispatch(m, arguments);
|
||||
return dispatch(m, arguments);
|
||||
};
|
||||
})(method);
|
||||
}
|
||||
@@ -7745,25 +7766,25 @@ getJasmineRequireObj().ReportDispatcher = function(j$) {
|
||||
if (reporters.length === 0 && fallbackReporter !== null) {
|
||||
reporters.push(fallbackReporter);
|
||||
}
|
||||
const onComplete = args[args.length - 1];
|
||||
args = Array.from(args).splice(0, args.length - 1);
|
||||
const fns = [];
|
||||
for (const reporter of reporters) {
|
||||
addFn(fns, reporter, method, args);
|
||||
}
|
||||
|
||||
queueRunnerFactory({
|
||||
queueableFns: fns,
|
||||
onComplete: onComplete,
|
||||
isReporter: true,
|
||||
onMultipleDone: function() {
|
||||
onLateError(
|
||||
new Error(
|
||||
"An asynchronous reporter callback called its 'done' callback " +
|
||||
'more than once.'
|
||||
)
|
||||
);
|
||||
}
|
||||
return new Promise(function(resolve) {
|
||||
queueRunnerFactory({
|
||||
queueableFns: fns,
|
||||
onComplete: resolve,
|
||||
isReporter: true,
|
||||
onMultipleDone: function() {
|
||||
onLateError(
|
||||
new Error(
|
||||
"An asynchronous reporter callback called its 'done' callback " +
|
||||
'more than once.'
|
||||
)
|
||||
);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -8419,7 +8440,6 @@ getJasmineRequireObj().Runner = function(j$) {
|
||||
this.executedBefore_ = true;
|
||||
|
||||
this.hasFailures = false;
|
||||
const totalSpecsDefined = this.totalSpecsDefined_();
|
||||
const focusedRunables = this.focusedRunables_();
|
||||
const config = this.getConfig_();
|
||||
|
||||
@@ -8433,7 +8453,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({
|
||||
@@ -8458,7 +8478,7 @@ getJasmineRequireObj().Runner = function(j$) {
|
||||
nodeStart: (suite, next) => {
|
||||
this.currentlyExecutingSuites_.push(suite);
|
||||
this.runableResources_.initForRunable(suite.id, suite.parentSuite.id);
|
||||
this.reporter_.suiteStarted(suite.result, next);
|
||||
this.reporter_.suiteStarted(suite.result).then(next);
|
||||
suite.startTimer();
|
||||
},
|
||||
nodeComplete: (suite, result, next) => {
|
||||
@@ -8496,106 +8516,97 @@ getJasmineRequireObj().Runner = function(j$) {
|
||||
);
|
||||
}
|
||||
|
||||
return this.execute2_(runablesToRun, order, processor);
|
||||
}
|
||||
|
||||
async execute2_(runablesToRun, order, processor) {
|
||||
const totalSpecsDefined = this.totalSpecsDefined_();
|
||||
|
||||
this.runableResources_.initForRunable(this.topSuite_.id);
|
||||
const jasmineTimer = new j$.Timer();
|
||||
jasmineTimer.start();
|
||||
|
||||
return new Promise(resolve => {
|
||||
/**
|
||||
* Information passed to the {@link Reporter#jasmineStarted} event.
|
||||
* @typedef JasmineStartedInfo
|
||||
* @property {Int} totalSpecsDefined - The total number of specs defined in this suite.
|
||||
* @property {Order} order - Information about the ordering (random or not) of this execution of the suite.
|
||||
* @since 2.0.0
|
||||
*/
|
||||
this.reporter_.jasmineStarted(
|
||||
{
|
||||
totalSpecsDefined,
|
||||
order: order
|
||||
},
|
||||
() => {
|
||||
this.currentlyExecutingSuites_.push(this.topSuite_);
|
||||
|
||||
processor.execute(() => {
|
||||
(async () => {
|
||||
if (this.topSuite_.hadBeforeAllFailure) {
|
||||
await this.reportChildrenOfBeforeAllFailure_(this.topSuite_);
|
||||
}
|
||||
|
||||
this.runableResources_.clearForRunable(this.topSuite_.id);
|
||||
this.currentlyExecutingSuites_.pop();
|
||||
let overallStatus, incompleteReason;
|
||||
|
||||
if (
|
||||
this.hasFailures ||
|
||||
this.topSuite_.result.failedExpectations.length > 0
|
||||
) {
|
||||
overallStatus = 'failed';
|
||||
} else if (focusedRunables.length > 0) {
|
||||
overallStatus = 'incomplete';
|
||||
incompleteReason = 'fit() or fdescribe() was found';
|
||||
} else if (totalSpecsDefined === 0) {
|
||||
overallStatus = 'incomplete';
|
||||
incompleteReason = 'No specs found';
|
||||
} else {
|
||||
overallStatus = 'passed';
|
||||
}
|
||||
|
||||
/**
|
||||
* Information passed to the {@link Reporter#jasmineDone} event.
|
||||
* @typedef JasmineDoneInfo
|
||||
* @property {OverallStatus} overallStatus - The overall result of the suite: 'passed', 'failed', or 'incomplete'.
|
||||
* @property {Int} totalTime - The total time (in ms) that it took to execute the suite
|
||||
* @property {IncompleteReason} incompleteReason - Explanation of why the suite was incomplete.
|
||||
* @property {Order} order - Information about the ordering (random or not) of this execution of the suite.
|
||||
* @property {Expectation[]} failedExpectations - List of expectations that failed in an {@link afterAll} at the global level.
|
||||
* @property {Expectation[]} deprecationWarnings - List of deprecation warnings that occurred at the global level.
|
||||
* @since 2.4.0
|
||||
*/
|
||||
const jasmineDoneInfo = {
|
||||
overallStatus: overallStatus,
|
||||
totalTime: jasmineTimer.elapsed(),
|
||||
incompleteReason: incompleteReason,
|
||||
order: order,
|
||||
failedExpectations: this.topSuite_.result.failedExpectations,
|
||||
deprecationWarnings: this.topSuite_.result.deprecationWarnings
|
||||
};
|
||||
this.topSuite_.reportedDone = true;
|
||||
this.reporter_.jasmineDone(jasmineDoneInfo, function() {
|
||||
resolve(jasmineDoneInfo);
|
||||
});
|
||||
})();
|
||||
});
|
||||
}
|
||||
);
|
||||
/**
|
||||
* Information passed to the {@link Reporter#jasmineStarted} event.
|
||||
* @typedef JasmineStartedInfo
|
||||
* @property {Int} totalSpecsDefined - The total number of specs defined in this suite.
|
||||
* @property {Order} order - Information about the ordering (random or not) of this execution of the suite.
|
||||
* @since 2.0.0
|
||||
*/
|
||||
await this.reporter_.jasmineStarted({
|
||||
totalSpecsDefined,
|
||||
order: order
|
||||
});
|
||||
|
||||
this.currentlyExecutingSuites_.push(this.topSuite_);
|
||||
await processor.execute();
|
||||
|
||||
if (this.topSuite_.hadBeforeAllFailure) {
|
||||
await this.reportChildrenOfBeforeAllFailure_(this.topSuite_);
|
||||
}
|
||||
|
||||
this.runableResources_.clearForRunable(this.topSuite_.id);
|
||||
this.currentlyExecutingSuites_.pop();
|
||||
let overallStatus, incompleteReason;
|
||||
|
||||
if (
|
||||
this.hasFailures ||
|
||||
this.topSuite_.result.failedExpectations.length > 0
|
||||
) {
|
||||
overallStatus = 'failed';
|
||||
} else if (this.focusedRunables_().length > 0) {
|
||||
overallStatus = 'incomplete';
|
||||
incompleteReason = 'fit() or fdescribe() was found';
|
||||
} else if (totalSpecsDefined === 0) {
|
||||
overallStatus = 'incomplete';
|
||||
incompleteReason = 'No specs found';
|
||||
} else {
|
||||
overallStatus = 'passed';
|
||||
}
|
||||
|
||||
/**
|
||||
* Information passed to the {@link Reporter#jasmineDone} event.
|
||||
* @typedef JasmineDoneInfo
|
||||
* @property {OverallStatus} overallStatus - The overall result of the suite: 'passed', 'failed', or 'incomplete'.
|
||||
* @property {Int} totalTime - The total time (in ms) that it took to execute the suite
|
||||
* @property {IncompleteReason} incompleteReason - Explanation of why the suite was incomplete.
|
||||
* @property {Order} order - Information about the ordering (random or not) of this execution of the suite.
|
||||
* @property {Expectation[]} failedExpectations - List of expectations that failed in an {@link afterAll} at the global level.
|
||||
* @property {Expectation[]} deprecationWarnings - List of deprecation warnings that occurred at the global level.
|
||||
* @since 2.4.0
|
||||
*/
|
||||
const jasmineDoneInfo = {
|
||||
overallStatus: overallStatus,
|
||||
totalTime: jasmineTimer.elapsed(),
|
||||
incompleteReason: incompleteReason,
|
||||
order: order,
|
||||
failedExpectations: this.topSuite_.result.failedExpectations,
|
||||
deprecationWarnings: this.topSuite_.result.deprecationWarnings
|
||||
};
|
||||
this.topSuite_.reportedDone = true;
|
||||
await this.reporter_.jasmineDone(jasmineDoneInfo);
|
||||
return jasmineDoneInfo;
|
||||
}
|
||||
|
||||
reportSuiteDone_(suite, result, next) {
|
||||
suite.reportedDone = true;
|
||||
this.reporter_.suiteDone(result, next);
|
||||
this.reporter_.suiteDone(result).then(next);
|
||||
}
|
||||
|
||||
async reportChildrenOfBeforeAllFailure_(suite) {
|
||||
for (const child of suite.children) {
|
||||
if (child instanceof j$.Suite) {
|
||||
await new Promise(resolve => {
|
||||
this.reporter_.suiteStarted(child.result, resolve);
|
||||
});
|
||||
await this.reporter_.suiteStarted(child.result);
|
||||
await this.reportChildrenOfBeforeAllFailure_(child);
|
||||
|
||||
// Marking the suite passed is consistent with how suites that
|
||||
// contain failed specs but no suite-level failures are reported.
|
||||
child.result.status = 'passed';
|
||||
|
||||
await new Promise(resolve => {
|
||||
this.reporter_.suiteDone(child.result, resolve);
|
||||
});
|
||||
await this.reporter_.suiteDone(child.result);
|
||||
} else {
|
||||
/* a spec */
|
||||
await new Promise(resolve => {
|
||||
this.reporter_.specStarted(child.result, resolve);
|
||||
});
|
||||
await this.reporter_.specStarted(child.result);
|
||||
|
||||
child.addExpectationResult(
|
||||
false,
|
||||
@@ -9621,7 +9632,7 @@ getJasmineRequireObj().Suite = function(j$) {
|
||||
Suite.prototype.reset = function() {
|
||||
/**
|
||||
* @typedef SuiteResult
|
||||
* @property {Int} id - The unique id of this suite.
|
||||
* @property {String} id - The unique id of this suite.
|
||||
* @property {String} description - The description text passed to the {@link describe} that made this suite.
|
||||
* @property {String} fullName - The full description including all ancestors of this suite.
|
||||
* @property {Expectation[]} failedExpectations - The list of expectations that failed in an {@link afterAll} for this suite.
|
||||
@@ -9872,11 +9883,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 +10029,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;
|
||||
@@ -10195,7 +10209,7 @@ getJasmineRequireObj().TreeProcessor = function() {
|
||||
return stats;
|
||||
};
|
||||
|
||||
this.execute = function(done) {
|
||||
this.execute = async function() {
|
||||
if (!processed) {
|
||||
this.processTree();
|
||||
}
|
||||
@@ -10206,16 +10220,18 @@ getJasmineRequireObj().TreeProcessor = function() {
|
||||
|
||||
const childFns = wrapChildren(tree, 0);
|
||||
|
||||
queueRunnerFactory({
|
||||
queueableFns: childFns,
|
||||
userContext: tree.sharedUserContext(),
|
||||
onException: function() {
|
||||
tree.handleException.apply(tree, arguments);
|
||||
},
|
||||
onComplete: done,
|
||||
onMultipleDone: tree.onMultipleDone
|
||||
? tree.onMultipleDone.bind(tree)
|
||||
: null
|
||||
await new Promise(function(resolve) {
|
||||
queueRunnerFactory({
|
||||
queueableFns: childFns,
|
||||
userContext: tree.sharedUserContext(),
|
||||
onException: function() {
|
||||
tree.handleException.apply(tree, arguments);
|
||||
},
|
||||
onComplete: resolve,
|
||||
onMultipleDone: tree.onMultipleDone
|
||||
? tree.onMultipleDone.bind(tree)
|
||||
: null
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "jasmine-core",
|
||||
"license": "MIT",
|
||||
"version": "4.4.0",
|
||||
"version": "4.5.0",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/jasmine/jasmine.git"
|
||||
|
||||
40
release_notes/4.5.0.md
Normal file
40
release_notes/4.5.0.md
Normal file
@@ -0,0 +1,40 @@
|
||||
# Jasmine 4.5.0 Release Notes
|
||||
|
||||
## New Features
|
||||
|
||||
* Added Safari 16 to supported browsers
|
||||
* Include inner exceptions in stack traces
|
||||
|
||||
## Bug Fixes
|
||||
|
||||
* Report exceptions thrown by a describe before any it calls
|
||||
* 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
|
||||
|
||||
## Documentation updates
|
||||
|
||||
* Fixed the jsdoc types of SuiteResult and SpecResult ids
|
||||
* Replaced var with const in API doc examples
|
||||
* Updated the style of the examples that are included in jasmine-core
|
||||
|
||||
## Internal improvements
|
||||
|
||||
* Converted TreeProcessor to async/await
|
||||
* Converted ReportDispatcher to promises
|
||||
|
||||
## Supported environments
|
||||
|
||||
jasmine-core 4.5.0 has been tested in the following environments.
|
||||
|
||||
| Environment | Supported versions |
|
||||
|-------------------|--------------------|
|
||||
| Node | 12.17+, 14, 16, 18 |
|
||||
| Safari | 14-16 |
|
||||
| Chrome | 107 |
|
||||
| Firefox | 91, 102, 106 |
|
||||
| Edge | 106 |
|
||||
|
||||
------
|
||||
|
||||
_Release Notes generated with _[Anchorman](http://github.com/infews/anchorman)_
|
||||
@@ -27,6 +27,7 @@ run_browser chrome latest
|
||||
run_browser firefox latest
|
||||
run_browser firefox 102
|
||||
run_browser firefox 91
|
||||
run_browser safari 16
|
||||
run_browser safari 15
|
||||
run_browser safari 14
|
||||
run_browser MicrosoftEdge latest
|
||||
|
||||
@@ -256,5 +256,51 @@ describe('ExceptionFormatter', function() {
|
||||
expect(result).not.toContain('an error');
|
||||
});
|
||||
});
|
||||
|
||||
describe('In environments that support the cause property of Errors', function() {
|
||||
beforeEach(function() {
|
||||
const inner = new Error('inner');
|
||||
const outer = new Error('outer', { cause: inner });
|
||||
|
||||
if (!outer.cause) {
|
||||
// Currently: Node 12, Node 14, Safari 14
|
||||
pending('Environment does not support error cause');
|
||||
}
|
||||
});
|
||||
|
||||
it('recursively includes the cause in the stack trace in this environment', function() {
|
||||
const subject = new jasmineUnderTest.ExceptionFormatter();
|
||||
const rootCause = new Error('root cause');
|
||||
const proximateCause = new Error('proximate cause', {
|
||||
cause: rootCause
|
||||
});
|
||||
const symptom = new Error('symptom', { cause: proximateCause });
|
||||
|
||||
const lines = subject.stack(symptom).split('\n');
|
||||
// Not all environments include the message in the stack trace.
|
||||
const hasRootMessage = lines[0].indexOf('symptom') !== -1;
|
||||
const firstSymptomStackIx = hasRootMessage ? 1 : 0;
|
||||
|
||||
expect(lines[firstSymptomStackIx])
|
||||
.withContext('first symptom stack frame')
|
||||
.toContain('ExceptionFormatterSpec.js');
|
||||
const proximateCauseMsgIx = lines.indexOf(
|
||||
'Caused by: Error: proximate cause'
|
||||
);
|
||||
expect(proximateCauseMsgIx)
|
||||
.withContext('index of proximate cause message')
|
||||
.toBeGreaterThan(firstSymptomStackIx);
|
||||
expect(lines[proximateCauseMsgIx + 1])
|
||||
.withContext('first proximate cause stack frame')
|
||||
.toContain('ExceptionFormatterSpec.js');
|
||||
const rootCauseMsgIx = lines.indexOf('Caused by: Error: root cause');
|
||||
expect(rootCauseMsgIx)
|
||||
.withContext('index of root cause message')
|
||||
.toBeGreaterThan(proximateCauseMsgIx + 1);
|
||||
expect(lines[rootCauseMsgIx + 1])
|
||||
.withContext('first root cause stack frame')
|
||||
.toContain('ExceptionFormatterSpec.js');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -18,13 +18,12 @@ describe('ReportDispatcher', function() {
|
||||
queueRunnerFactory
|
||||
),
|
||||
reporter = jasmine.createSpyObj('reporter', ['foo', 'bar']),
|
||||
anotherReporter = jasmine.createSpyObj('reporter', ['foo', 'bar']),
|
||||
completeCallback = jasmine.createSpy('complete');
|
||||
anotherReporter = jasmine.createSpyObj('reporter', ['foo', 'bar']);
|
||||
|
||||
dispatcher.addReporter(reporter);
|
||||
dispatcher.addReporter(anotherReporter);
|
||||
|
||||
dispatcher.foo(123, 456, completeCallback);
|
||||
dispatcher.foo(123, 456);
|
||||
|
||||
expect(queueRunnerFactory).toHaveBeenCalledWith(
|
||||
jasmine.objectContaining({
|
||||
@@ -47,7 +46,7 @@ describe('ReportDispatcher', function() {
|
||||
|
||||
queueRunnerFactory.calls.reset();
|
||||
|
||||
dispatcher.bar('a', 'b', completeCallback);
|
||||
dispatcher.bar('a', 'b');
|
||||
|
||||
expect(queueRunnerFactory).toHaveBeenCalledWith(
|
||||
jasmine.objectContaining({
|
||||
@@ -91,11 +90,10 @@ describe('ReportDispatcher', function() {
|
||||
['foo', 'bar'],
|
||||
queueRunnerFactory
|
||||
),
|
||||
reporter = jasmine.createSpyObj('reporter', ['foo', 'bar']),
|
||||
completeCallback = jasmine.createSpy('complete');
|
||||
reporter = jasmine.createSpyObj('reporter', ['foo', 'bar']);
|
||||
|
||||
dispatcher.provideFallbackReporter(reporter);
|
||||
dispatcher.foo(123, 456, completeCallback);
|
||||
dispatcher.foo(123, 456);
|
||||
|
||||
expect(queueRunnerFactory).toHaveBeenCalledWith(
|
||||
jasmine.objectContaining({
|
||||
@@ -116,12 +114,11 @@ describe('ReportDispatcher', function() {
|
||||
queueRunnerFactory
|
||||
),
|
||||
reporter = jasmine.createSpyObj('reporter', ['foo', 'bar']),
|
||||
fallbackReporter = jasmine.createSpyObj('otherReporter', ['foo', 'bar']),
|
||||
completeCallback = jasmine.createSpy('complete');
|
||||
fallbackReporter = jasmine.createSpyObj('otherReporter', ['foo', 'bar']);
|
||||
|
||||
dispatcher.provideFallbackReporter(fallbackReporter);
|
||||
dispatcher.addReporter(reporter);
|
||||
dispatcher.foo(123, 456, completeCallback);
|
||||
dispatcher.foo(123, 456);
|
||||
|
||||
expect(queueRunnerFactory).toHaveBeenCalledWith(
|
||||
jasmine.objectContaining({
|
||||
@@ -143,11 +140,10 @@ describe('ReportDispatcher', function() {
|
||||
queueRunnerFactory
|
||||
),
|
||||
reporter1 = jasmine.createSpyObj('reporter1', ['foo', 'bar']),
|
||||
reporter2 = jasmine.createSpyObj('reporter2', ['foo', 'bar']),
|
||||
completeCallback = jasmine.createSpy('complete');
|
||||
reporter2 = jasmine.createSpyObj('reporter2', ['foo', 'bar']);
|
||||
|
||||
dispatcher.addReporter(reporter1);
|
||||
dispatcher.foo(123, completeCallback);
|
||||
dispatcher.foo(123);
|
||||
expect(queueRunnerFactory).toHaveBeenCalledWith(
|
||||
jasmine.objectContaining({
|
||||
queueableFns: [{ fn: jasmine.any(Function) }],
|
||||
@@ -161,7 +157,7 @@ describe('ReportDispatcher', function() {
|
||||
|
||||
dispatcher.clearReporters();
|
||||
dispatcher.addReporter(reporter2);
|
||||
dispatcher.bar(456, completeCallback);
|
||||
dispatcher.bar(456);
|
||||
|
||||
expect(queueRunnerFactory).toHaveBeenCalledWith(
|
||||
jasmine.objectContaining({
|
||||
|
||||
@@ -274,7 +274,7 @@ describe('TreeProcessor', function() {
|
||||
expect(result.valid).toBe(true);
|
||||
});
|
||||
|
||||
it('runs a single leaf', function() {
|
||||
it('runs a single leaf', async function() {
|
||||
const leaf = new Leaf(),
|
||||
node = new Node({ children: [leaf], userContext: { root: 'context' } }),
|
||||
queueRunner = jasmine.createSpy('queueRunner'),
|
||||
@@ -282,25 +282,27 @@ describe('TreeProcessor', function() {
|
||||
tree: node,
|
||||
runnableIds: [leaf.id],
|
||||
queueRunnerFactory: queueRunner
|
||||
}),
|
||||
treeComplete = jasmine.createSpy('treeComplete');
|
||||
});
|
||||
|
||||
processor.execute(treeComplete);
|
||||
const promise = processor.execute();
|
||||
|
||||
expect(queueRunner).toHaveBeenCalledWith({
|
||||
onComplete: treeComplete,
|
||||
onComplete: jasmine.any(Function),
|
||||
onException: jasmine.any(Function),
|
||||
userContext: { root: 'context' },
|
||||
queueableFns: [{ fn: jasmine.any(Function) }],
|
||||
onMultipleDone: null
|
||||
});
|
||||
|
||||
queueRunner.calls.mostRecent().args[0].queueableFns[0].fn('foo');
|
||||
|
||||
const queueRunnerArgs = queueRunner.calls.mostRecent().args[0];
|
||||
queueRunnerArgs.queueableFns[0].fn('foo');
|
||||
expect(leaf.execute).toHaveBeenCalledWith(queueRunner, 'foo', false, false);
|
||||
|
||||
queueRunnerArgs.onComplete();
|
||||
await expectAsync(promise).toBeResolvedTo(undefined);
|
||||
});
|
||||
|
||||
it('runs a node with no children', function() {
|
||||
it('runs a node with no children', async function() {
|
||||
const node = new Node({ userContext: { node: 'context' } }),
|
||||
root = new Node({ children: [node], userContext: { root: 'context' } }),
|
||||
nodeStart = jasmine.createSpy('nodeStart'),
|
||||
@@ -313,21 +315,20 @@ describe('TreeProcessor', function() {
|
||||
nodeComplete: nodeComplete,
|
||||
queueRunnerFactory: queueRunner
|
||||
}),
|
||||
treeComplete = jasmine.createSpy('treeComplete'),
|
||||
nodeDone = jasmine.createSpy('nodeDone');
|
||||
|
||||
processor.execute(treeComplete);
|
||||
const promise = processor.execute();
|
||||
|
||||
expect(queueRunner).toHaveBeenCalledWith({
|
||||
onComplete: treeComplete,
|
||||
onComplete: jasmine.any(Function),
|
||||
onException: jasmine.any(Function),
|
||||
userContext: { root: 'context' },
|
||||
queueableFns: [{ fn: jasmine.any(Function) }],
|
||||
onMultipleDone: null
|
||||
});
|
||||
|
||||
queueRunner.calls.mostRecent().args[0].queueableFns[0].fn(nodeDone);
|
||||
|
||||
const queueRunnerArgs = queueRunner.calls.mostRecent().args[0];
|
||||
queueRunnerArgs.queueableFns[0].fn(nodeDone);
|
||||
expect(queueRunner).toHaveBeenCalledWith({
|
||||
onComplete: jasmine.any(Function),
|
||||
onMultipleDone: null,
|
||||
@@ -348,6 +349,9 @@ describe('TreeProcessor', function() {
|
||||
{ my: 'result' },
|
||||
jasmine.any(Function)
|
||||
);
|
||||
|
||||
queueRunnerArgs.onComplete();
|
||||
await expectAsync(promise).toBeResolvedTo(undefined);
|
||||
});
|
||||
|
||||
it('runs a node with children', function() {
|
||||
|
||||
@@ -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']);
|
||||
|
||||
@@ -3943,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() {},
|
||||
|
||||
@@ -80,7 +80,8 @@ getJasmineRequireObj().clearStack = function(j$) {
|
||||
SAFARI ||
|
||||
j$.util.isUndefined(global.MessageChannel) /* tests */
|
||||
) {
|
||||
// queueMicrotask is dramatically faster than MessageChannel in Safari.
|
||||
// queueMicrotask is dramatically faster than MessageChannel in Safari,
|
||||
// at least through version 16.
|
||||
// Some of our own integration tests provide a mock queueMicrotask in all
|
||||
// environments because it's simpler to mock than MessageChannel.
|
||||
return browserQueueMicrotaskImpl(global);
|
||||
|
||||
@@ -701,12 +701,12 @@ getJasmineRequireObj().Env = function(j$) {
|
||||
function specStarted(spec, suite, next) {
|
||||
runner.currentSpec = spec;
|
||||
runableResources.initForRunable(spec.id, suite.id);
|
||||
reporter.specStarted(spec.result, next);
|
||||
reporter.specStarted(spec.result).then(next);
|
||||
}
|
||||
|
||||
function reportSpecDone(spec, result, next) {
|
||||
spec.reportedDone = true;
|
||||
reporter.specDone(result, next);
|
||||
reporter.specDone(result).then(next);
|
||||
}
|
||||
|
||||
this.it = function(description, fn, timeout) {
|
||||
|
||||
@@ -44,18 +44,38 @@ getJasmineRequireObj().ExceptionFormatter = function(j$) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const stackTrace = new j$.StackTrace(error);
|
||||
const lines = filterJasmine(stackTrace);
|
||||
let result = '';
|
||||
const lines = this.stack_(error, {
|
||||
messageHandling: omitMessage ? 'omit' : undefined
|
||||
});
|
||||
return lines.join('\n');
|
||||
};
|
||||
|
||||
if (stackTrace.message && !omitMessage) {
|
||||
// messageHandling can be falsy (unspecified), 'omit', or 'require'
|
||||
this.stack_ = function(error, { messageHandling }) {
|
||||
let lines = formatProperties(error).split('\n');
|
||||
|
||||
if (lines[lines.length - 1] === '') {
|
||||
lines.pop();
|
||||
}
|
||||
|
||||
const stackTrace = new j$.StackTrace(error);
|
||||
lines = lines.concat(filterJasmine(stackTrace));
|
||||
|
||||
if (messageHandling === 'require') {
|
||||
lines.unshift(stackTrace.message || 'Error: ' + error.message);
|
||||
} else if (messageHandling !== 'omit' && stackTrace.message) {
|
||||
lines.unshift(stackTrace.message);
|
||||
}
|
||||
|
||||
result += formatProperties(error);
|
||||
result += lines.join('\n');
|
||||
if (error.cause) {
|
||||
const substack = this.stack_(error.cause, {
|
||||
messageHandling: 'require'
|
||||
});
|
||||
substack[0] = 'Caused by: ' + substack[0];
|
||||
lines = lines.concat(substack);
|
||||
}
|
||||
|
||||
return result;
|
||||
return lines;
|
||||
};
|
||||
|
||||
function filterJasmine(stackTrace) {
|
||||
|
||||
@@ -5,7 +5,7 @@ getJasmineRequireObj().ReportDispatcher = function(j$) {
|
||||
for (const method of dispatchedMethods) {
|
||||
this[method] = (function(m) {
|
||||
return function() {
|
||||
dispatch(m, arguments);
|
||||
return dispatch(m, arguments);
|
||||
};
|
||||
})(method);
|
||||
}
|
||||
@@ -31,25 +31,25 @@ getJasmineRequireObj().ReportDispatcher = function(j$) {
|
||||
if (reporters.length === 0 && fallbackReporter !== null) {
|
||||
reporters.push(fallbackReporter);
|
||||
}
|
||||
const onComplete = args[args.length - 1];
|
||||
args = Array.from(args).splice(0, args.length - 1);
|
||||
const fns = [];
|
||||
for (const reporter of reporters) {
|
||||
addFn(fns, reporter, method, args);
|
||||
}
|
||||
|
||||
queueRunnerFactory({
|
||||
queueableFns: fns,
|
||||
onComplete: onComplete,
|
||||
isReporter: true,
|
||||
onMultipleDone: function() {
|
||||
onLateError(
|
||||
new Error(
|
||||
"An asynchronous reporter callback called its 'done' callback " +
|
||||
'more than once.'
|
||||
)
|
||||
);
|
||||
}
|
||||
return new Promise(function(resolve) {
|
||||
queueRunnerFactory({
|
||||
queueableFns: fns,
|
||||
onComplete: resolve,
|
||||
isReporter: true,
|
||||
onMultipleDone: function() {
|
||||
onLateError(
|
||||
new Error(
|
||||
"An asynchronous reporter callback called its 'done' callback " +
|
||||
'more than once.'
|
||||
)
|
||||
);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -37,7 +37,6 @@ getJasmineRequireObj().Runner = function(j$) {
|
||||
this.executedBefore_ = true;
|
||||
|
||||
this.hasFailures = false;
|
||||
const totalSpecsDefined = this.totalSpecsDefined_();
|
||||
const focusedRunables = this.focusedRunables_();
|
||||
const config = this.getConfig_();
|
||||
|
||||
@@ -51,7 +50,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({
|
||||
@@ -76,7 +75,7 @@ getJasmineRequireObj().Runner = function(j$) {
|
||||
nodeStart: (suite, next) => {
|
||||
this.currentlyExecutingSuites_.push(suite);
|
||||
this.runableResources_.initForRunable(suite.id, suite.parentSuite.id);
|
||||
this.reporter_.suiteStarted(suite.result, next);
|
||||
this.reporter_.suiteStarted(suite.result).then(next);
|
||||
suite.startTimer();
|
||||
},
|
||||
nodeComplete: (suite, result, next) => {
|
||||
@@ -114,106 +113,97 @@ getJasmineRequireObj().Runner = function(j$) {
|
||||
);
|
||||
}
|
||||
|
||||
return this.execute2_(runablesToRun, order, processor);
|
||||
}
|
||||
|
||||
async execute2_(runablesToRun, order, processor) {
|
||||
const totalSpecsDefined = this.totalSpecsDefined_();
|
||||
|
||||
this.runableResources_.initForRunable(this.topSuite_.id);
|
||||
const jasmineTimer = new j$.Timer();
|
||||
jasmineTimer.start();
|
||||
|
||||
return new Promise(resolve => {
|
||||
/**
|
||||
* Information passed to the {@link Reporter#jasmineStarted} event.
|
||||
* @typedef JasmineStartedInfo
|
||||
* @property {Int} totalSpecsDefined - The total number of specs defined in this suite.
|
||||
* @property {Order} order - Information about the ordering (random or not) of this execution of the suite.
|
||||
* @since 2.0.0
|
||||
*/
|
||||
this.reporter_.jasmineStarted(
|
||||
{
|
||||
totalSpecsDefined,
|
||||
order: order
|
||||
},
|
||||
() => {
|
||||
this.currentlyExecutingSuites_.push(this.topSuite_);
|
||||
|
||||
processor.execute(() => {
|
||||
(async () => {
|
||||
if (this.topSuite_.hadBeforeAllFailure) {
|
||||
await this.reportChildrenOfBeforeAllFailure_(this.topSuite_);
|
||||
}
|
||||
|
||||
this.runableResources_.clearForRunable(this.topSuite_.id);
|
||||
this.currentlyExecutingSuites_.pop();
|
||||
let overallStatus, incompleteReason;
|
||||
|
||||
if (
|
||||
this.hasFailures ||
|
||||
this.topSuite_.result.failedExpectations.length > 0
|
||||
) {
|
||||
overallStatus = 'failed';
|
||||
} else if (focusedRunables.length > 0) {
|
||||
overallStatus = 'incomplete';
|
||||
incompleteReason = 'fit() or fdescribe() was found';
|
||||
} else if (totalSpecsDefined === 0) {
|
||||
overallStatus = 'incomplete';
|
||||
incompleteReason = 'No specs found';
|
||||
} else {
|
||||
overallStatus = 'passed';
|
||||
}
|
||||
|
||||
/**
|
||||
* Information passed to the {@link Reporter#jasmineDone} event.
|
||||
* @typedef JasmineDoneInfo
|
||||
* @property {OverallStatus} overallStatus - The overall result of the suite: 'passed', 'failed', or 'incomplete'.
|
||||
* @property {Int} totalTime - The total time (in ms) that it took to execute the suite
|
||||
* @property {IncompleteReason} incompleteReason - Explanation of why the suite was incomplete.
|
||||
* @property {Order} order - Information about the ordering (random or not) of this execution of the suite.
|
||||
* @property {Expectation[]} failedExpectations - List of expectations that failed in an {@link afterAll} at the global level.
|
||||
* @property {Expectation[]} deprecationWarnings - List of deprecation warnings that occurred at the global level.
|
||||
* @since 2.4.0
|
||||
*/
|
||||
const jasmineDoneInfo = {
|
||||
overallStatus: overallStatus,
|
||||
totalTime: jasmineTimer.elapsed(),
|
||||
incompleteReason: incompleteReason,
|
||||
order: order,
|
||||
failedExpectations: this.topSuite_.result.failedExpectations,
|
||||
deprecationWarnings: this.topSuite_.result.deprecationWarnings
|
||||
};
|
||||
this.topSuite_.reportedDone = true;
|
||||
this.reporter_.jasmineDone(jasmineDoneInfo, function() {
|
||||
resolve(jasmineDoneInfo);
|
||||
});
|
||||
})();
|
||||
});
|
||||
}
|
||||
);
|
||||
/**
|
||||
* Information passed to the {@link Reporter#jasmineStarted} event.
|
||||
* @typedef JasmineStartedInfo
|
||||
* @property {Int} totalSpecsDefined - The total number of specs defined in this suite.
|
||||
* @property {Order} order - Information about the ordering (random or not) of this execution of the suite.
|
||||
* @since 2.0.0
|
||||
*/
|
||||
await this.reporter_.jasmineStarted({
|
||||
totalSpecsDefined,
|
||||
order: order
|
||||
});
|
||||
|
||||
this.currentlyExecutingSuites_.push(this.topSuite_);
|
||||
await processor.execute();
|
||||
|
||||
if (this.topSuite_.hadBeforeAllFailure) {
|
||||
await this.reportChildrenOfBeforeAllFailure_(this.topSuite_);
|
||||
}
|
||||
|
||||
this.runableResources_.clearForRunable(this.topSuite_.id);
|
||||
this.currentlyExecutingSuites_.pop();
|
||||
let overallStatus, incompleteReason;
|
||||
|
||||
if (
|
||||
this.hasFailures ||
|
||||
this.topSuite_.result.failedExpectations.length > 0
|
||||
) {
|
||||
overallStatus = 'failed';
|
||||
} else if (this.focusedRunables_().length > 0) {
|
||||
overallStatus = 'incomplete';
|
||||
incompleteReason = 'fit() or fdescribe() was found';
|
||||
} else if (totalSpecsDefined === 0) {
|
||||
overallStatus = 'incomplete';
|
||||
incompleteReason = 'No specs found';
|
||||
} else {
|
||||
overallStatus = 'passed';
|
||||
}
|
||||
|
||||
/**
|
||||
* Information passed to the {@link Reporter#jasmineDone} event.
|
||||
* @typedef JasmineDoneInfo
|
||||
* @property {OverallStatus} overallStatus - The overall result of the suite: 'passed', 'failed', or 'incomplete'.
|
||||
* @property {Int} totalTime - The total time (in ms) that it took to execute the suite
|
||||
* @property {IncompleteReason} incompleteReason - Explanation of why the suite was incomplete.
|
||||
* @property {Order} order - Information about the ordering (random or not) of this execution of the suite.
|
||||
* @property {Expectation[]} failedExpectations - List of expectations that failed in an {@link afterAll} at the global level.
|
||||
* @property {Expectation[]} deprecationWarnings - List of deprecation warnings that occurred at the global level.
|
||||
* @since 2.4.0
|
||||
*/
|
||||
const jasmineDoneInfo = {
|
||||
overallStatus: overallStatus,
|
||||
totalTime: jasmineTimer.elapsed(),
|
||||
incompleteReason: incompleteReason,
|
||||
order: order,
|
||||
failedExpectations: this.topSuite_.result.failedExpectations,
|
||||
deprecationWarnings: this.topSuite_.result.deprecationWarnings
|
||||
};
|
||||
this.topSuite_.reportedDone = true;
|
||||
await this.reporter_.jasmineDone(jasmineDoneInfo);
|
||||
return jasmineDoneInfo;
|
||||
}
|
||||
|
||||
reportSuiteDone_(suite, result, next) {
|
||||
suite.reportedDone = true;
|
||||
this.reporter_.suiteDone(result, next);
|
||||
this.reporter_.suiteDone(result).then(next);
|
||||
}
|
||||
|
||||
async reportChildrenOfBeforeAllFailure_(suite) {
|
||||
for (const child of suite.children) {
|
||||
if (child instanceof j$.Suite) {
|
||||
await new Promise(resolve => {
|
||||
this.reporter_.suiteStarted(child.result, resolve);
|
||||
});
|
||||
await this.reporter_.suiteStarted(child.result);
|
||||
await this.reportChildrenOfBeforeAllFailure_(child);
|
||||
|
||||
// Marking the suite passed is consistent with how suites that
|
||||
// contain failed specs but no suite-level failures are reported.
|
||||
child.result.status = 'passed';
|
||||
|
||||
await new Promise(resolve => {
|
||||
this.reporter_.suiteDone(child.result, resolve);
|
||||
});
|
||||
await this.reporter_.suiteDone(child.result);
|
||||
} else {
|
||||
/* a spec */
|
||||
await new Promise(resolve => {
|
||||
this.reporter_.specStarted(child.result, resolve);
|
||||
});
|
||||
await this.reporter_.specStarted(child.result);
|
||||
|
||||
child.addExpectationResult(
|
||||
false,
|
||||
|
||||
@@ -39,7 +39,7 @@ getJasmineRequireObj().Spec = function(j$) {
|
||||
|
||||
/**
|
||||
* @typedef SpecResult
|
||||
* @property {Int} id - The unique id of this spec.
|
||||
* @property {String} id - The unique id of this spec.
|
||||
* @property {String} description - The description passed to the {@link it} that created this spec.
|
||||
* @property {String} fullName - The full description including all ancestors of this spec.
|
||||
* @property {Expectation[]} failedExpectations - The list of expectations that failed during execution of this spec.
|
||||
|
||||
@@ -106,7 +106,7 @@ getJasmineRequireObj().Suite = function(j$) {
|
||||
Suite.prototype.reset = function() {
|
||||
/**
|
||||
* @typedef SuiteResult
|
||||
* @property {Int} id - The unique id of this suite.
|
||||
* @property {String} id - The unique id of this suite.
|
||||
* @property {String} description - The description text passed to the {@link describe} that made this suite.
|
||||
* @property {String} fullName - The full description including all ancestors of this suite.
|
||||
* @property {Expectation[]} failedExpectations - The list of expectations that failed in an {@link afterAll} for this suite.
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -27,7 +27,7 @@ getJasmineRequireObj().TreeProcessor = function() {
|
||||
return stats;
|
||||
};
|
||||
|
||||
this.execute = function(done) {
|
||||
this.execute = async function() {
|
||||
if (!processed) {
|
||||
this.processTree();
|
||||
}
|
||||
@@ -38,16 +38,18 @@ getJasmineRequireObj().TreeProcessor = function() {
|
||||
|
||||
const childFns = wrapChildren(tree, 0);
|
||||
|
||||
queueRunnerFactory({
|
||||
queueableFns: childFns,
|
||||
userContext: tree.sharedUserContext(),
|
||||
onException: function() {
|
||||
tree.handleException.apply(tree, arguments);
|
||||
},
|
||||
onComplete: done,
|
||||
onMultipleDone: tree.onMultipleDone
|
||||
? tree.onMultipleDone.bind(tree)
|
||||
: null
|
||||
await new Promise(function(resolve) {
|
||||
queueRunnerFactory({
|
||||
queueableFns: childFns,
|
||||
userContext: tree.sharedUserContext(),
|
||||
onException: function() {
|
||||
tree.handleException.apply(tree, arguments);
|
||||
},
|
||||
onComplete: resolve,
|
||||
onMultipleDone: tree.onMultipleDone
|
||||
? tree.onMultipleDone.bind(tree)
|
||||
: null
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@@ -652,7 +652,7 @@ getJasmineRequireObj().MatchersUtil = function(j$) {
|
||||
* };
|
||||
* }
|
||||
*
|
||||
* var actual = {
|
||||
* const actual = {
|
||||
* n: 2,
|
||||
* otherFields: "don't care"
|
||||
* };
|
||||
|
||||
@@ -6,7 +6,7 @@ getJasmineRequireObj().toHaveClass = function(j$) {
|
||||
* @since 3.0.0
|
||||
* @param {Object} expected - The class name to test for
|
||||
* @example
|
||||
* var el = document.createElement('div');
|
||||
* const el = document.createElement('div');
|
||||
* el.className = 'foo bar baz';
|
||||
* expect(el).toHaveClass('bar');
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user