Compare commits

..

30 Commits

Author SHA1 Message Date
Steve Gravrock
f509078020 Bump version to 4.6.0 2023-03-15 17:21:56 -07:00
Steve Gravrock
ff237f4b66 Fixed sass version pinning 2023-03-11 14:26:22 -08:00
Steve Gravrock
e42e3d9e00 Pin sass to the last version that works on Node 12 2023-03-11 14:19:16 -08:00
Steve Gravrock
166e5f4d6c Report the ID of each suite/spec's parent
This is intended to support parallel execution, which is planned for a
future release of Jasmine. Because the execution of unrelated suites will
interleave when run in parallel, reporters will not be able to assume
that the most recent `suiteStarted` event identifies the parent of the
current suite/spec. By adding this feature now, we allow reporters to
support both parallel execution and at least some 4.x versions without
having to implement two different ways of finding the parent suite.
2023-02-25 10:24:14 -08:00
Steve Gravrock
6ad8d20694 Report the path/url of the file that the spec/suite was defined in
Fixes #1884
2023-02-15 21:39:21 -08:00
Steve Gravrock
bc3a495160 Pin eslint-plugin-compat to <4.1.0 to fix import error on CI
See <https://github.com/amilajack/eslint-plugin-compat/issues/528>.
2023-02-07 18:46:17 -08:00
Steve Gravrock
b323631611 Pin Grunt to <1.6.0 for compatiblity with Node 12 2023-01-30 17:57:47 -08:00
Steve Gravrock
e8767ba660 Removed "Does the problem occur with the latest version of jasmine-core" from issue templates 2023-01-25 20:50:51 -08:00
Steve Gravrock
169a2a8ad2 Upgraded to new issue templates 2022-11-20 14:01:43 -08:00
Steve Gravrock
b267029301 Revert "Upgraded to new issue templates"
This reverts commit cf574634b8.
2022-11-20 13:58:20 -08:00
Steve Gravrock
cf574634b8 Upgraded to new issue templates 2022-11-20 13:56:47 -08:00
Steve Gravrock
f8c01574e6 Added Firefox 102 (current ESR) to browser list in README 2022-10-29 15:26:30 -07:00
Steve Gravrock
481f1e7c5c Bump version to 4.5.0 2022-10-29 14:48:32 -07:00
Steve Gravrock
5e650953cd Added Safari 16 to supported browsers 2022-10-22 13:08:10 -07:00
Steve Gravrock
87f9ab29df Fixed the jsdoc types of SuiteResult and SpecResult ids 2022-10-19 17:20:24 -07:00
Steve Gravrock
b831e81074 Include inner exceptions in stack traces 2022-09-24 12:12:21 -07:00
Steve Gravrock
4c13c2b00b Replaced var with const in API doc examples 2022-09-24 10:12:22 -07:00
Steve Gravrock
4cd190b232 Merge branch 'internal-async' 2022-09-18 13:31:43 -07:00
Steve Gravrock
d4025999b7 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.
2022-09-17 13:24:45 -07:00
Steve Gravrock
44f331f43d Updated the style of the examples
* const/let instead of var
* classes
* pass our own eslint checks
2022-09-17 12:00:20 -07:00
Steve Gravrock
59848ca151 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
2022-09-03 12:36:35 -07:00
Steve Gravrock
c14bfe3e5f 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.
2022-09-03 11:02:13 -07:00
Steve Gravrock
26c48ab324 Bump version to 4.4.0 2022-09-03 09:42:39 -07:00
Steve Gravrock
cfecab9f79 Updated contributing guide 2022-08-22 18:10:33 -07:00
Steve Gravrock
b3d9435dbb Convreted TreeProcessor to async/await 2022-08-22 17:04:44 -07:00
Steve Gravrock
fec8dd37b0 Merge branch 'main' into internal-async 2022-08-22 10:46:03 -07:00
Steve Gravrock
f934e6d816 Assume that addEventListener/removeEventListener are present in browsers
Jasmine 3.0 dropped support for the last browser that didn't support
the standard event handler methods (IE 9).
2022-08-20 10:27:44 -07:00
Steve Gravrock
79c6bbc189 clearStack optimizations
* Avoid setTimeout in Node, because we don't need the overhead there.
* Still call setTimeout in browsers to prevent the tab from being killed.
* Use queueMicrotask in Safari, because it's dramatically faster than
  MessageChannel there.
* Continue to use MessageChannel in other supported browsers becuase it's
  somewhat faster than queueMicrotask there.
* Don't use setImmediate any more because there's a faster alternative in
  all supported envs.

In jasmine-core's own test suite, this yields a roughly 50-70% speedup
in Node, ~20% in Edge, and 75-90%(!) in Safari.
2022-08-15 17:50:49 -07:00
Steve Gravrock
5f3475342e Re-added missing JasmineStartedInfo jsdoc 2022-08-06 10:53:28 -07:00
Steve Gravrock
21f25972bb Converted ReportDispatcher to promises 2022-07-01 17:25:22 -07:00
45 changed files with 1633 additions and 812 deletions

View File

@@ -1,19 +1,10 @@
# Developing for Jasmine Core
# Contributing to Jasmine
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)
for everyone. If you want to contribute but don't know what to work on,
[issues tagged help needed](https://github.com/issues?q=is%3Aopen+is%3Aissue+org%3Ajasmine+label%3A%22help+needed%22+)
should have enough detail to get started.
## Links
- [Jasmine Google Group](http://groups.google.com/group/jasmine-js)
- [Jasmine-dev Google Group](http://groups.google.com/group/jasmine-js-dev)
- [Jasmine backlog](https://www.pivotaltracker.com/n/projects/10606)
## Before Submitting a Pull Request
1. Ensure all specs are green in browsers *and* node.
@@ -94,14 +85,7 @@ Or, How to make a successful pull request
* _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.
Key exceptions:
* Use `const` or `let` for new variable declarations, even if nearby code
uses `var`.
* New async specs should usually be async/await or promise-returning, not
callback based.
a cohesive whole.
* _Ensure the *entire* test suite is green_ in all the big browsers, Node, and
ESLint/Prettier. Your contribution shouldn't break Jasmine for other users.
@@ -119,3 +103,10 @@ chromedriver), you can also use Jasmine's CI tooling:
$ JASMINE_BROWSER=<name of browser> npm run ci
### Submitting a Pull Requeset
Once you've done the steps listed under "Before Submitting a Pull Request"
above, you can submit a pull request via the
[standard GitHub process](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/creating-a-pull-request).
TL;DR: Fork the repository, push your work up to your fork, and create a PR from
there.

View File

@@ -1,47 +0,0 @@
## Are you creating an issue in the correct repository?
- 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)
- [Karma](https://github.com/karma-runner/karma/issues)
- [Grunt Contrib Jasmine](https://github.com/gruntjs/grunt-contrib-jasmine/issues)
<!--- Provide a general summary of the issue in the Title above -->
## Expected Behavior
<!--- If you're describing a bug, tell us what should happen -->
<!--- If you're suggesting a change/improvement, tell us how it should work -->
## Current Behavior
<!--- If describing a bug, tell us what happens instead of the expected behavior -->
<!--- If suggesting a change/improvement, explain the difference from current behavior -->
## Possible Solution
<!--- Not obligatory, but suggest a fix/reason for the bug, -->
<!--- or ideas how to implement the addition or change -->
## Suite that reproduces the behavior (for bugs)
<!--- Provide a sample suite that reproduces the bug. -->
```javascript
describe("sample", function() {
});
```
## Context
<!--- How has this issue affected you? What are you trying to accomplish? -->
<!--- Providing context helps us come up with a solution that is most useful in the real world -->
## Your Environment
<!--- Include as many relevant details about the environment you experienced the bug in -->
* Version used:
* Environment name and version (e.g. Chrome 39, node.js 5.4):
* Operating System and version (desktop or mobile):
* Link to your project:

98
.github/ISSUE_TEMPLATE/bug_report.yml vendored Normal file
View File

@@ -0,0 +1,98 @@
name: Bug Report
description: I think I've found a bug in Jasmine
labels: ["unconfirmed bug"]
body:
- type: markdown
attributes:
value: |
Thanks for taking the time to report a bug. Please follow these steps first.
## Troubleshooting
Please take the time to rule out issues with your code or third party libraries before filing a bug report. If you are reporting an error, try to determine whether the error is coming from Jasmine, another library, or your own code.
Check the [FAQ](https://jasmine.github.io/pages/faq.html) and any other relevant [documentation](https://jasmine.github.io/pages/docs_home.html) to see if your issue has already been addressed.
## Special troubleshooting steps for asynchronous scenarios
If the issue has to do with testing asynchronous code, please read the [async tutorial](https://jasmine.github.io/tutorials/async) and the async section of the FAQ. In particular, check for the following common errors:
* Are you trying to write a synchronous test for asynchronous code?
* Does the test signal completion before the code under test finishes?
* Do expectations run before the code that they're trying to verify?
## Try the latest version of Jasmine
If at all possible, upgrade to the latest versions of `jasmine-core` and any other relevant packages (e.g. `jasmine`, `jasmine-browser-runner`). If you can't do that, please check the [release notes](https://github.com/jasmine/jasmine/tree/main/release_notes) for all newer versions to make sure that the bug hasn't already been fixed.
## Put together a [minimal, reproducible example](https://stackoverflow.com/help/minimal-reproducible-example)
Please help us help you by creating a minimal but complete setup that demonstrates the problem. Remove any code and libraries that aren't absolutely necessary, but make sure it doesn't depend on any code you haven't included. In many cases a simple code snippet is enough. In cases involving external libraries, *especially* Karma or Angular, we're likely to need a runable Git repository or jsbin/stackblitz/etc.
**If we can't reproduce it, we can't fix it. Bug reports without a minimal, reproducible example are very likely to be closed.**
- type: textarea
id: steps-to-reproduce
attributes:
label: Steps to Reproduce
placeholder: |
Example steps:
1. Paste the example code below into `mySpec.js`.
2. Run `npx jasmine@<some version> mySpec.js`
validations:
required: true
- type: textarea
id: expected-behavior
attributes:
label: Expected Behavior
description: What do you think should have happened?
validations:
required: true
- type: textarea
id: actual-behavior
attributes:
label: Actual Behavior
description: What happened instead?
validations:
required: true
- type: textarea
id: code-sample
attributes:
label: Example code that reproduces the problem
description: Please include either a code snippet that reproduces the problem or a link to a repository or jsbin/stackblitz/etc containing a minimal, reproducible example as described above.
render: JavaScript
validations:
required: true
- type: textarea
id: possible-solution
attributes:
label: Possible Solution
description: This is optional, but if you have an idea for how to fix the bug we'd like to hear it.
- type: textarea
id: context
attributes:
label: Context
description: How has this issue affected you? What are you trying to accomplish? By providing context, you can help us come up with a solution that is most useful in the real world.
- type: input
id: jasmine-core-version
attributes:
label: jasmine-core version
validations:
required: true
- type: textarea
id: other-versions
attributes:
label: Versions of other relevant packages
placeholder: |
jasmine-browser-runner 1.2.0
fancy-reporter 132.4.8
- type: input
id: browser-or-node-version
attributes:
label: Node.js or browser version
placeholder: E.g. "node 16.2.0" or "Safari 15"
validations:
required: true
- type: input
id: os
attributes:
label: Operating System
placeholder: E.g. "Windows 10", "MacOS 12.5", "MCC Interim Linux 0.99.p8"
validations:
required: true

14
.github/ISSUE_TEMPLATE/config.yml vendored Normal file
View File

@@ -0,0 +1,14 @@
blank_issues_enabled: false
contact_links:
- name: Issues with the `jasmine` CLI
url: https://github.com/jasmine/jasmine-npm/issues
about: Please create issues related to the `jasmine` package in its repository.
- name: Issues with jasmine-browser-runner
url: https://github.com/jasmine/jasmine-browser-runner/issues
about: Please create issues related to the `jasmine-browser-runner` package in its repository.
- name: Documentation issues
url: https://github.com/jasmine/jasmine.github.io/issues
about: Please create documentation issues in the docs repository.
- name: TypeScript issues
url: https://github.com/DefinitelyTyped/DefinitelyTyped/discussions
about: Please create issues related to TypeScript compilation errors or other problems with type definitions at DefinitelyTyped.

View File

@@ -0,0 +1,31 @@
name: Feature Proposal
description: I'd like to propose a new feature
labels: ["feature request"]
body:
- type: markdown
attributes:
value: Thanks for taking the time to propose a new feature. Although Jasmine is mostly feature complete, we're always open to hearing new ideas.
- type: textarea
id: description
attributes:
label: Feature Proposal
validations:
required: true
- type: textarea
id: context
attributes:
label: Context
description: How would this feature be useful to you? What are you trying to accomplish? By providing context, you can help us come up with a solution that is most useful in the real world.
validations:
required: true
- type: textarea
id: example
attributes:
label: Example
description: If you're proposing a new API or something similar, please show an example of how it would be used.
render: JavaScript
- type: textarea
id: other-info
attributes:
label: Other Information
description: Anything else that you think would be helpful.

View File

@@ -0,0 +1,73 @@
name: Question or Support Request
description: I need help using Jasmine
labels: ["question"]
body:
- type: markdown
attributes:
value: |
Jasmine is supported by volunteers working in their free time. Although we're generally willing to help, we're going to ask you to put in some effort first to help us help you.
## Troubleshooting
Please take the time to rule out problems with your code or third party libraries before opening an issue. If you're running into an error, try to determine whether the error is coming from Jasmine, another library, or your own code.
Check the [FAQ](https://jasmine.github.io/pages/faq.html) and any other relevant [documentation](https://jasmine.github.io/pages/docs_home.html) to see if your question has already been answered. Consider searching [Stack Overflow](https://stackoverflow.com/questions/tagged/jasmine) and past issues in this repository for related questions as well.
## Special troubleshooting steps for asynchronous scenarios
If the issue has to do with testing asynchronous code, please read the [async tutorial](https://jasmine.github.io/tutorials/async) and the async section of the FAQ. In particular, check for the following common errors:
* Are you trying to write a synchronous test for asynchronous code?
* Does the test signal completion before the code under test finishes?
* Do expectations run before the code that they're trying to verify?
## Consider asking Angular questions in an Angular forum
Questions like "how do I test this Angular service" are mostly about Angular, not Jasmine. You'll likely get better responses in an Angular forum. Here's a rule of thumb: If you can't demonstrate the problem without Angular, you probably have an Angular question.
## Try the latest version of Jasmine
If at all possible, upgrade to the latest versions of `jasmine-core` and any other relevant packages (e.g. `jasmine`, `jasmine-browser-runner`).
## Put together a [minimal, reproducible example](https://stackoverflow.com/help/minimal-reproducible-example)
Please help us help you by creating a minimal but complete setup that demonstrates the problem. Remove any code and libraries that aren't absolutely necessary, but make sure it doesn't depend on any code you haven't included. In many cases a simple code snippet is enough. In cases involving external libraries, *especially* Karma or Angular, we're likely to need a runable Git repository or jsbin/stackblitz/etc.
- type: textarea
id: question
attributes:
label: Your question
description: Clearly describe what you'd like help with.
validations:
required: true
- type: textarea
id: code-sample
attributes:
label: Example code that demonstrates the problem
description: Please include either a code snippet that demonstrates the problem or a link to a repository or jsbin/stackblitz/etc containing a minimal, reproducible example as described above.
render: JavaScript
validations:
required: true
- type: input
id: jasmine-core-version
attributes:
label: jasmine-core version
validations:
required: true
- type: textarea
id: other-versions
attributes:
label: Versions of other relevant packages
placeholder: |
jasmine-browser-runner 1.2.0
fancy-reporter 132.4.8
- type: input
id: browser-or-node-version
attributes:
label: Node.js or browser version
placeholder: E.g. "node 16.2.0" or "Safari 15"
validations:
required: true
- type: input
id: os
attributes:
label: Operating System
placeholder: E.g. "Windows 10", "MacOS 12.5", "MCC Interim Linux 0.99.p8"
validations:
required: true

View File

@@ -33,9 +33,9 @@ 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 |
| Firefox | Evergreen, 91, 102 |
| Edge | Evergreen |
For evergreen browsers, each version of Jasmine is tested against the version of the browser that is available to us

View File

@@ -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.

View File

@@ -1,5 +1,5 @@
/*
Copyright (c) 2008-2022 Pivotal Labs
Copyright (c) 2008-2023 Pivotal Labs
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the

View File

@@ -1,5 +1,5 @@
/*
Copyright (c) 2008-2022 Pivotal Labs
Copyright (c) 2008-2023 Pivotal Labs
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the

View File

@@ -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;

View File

@@ -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;

View File

@@ -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
}
};
}
};
}

View File

@@ -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');
});
});
});

View File

@@ -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');
});
});
});

View File

@@ -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

View File

@@ -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);
}
}

View File

@@ -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");
};

View File

@@ -1,5 +1,5 @@
/*
Copyright (c) 2008-2022 Pivotal Labs
Copyright (c) 2008-2023 Pivotal Labs
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the

View File

@@ -1,5 +1,5 @@
/*
Copyright (c) 2008-2022 Pivotal Labs
Copyright (c) 2008-2023 Pivotal Labs
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
@@ -741,6 +741,8 @@ getJasmineRequireObj().Spec = function(j$) {
this.asyncExpectationFactory = attrs.asyncExpectationFactory;
this.resultCallback = attrs.resultCallback || function() {};
this.id = attrs.id;
this.filename = attrs.filename;
this.parentSuiteId = attrs.parentSuiteId;
this.description = attrs.description || '';
this.queueableFn = attrs.queueableFn;
this.beforeAndAfterFns =
@@ -774,35 +776,7 @@ getJasmineRequireObj().Spec = function(j$) {
this.exclude();
}
/**
* @typedef SpecResult
* @property {Int} 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.
* @property {Expectation[]} passedExpectations - The list of expectations that passed during execution of this spec.
* @property {Expectation[]} deprecationWarnings - The list of deprecation warnings that occurred during execution this spec.
* @property {String} pendingReason - If the spec is {@link pending}, this will be the reason.
* @property {String} status - Once the spec has completed, this string represents the pass/fail status of this spec.
* @property {number} duration - The time in ms used by the spec execution, including any before/afterEach.
* @property {Object} properties - User-supplied properties, if any, that were set using {@link Env#setSpecProperty}
* @property {DebugLogEntry[]|null} debugLogs - Messages, if any, that were logged using {@link jasmine.debugLog} during a failing spec.
* @since 2.0.0
*/
this.result = {
id: this.id,
description: this.description,
fullName: this.getFullName(),
failedExpectations: [],
passedExpectations: [],
deprecationWarnings: [],
pendingReason: '',
duration: null,
properties: null,
debugLogs: null
};
this.reportedDone = false;
this.reset();
}
Spec.prototype.addExpectationResult = function(passed, data, isError) {
@@ -912,14 +886,33 @@ getJasmineRequireObj().Spec = function(j$) {
};
Spec.prototype.reset = function() {
/**
* @typedef SpecResult
* @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 {String|null} parentSuiteId - The ID of the suite containing this spec, or null if this spec is not in a describe().
* @property {String} filename - The name of the file the spec was defined in.
* @property {Expectation[]} failedExpectations - The list of expectations that failed during execution of this spec.
* @property {Expectation[]} passedExpectations - The list of expectations that passed during execution of this spec.
* @property {Expectation[]} deprecationWarnings - The list of deprecation warnings that occurred during execution this spec.
* @property {String} pendingReason - If the spec is {@link pending}, this will be the reason.
* @property {String} status - Once the spec has completed, this string represents the pass/fail status of this spec.
* @property {number} duration - The time in ms used by the spec execution, including any before/afterEach.
* @property {Object} properties - User-supplied properties, if any, that were set using {@link Env#setSpecProperty}
* @property {DebugLogEntry[]|null} debugLogs - Messages, if any, that were logged using {@link jasmine.debugLog} during a failing spec.
* @since 2.0.0
*/
this.result = {
id: this.id,
description: this.description,
fullName: this.getFullName(),
parentSuiteId: this.parentSuiteId,
filename: this.filename,
failedExpectations: [],
passedExpectations: [],
deprecationWarnings: [],
pendingReason: this.excludeMessage,
pendingReason: this.excludeMessage || '',
duration: null,
properties: null,
debugLogs: null
@@ -1816,17 +1809,23 @@ getJasmineRequireObj().Env = function(j$) {
this.describe = function(description, definitionFn) {
ensureIsNotNested('describe');
return suiteBuilder.describe(description, definitionFn).metadata;
const filename = callerCallerFilename();
return suiteBuilder.describe(description, definitionFn, filename)
.metadata;
};
this.xdescribe = function(description, definitionFn) {
ensureIsNotNested('xdescribe');
return suiteBuilder.xdescribe(description, definitionFn).metadata;
const filename = callerCallerFilename();
return suiteBuilder.xdescribe(description, definitionFn, filename)
.metadata;
};
this.fdescribe = function(description, definitionFn) {
ensureIsNotNested('fdescribe');
return suiteBuilder.fdescribe(description, definitionFn).metadata;
const filename = callerCallerFilename();
return suiteBuilder.fdescribe(description, definitionFn, filename)
.metadata;
};
function specResultCallback(spec, result, next) {
@@ -1843,27 +1842,30 @@ 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) {
ensureIsNotNested('it');
return suiteBuilder.it(description, fn, timeout).metadata;
const filename = callerCallerFilename();
return suiteBuilder.it(description, fn, timeout, filename).metadata;
};
this.xit = function(description, fn, timeout) {
ensureIsNotNested('xit');
return suiteBuilder.xit(description, fn, timeout).metadata;
const filename = callerCallerFilename();
return suiteBuilder.xit(description, fn, timeout, filename).metadata;
};
this.fit = function(description, fn, timeout) {
ensureIsNotNested('fit');
return suiteBuilder.fit(description, fn, timeout).metadata;
const filename = callerCallerFilename();
return suiteBuilder.fit(description, fn, timeout, filename).metadata;
};
/**
@@ -2003,6 +2005,10 @@ getJasmineRequireObj().Env = function(j$) {
};
}
function callerCallerFilename() {
return new j$.StackTrace(new Error()).frames[3].file;
}
return Env;
};
@@ -2789,8 +2795,32 @@ getJasmineRequireObj().CallTracker = function(j$) {
getJasmineRequireObj().clearStack = function(j$) {
const maxInlineCallCount = 10;
function messageChannelImpl(global, setTimeout) {
const channel = new global.MessageChannel();
function browserQueueMicrotaskImpl(global) {
const { setTimeout, queueMicrotask } = global;
let currentCallCount = 0;
return function clearStack(fn) {
currentCallCount++;
if (currentCallCount < maxInlineCallCount) {
queueMicrotask(fn);
} else {
currentCallCount = 0;
setTimeout(fn);
}
};
}
function nodeQueueMicrotaskImpl(global) {
const { queueMicrotask } = global;
return function(fn) {
queueMicrotask(fn);
};
}
function messageChannelImpl(global) {
const { MessageChannel, setTimeout } = global;
const channel = new MessageChannel();
let head = {};
let tail = head;
@@ -2801,7 +2831,7 @@ getJasmineRequireObj().clearStack = function(j$) {
delete head.task;
if (taskRunning) {
global.setTimeout(task, 0);
setTimeout(task, 0);
} else {
try {
taskRunning = true;
@@ -2827,29 +2857,32 @@ getJasmineRequireObj().clearStack = function(j$) {
}
function getClearStack(global) {
let currentCallCount = 0;
const realSetTimeout = global.setTimeout;
const setTimeoutImpl = function clearStack(fn) {
Function.prototype.apply.apply(realSetTimeout, [global, [fn, 0]]);
};
const NODE_JS =
global.process &&
global.process.versions &&
typeof global.process.versions.node === 'string';
if (j$.isFunction_(global.setImmediate)) {
const realSetImmediate = global.setImmediate;
return function(fn) {
currentCallCount++;
const SAFARI =
global.navigator &&
/^((?!chrome|android).)*safari/i.test(global.navigator.userAgent);
if (currentCallCount < maxInlineCallCount) {
realSetImmediate(fn);
} else {
currentCallCount = 0;
setTimeoutImpl(fn);
}
};
} else if (!j$.util.isUndefined(global.MessageChannel)) {
return messageChannelImpl(global, setTimeoutImpl);
if (NODE_JS) {
// Unlike browsers, Node doesn't require us to do a periodic setTimeout
// so we avoid the overhead.
return nodeQueueMicrotaskImpl(global);
} else if (
SAFARI ||
j$.util.isUndefined(global.MessageChannel) /* tests */
) {
// 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);
} else {
return setTimeoutImpl;
// MessageChannel is faster than queueMicrotask in supported browsers
// other than Safari.
return messageChannelImpl(global);
}
}
@@ -3468,18 +3501,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) {
@@ -4055,21 +4108,14 @@ getJasmineRequireObj().GlobalErrors = function(j$) {
}
};
if (global.addEventListener) {
global.addEventListener(
'unhandledrejection',
browserRejectionHandler
);
}
global.addEventListener('unhandledrejection', browserRejectionHandler);
this.uninstall = function uninstall() {
global.onerror = originalHandler;
if (global.removeEventListener) {
global.removeEventListener(
'unhandledrejection',
browserRejectionHandler
);
}
global.removeEventListener(
'unhandledrejection',
browserRejectionHandler
);
};
}
};
@@ -5222,7 +5268,7 @@ getJasmineRequireObj().MatchersUtil = function(j$) {
* };
* }
*
* var actual = {
* const actual = {
* n: 2,
* otherFields: "don't care"
* };
@@ -6368,7 +6414,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');
*/
@@ -7700,7 +7746,7 @@ getJasmineRequireObj().ReportDispatcher = function(j$) {
for (const method of dispatchedMethods) {
this[method] = (function(m) {
return function() {
dispatch(m, arguments);
return dispatch(m, arguments);
};
})(method);
}
@@ -7726,25 +7772,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.'
)
);
}
});
});
}
@@ -8400,7 +8446,6 @@ getJasmineRequireObj().Runner = function(j$) {
this.executedBefore_ = true;
this.hasFailures = false;
const totalSpecsDefined = this.totalSpecsDefined_();
const focusedRunables = this.focusedRunables_();
const config = this.getConfig_();
@@ -8414,7 +8459,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({
@@ -8439,7 +8484,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) => {
@@ -8477,106 +8522,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,
@@ -9500,6 +9536,8 @@ getJasmineRequireObj().Suite = function(j$) {
this.id = attrs.id;
this.parentSuite = attrs.parentSuite;
this.description = attrs.description;
this.reportedParentSuiteId = attrs.reportedParentSuiteId;
this.filename = attrs.filename;
this.expectationFactory = attrs.expectationFactory;
this.asyncExpectationFactory = attrs.asyncExpectationFactory;
this.throwOnExpectationFailure = !!attrs.throwOnExpectationFailure;
@@ -9602,9 +9640,11 @@ 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 {String|null} parentSuiteId - The ID of the suite containing this suite, or null if this is not in another describe().
* @property {String} filename - The name of the file the suite was defined in.
* @property {Expectation[]} failedExpectations - The list of expectations that failed in an {@link afterAll} for this suite.
* @property {Expectation[]} deprecationWarnings - The list of deprecation warnings that occurred on this suite.
* @property {String} status - Once the suite has completed, this string represents the pass/fail status of this suite.
@@ -9616,6 +9656,8 @@ getJasmineRequireObj().Suite = function(j$) {
id: this.id,
description: this.description,
fullName: this.getFullName(),
parentSuiteId: this.reportedParentSuiteId,
filename: this.filename,
failedExpectations: [],
deprecationWarnings: [],
duration: null,
@@ -9843,9 +9885,9 @@ getJasmineRequireObj().SuiteBuilder = function(j$) {
this.focusedRunables = [];
}
describe(description, definitionFn) {
describe(description, definitionFn, filename) {
ensureIsFunction(definitionFn, 'describe');
const suite = this.suiteFactory_(description);
const suite = this.suiteFactory_(description, filename);
if (definitionFn.length > 0) {
throw new Error('describe does not expect any arguments');
}
@@ -9853,17 +9895,12 @@ 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;
}
fdescribe(description, definitionFn) {
fdescribe(description, definitionFn, filename) {
ensureIsFunction(definitionFn, 'fdescribe');
const suite = this.suiteFactory_(description);
const suite = this.suiteFactory_(description, filename);
suite.isFocused = true;
this.focusedRunables.push(suite.id);
@@ -9873,37 +9910,37 @@ getJasmineRequireObj().SuiteBuilder = function(j$) {
return suite;
}
xdescribe(description, definitionFn) {
xdescribe(description, definitionFn, filename) {
ensureIsFunction(definitionFn, 'xdescribe');
const suite = this.suiteFactory_(description);
const suite = this.suiteFactory_(description, filename);
suite.exclude();
this.addSpecsToSuite_(suite, definitionFn);
return suite;
}
it(description, fn, timeout) {
it(description, fn, timeout, filename) {
// it() sometimes doesn't have a fn argument, so only check the type if
// it's given.
if (arguments.length > 1 && typeof fn !== 'undefined') {
ensureIsFunctionOrAsync(fn, 'it');
}
return this.it_(description, fn, timeout);
return this.it_(description, fn, timeout, filename);
}
xit(description, fn, timeout) {
xit(description, fn, timeout, filename) {
// xit(), like it(), doesn't always have a fn argument, so only check the
// type when needed.
if (arguments.length > 1 && typeof fn !== 'undefined') {
ensureIsFunctionOrAsync(fn, 'xit');
}
const spec = this.it_(description, fn, timeout);
const spec = this.it_(description, fn, timeout, filename);
spec.exclude('Temporarily disabled with xit');
return spec;
}
fit(description, fn, timeout) {
fit(description, fn, timeout, filename) {
// Unlike it and xit, the function is required because it doesn't make
// sense to focus on nothing.
ensureIsFunctionOrAsync(fn, 'fit');
@@ -9911,7 +9948,7 @@ getJasmineRequireObj().SuiteBuilder = function(j$) {
if (timeout) {
j$.util.validateTimeout(timeout);
}
const spec = this.specFactory_(description, fn, timeout);
const spec = this.specFactory_(description, fn, timeout, filename);
this.currentDeclarationSuite_.addChild(spec);
this.focusedRunables.push(spec.id);
this.unfocusAncestor_();
@@ -9971,12 +10008,12 @@ getJasmineRequireObj().SuiteBuilder = function(j$) {
});
}
it_(description, fn, timeout) {
it_(description, fn, timeout, filename) {
if (timeout) {
j$.util.validateTimeout(timeout);
}
const spec = this.specFactory_(description, fn, timeout);
const spec = this.specFactory_(description, fn, timeout, filename);
if (this.currentDeclarationSuite_.markedExcluding) {
spec.exclude();
}
@@ -9985,12 +10022,17 @@ getJasmineRequireObj().SuiteBuilder = function(j$) {
return spec;
}
suiteFactory_(description) {
suiteFactory_(description, filename) {
const config = this.env_.configuration();
const parentSuite = this.currentDeclarationSuite_;
const reportedParentSuiteId =
parentSuite === this.topSuite ? null : parentSuite.id;
return new j$.Suite({
id: 'suite' + this.nextSuiteId_++,
description,
parentSuite: this.currentDeclarationSuite_,
filename,
parentSuite,
reportedParentSuiteId,
timer: new j$.Timer(),
expectationFactory: this.expectationFactory_,
asyncExpectationFactory: this.suiteAsyncExpectationFactory_,
@@ -10004,22 +10046,33 @@ 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;
}
specFactory_(description, fn, timeout) {
specFactory_(description, fn, timeout, filename) {
this.totalSpecsDefined++;
const config = this.env_.configuration();
const suite = this.currentDeclarationSuite_;
const parentSuiteId = suite === this.topSuite ? null : suite.id;
const spec = new j$.Spec({
id: 'spec' + this.nextSpecId_++,
filename,
parentSuiteId,
beforeAndAfterFns: beforeAndAfterFns(suite),
expectationFactory: this.expectationFactory_,
asyncExpectationFactory: this.specAsyncExpectationFactory_,
@@ -10176,7 +10229,7 @@ getJasmineRequireObj().TreeProcessor = function() {
return stats;
};
this.execute = function(done) {
this.execute = async function() {
if (!processed) {
this.processTree();
}
@@ -10187,16 +10240,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
});
});
};
@@ -10429,5 +10484,5 @@ getJasmineRequireObj().UserContext = function(j$) {
};
getJasmineRequireObj().version = function() {
return '4.3.0';
return '4.6.0';
};

View File

@@ -1,5 +1,5 @@
/*
Copyright (c) 2008-2022 Pivotal Labs
Copyright (c) 2008-2023 Pivotal Labs
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the

View File

@@ -1,7 +1,7 @@
{
"name": "jasmine-core",
"license": "MIT",
"version": "4.3.0",
"version": "4.6.0",
"repository": {
"type": "git",
"url": "https://github.com/jasmine/jasmine.git"
@@ -35,9 +35,9 @@
],
"devDependencies": {
"eslint": "^7.32.0",
"eslint-plugin-compat": "^4.0.0",
"eslint-plugin-compat": ">=4.0.0 <4.1.0",
"glob": "^7.2.0",
"grunt": "^1.0.4",
"grunt": ">=1.0.4 <1.6.0",
"grunt-cli": "^1.3.2",
"grunt-contrib-compress": "^2.0.0",
"grunt-contrib-concat": "^2.0.0",
@@ -48,7 +48,7 @@
"jsdom": "^19.0.0",
"load-grunt-tasks": "^5.1.0",
"prettier": "1.17.1",
"sass": "^1.45.1",
"sass": "1.58.3",
"shelljs": "^0.8.3",
"temp": "^0.9.0"
},

28
release_notes/4.4.0.md Normal file
View File

@@ -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)_

40
release_notes/4.5.0.md Normal file
View 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)_

19
release_notes/4.6.0.md Normal file
View File

@@ -0,0 +1,19 @@
# Jasmine Core 4.6.0 Release Notes
## New Features
* Report the ID of each suite/spec's parent
* Report the path/url of the file that the spec/suite was defined in
* Fixes [#1884](https://github.com/jasmine/jasmine/issues/1884)
* Added Firefox 102 (current ESR) to supported browsers
## Internal improvements
* Pinned sass to 1.58.3 for compatibility with Node 12
* Pinned eslint-plugin-compat to <4.1.0 for compatibility with Node 12
* Pinned Grunt to <1.6.0 for compatibility with Node 12
------
_Release Notes generated with _[Anchorman](http://github.com/infews/anchorman)_

View File

@@ -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

View File

@@ -9,160 +9,216 @@ describe('ClearStack', function() {
});
});
it('uses setImmediate when available', function() {
const setImmediate = jasmine
.createSpy('setImmediate')
.and.callFake(function(fn) {
fn();
}),
global = { setImmediate: setImmediate },
clearStack = jasmineUnderTest.getClearStack(global);
let called = false;
describe('in Safari', function() {
usesQueueMicrotaskWithSetTimeout(function() {
return {
navigator: {
userAgent:
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit/605.0.8 (KHTML, like Gecko) Version/15.1 Safari/605.0.8'
},
// queueMicrotask should be used even though MessageChannel is present
MessageChannel: fakeMessageChannel
};
});
});
clearStack(function() {
called = true;
describe('in browsers other than Safari', function() {
usesMessageChannel(function() {
return {
navigator: {
// Chrome's user agent string contains "Safari" so it's a good test case
userAgent:
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.0.0 Safari/537.36'
}
};
});
expect(called).toBe(true);
expect(setImmediate).toHaveBeenCalled();
describe('when MessageChannel is unavailable', function() {
usesQueueMicrotaskWithSetTimeout(function() {
return {
navigator: {
userAgent: 'CERN-LineMode/2.15 libwww/2.17b3',
MessageChannel: undefined
}
};
});
});
});
it('uses setTimeout instead of setImmediate every 10 calls to make sure we release the CPU', function() {
const setImmediate = jasmine.createSpy('setImmediate'),
setTimeout = jasmine.createSpy('setTimeout'),
global = { setImmediate: setImmediate, setTimeout: setTimeout },
clearStack = jasmineUnderTest.getClearStack(global);
clearStack(function() {});
clearStack(function() {});
clearStack(function() {});
clearStack(function() {});
clearStack(function() {});
clearStack(function() {});
clearStack(function() {});
clearStack(function() {});
clearStack(function() {});
expect(setImmediate).toHaveBeenCalled();
expect(setTimeout).not.toHaveBeenCalled();
clearStack(function() {});
expect(setImmediate.calls.count()).toEqual(9);
expect(setTimeout.calls.count()).toEqual(1);
clearStack(function() {});
expect(setImmediate.calls.count()).toEqual(10);
expect(setTimeout.calls.count()).toEqual(1);
});
it('uses MessageChannels when available', function() {
const fakeChannel = {
port1: {},
port2: {
postMessage: function() {
fakeChannel.port1.onmessage();
describe('in Node', function() {
usesQueueMicrotaskWithoutSetTimeout(function() {
return {
process: {
versions: {
node: '3.1415927'
}
}
},
global = {
MessageChannel: function() {
return fakeChannel;
}
},
clearStack = jasmineUnderTest.getClearStack(global);
let called = false;
};
});
});
clearStack(function() {
called = true;
function usesMessageChannel(makeGlobal) {
it('uses MessageChannel', function() {
const global = {
...makeGlobal(),
MessageChannel: fakeMessageChannel
};
const clearStack = jasmineUnderTest.getClearStack(global);
let called = false;
clearStack(function() {
called = true;
});
expect(called).toBe(true);
});
expect(called).toBe(true);
});
it('uses setTimeout instead of MessageChannel every 10 calls to make sure we release the CPU', function() {
const fakeChannel = {
port1: {},
port2: {
postMessage: jasmine
.createSpy('postMessage')
.and.callFake(function() {
fakeChannel.port1.onmessage();
})
}
},
setTimeout = jasmine.createSpy('setTimeout'),
global = {
it('uses setTimeout instead of MessageChannel every 10 calls to make sure we release the CPU', function() {
const fakeChannel = fakeMessageChannel();
spyOn(fakeChannel.port2, 'postMessage');
const setTimeout = jasmine.createSpy('setTimeout');
const global = {
...makeGlobal(),
setTimeout,
MessageChannel: function() {
return fakeChannel;
},
setTimeout: setTimeout
},
clearStack = jasmineUnderTest.getClearStack(global);
clearStack(function() {});
clearStack(function() {});
clearStack(function() {});
clearStack(function() {});
clearStack(function() {});
clearStack(function() {});
clearStack(function() {});
clearStack(function() {});
clearStack(function() {});
expect(fakeChannel.port2.postMessage).toHaveBeenCalled();
expect(setTimeout).not.toHaveBeenCalled();
clearStack(function() {});
expect(fakeChannel.port2.postMessage.calls.count()).toEqual(9);
expect(setTimeout.calls.count()).toEqual(1);
clearStack(function() {});
expect(fakeChannel.port2.postMessage.calls.count()).toEqual(10);
expect(setTimeout.calls.count()).toEqual(1);
});
it('calls setTimeout when onmessage is called recursively', function() {
const fakeChannel = {
port1: {},
port2: {
postMessage: function() {
fakeChannel.port1.onmessage();
}
}
},
setTimeout = jasmine.createSpy('setTimeout'),
global = {
MessageChannel: function() {
return fakeChannel;
},
setTimeout: setTimeout
},
clearStack = jasmineUnderTest.getClearStack(global),
fn = jasmine.createSpy('second clearStack function');
};
const clearStack = jasmineUnderTest.getClearStack(global);
clearStack(function() {
clearStack(fn);
for (let i = 0; i < 9; i++) {
clearStack(function() {});
}
expect(fakeChannel.port2.postMessage).toHaveBeenCalled();
expect(setTimeout).not.toHaveBeenCalled();
clearStack(function() {});
expect(fakeChannel.port2.postMessage).toHaveBeenCalledTimes(9);
expect(setTimeout).toHaveBeenCalledTimes(1);
clearStack(function() {});
expect(fakeChannel.port2.postMessage).toHaveBeenCalledTimes(10);
expect(setTimeout).toHaveBeenCalledTimes(1);
});
expect(fn).not.toHaveBeenCalled();
expect(setTimeout).toHaveBeenCalledWith(fn, 0);
});
it('calls setTimeout when onmessage is called recursively', function() {
const setTimeout = jasmine.createSpy('setTimeout');
const global = {
...makeGlobal(),
setTimeout,
MessageChannel: fakeMessageChannel
};
const clearStack = jasmineUnderTest.getClearStack(global);
const fn = jasmine.createSpy('second clearStack function');
it('falls back to setTimeout', function() {
const setTimeout = jasmine
.createSpy('setTimeout')
.and.callFake(function(fn) {
clearStack(function() {
clearStack(fn);
});
expect(fn).not.toHaveBeenCalled();
expect(setTimeout).toHaveBeenCalledWith(fn, 0);
});
}
function usesQueueMicrotaskWithSetTimeout(makeGlobal) {
it('uses queueMicrotask', function() {
const global = {
...makeGlobal(),
queueMicrotask: function(fn) {
fn();
}),
global = { setTimeout: setTimeout },
clearStack = jasmineUnderTest.getClearStack(global);
let called = false;
}
};
const clearStack = jasmineUnderTest.getClearStack(global);
let called = false;
clearStack(function() {
called = true;
clearStack(function() {
called = true;
});
expect(called).toBe(true);
});
expect(called).toBe(true);
expect(setTimeout).toHaveBeenCalledWith(jasmine.any(Function), 0);
});
it('uses setTimeout instead of queueMicrotask every 10 calls to make sure we release the CPU', function() {
const queueMicrotask = jasmine.createSpy('queueMicrotask');
const setTimeout = jasmine.createSpy('setTimeout');
const global = {
...makeGlobal(),
queueMicrotask,
setTimeout
};
const clearStack = jasmineUnderTest.getClearStack(global);
for (let i = 0; i < 9; i++) {
clearStack(function() {});
}
expect(queueMicrotask).toHaveBeenCalled();
expect(setTimeout).not.toHaveBeenCalled();
clearStack(function() {});
expect(queueMicrotask).toHaveBeenCalledTimes(9);
expect(setTimeout).toHaveBeenCalledTimes(1);
clearStack(function() {});
expect(queueMicrotask).toHaveBeenCalledTimes(10);
expect(setTimeout).toHaveBeenCalledTimes(1);
});
}
function usesQueueMicrotaskWithoutSetTimeout(makeGlobal) {
it('uses queueMicrotask', function() {
const global = {
...makeGlobal(),
queueMicrotask: function(fn) {
fn();
}
};
const clearStack = jasmineUnderTest.getClearStack(global);
let called = false;
clearStack(function() {
called = true;
});
expect(called).toBe(true);
});
it('does not use setTimeout', function() {
const queueMicrotask = jasmine.createSpy('queueMicrotask');
const setTimeout = jasmine.createSpy('setTimeout');
const global = {
...makeGlobal(),
queueMicrotask,
setTimeout
};
const clearStack = jasmineUnderTest.getClearStack(global);
clearStack(function() {});
clearStack(function() {});
clearStack(function() {});
clearStack(function() {});
clearStack(function() {});
clearStack(function() {});
clearStack(function() {});
clearStack(function() {});
clearStack(function() {});
clearStack(function() {});
expect(queueMicrotask).toHaveBeenCalledTimes(10);
expect(setTimeout).not.toHaveBeenCalled();
});
}
function fakeMessageChannel() {
const channel = {
port1: {},
port2: {
postMessage: function() {
channel.port1.onmessage();
}
}
};
return channel;
}
});

View File

@@ -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');
});
});
});
});

View File

@@ -1,8 +1,8 @@
describe('GlobalErrors', function() {
it('calls the added handler on error', function() {
const fakeGlobal = { onerror: null },
handler = jasmine.createSpy('errorHandler'),
errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
const fakeGlobal = minimalBrowserGlobal();
const handler = jasmine.createSpy('errorHandler');
const errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
errors.install();
errors.pushListener(handler);
@@ -13,10 +13,10 @@ describe('GlobalErrors', function() {
});
it('enables external interception of error by overriding global.onerror', function() {
const fakeGlobal = { onerror: null },
handler = jasmine.createSpy('errorHandler'),
hijackHandler = jasmine.createSpy('hijackErrorHandler'),
errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
const fakeGlobal = minimalBrowserGlobal();
const handler = jasmine.createSpy('errorHandler');
const hijackHandler = jasmine.createSpy('hijackErrorHandler');
const errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
errors.install();
errors.pushListener(handler);
@@ -30,10 +30,10 @@ describe('GlobalErrors', function() {
});
it('calls the global error handler with all parameters', function() {
const fakeGlobal = { onerror: null },
handler = jasmine.createSpy('errorHandler'),
errors = new jasmineUnderTest.GlobalErrors(fakeGlobal),
fooError = new Error('foo');
const fakeGlobal = minimalBrowserGlobal();
const handler = jasmine.createSpy('errorHandler');
const errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
const fooError = new Error('foo');
errors.install();
errors.pushListener(handler);
@@ -50,10 +50,10 @@ describe('GlobalErrors', function() {
});
it('only calls the most recent handler', function() {
const fakeGlobal = { onerror: null },
handler1 = jasmine.createSpy('errorHandler1'),
handler2 = jasmine.createSpy('errorHandler2'),
errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
const fakeGlobal = minimalBrowserGlobal();
const handler1 = jasmine.createSpy('errorHandler1');
const handler2 = jasmine.createSpy('errorHandler2');
const errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
errors.install();
errors.pushListener(handler1);
@@ -66,10 +66,10 @@ describe('GlobalErrors', function() {
});
it('calls previous handlers when one is removed', function() {
const fakeGlobal = { onerror: null },
handler1 = jasmine.createSpy('errorHandler1'),
handler2 = jasmine.createSpy('errorHandler2'),
errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
const fakeGlobal = minimalBrowserGlobal();
const handler1 = jasmine.createSpy('errorHandler1');
const handler2 = jasmine.createSpy('errorHandler2');
const errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
errors.install();
errors.pushListener(handler1);
@@ -91,9 +91,12 @@ describe('GlobalErrors', function() {
});
it('uninstalls itself, putting back a previous callback', function() {
const originalCallback = jasmine.createSpy('error'),
fakeGlobal = { onerror: originalCallback },
errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
const originalCallback = jasmine.createSpy('error');
const fakeGlobal = {
...minimalBrowserGlobal(),
onerror: originalCallback
};
const errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
expect(fakeGlobal.onerror).toBe(originalCallback);
@@ -107,9 +110,9 @@ describe('GlobalErrors', function() {
});
it('rethrows the original error when there is no handler', function() {
const fakeGlobal = {},
errors = new jasmineUnderTest.GlobalErrors(fakeGlobal),
originalError = new Error('nope');
const fakeGlobal = minimalBrowserGlobal();
const errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
const originalError = new Error('nope');
errors.install();
@@ -407,7 +410,7 @@ describe('GlobalErrors', function() {
describe('#setOverrideListener', function() {
it('overrides the existing handlers in browsers until removed', function() {
const fakeGlobal = { onerror: null };
const fakeGlobal = minimalBrowserGlobal();
const handler0 = jasmine.createSpy('handler0');
const handler1 = jasmine.createSpy('handler1');
const overrideHandler = jasmine.createSpy('overrideHandler');
@@ -529,8 +532,7 @@ describe('GlobalErrors', function() {
});
it('throws if there is already an override handler', function() {
const fakeGlobal = { onerror: null };
const errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
const errors = new jasmineUnderTest.GlobalErrors(minimalBrowserGlobal());
errors.setOverrideListener(() => {}, () => {});
expect(function() {
@@ -541,9 +543,8 @@ describe('GlobalErrors', function() {
describe('#removeOverrideListener', function() {
it("calls the handler's onRemove callback", function() {
const fakeGlobal = { onerror: null };
const onRemove = jasmine.createSpy('onRemove');
const errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
const errors = new jasmineUnderTest.GlobalErrors(minimalBrowserGlobal());
errors.setOverrideListener(() => {}, onRemove);
errors.removeOverrideListener();
@@ -552,10 +553,17 @@ describe('GlobalErrors', function() {
});
it('does not throw if there is no handler', function() {
const fakeGlobal = { onerror: null };
const errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
const errors = new jasmineUnderTest.GlobalErrors(minimalBrowserGlobal());
expect(() => errors.removeOverrideListener()).not.toThrow();
});
});
function minimalBrowserGlobal() {
return {
addEventListener() {},
removeEventListener() {},
onerror: null
};
}
});

View File

@@ -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({

View File

@@ -195,6 +195,8 @@ describe('Spec', function() {
onStart: startCallback,
resultCallback: resultCallback,
description: 'with a spec',
parentSuiteId: 'suite1',
filename: 'someSpecFile.js',
getSpecName: function() {
return 'a suite with a spec';
},
@@ -219,6 +221,8 @@ describe('Spec', function() {
status: 'pending',
description: 'with a spec',
fullName: 'a suite with a spec',
parentSuiteId: 'suite1',
filename: 'someSpecFile.js',
failedExpectations: [],
passedExpectations: [],
deprecationWarnings: [],

View File

@@ -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() {

View File

@@ -431,11 +431,15 @@ describe('Env integration', function() {
describe('Handling async errors', function() {
it('routes async errors to a running spec', async function() {
const global = {
...browserEventMethods(),
setTimeout: function(fn, delay) {
return setTimeout(fn, delay);
},
clearTimeout: function(fn, delay) {
clearTimeout(fn, delay);
},
queueMicrotask: function(fn) {
queueMicrotask(fn);
}
};
spyOn(jasmineUnderTest, 'getGlobal').and.returnValue(global);
@@ -468,11 +472,15 @@ describe('Env integration', function() {
describe('When the running spec has reported specDone', function() {
it('routes async errors to an ancestor suite', async function() {
const global = {
...browserEventMethods(),
setTimeout: function(fn, delay) {
return setTimeout(fn, delay);
},
clearTimeout: function(fn) {
clearTimeout(fn);
},
queueMicrotask: function(fn) {
queueMicrotask(fn);
}
};
spyOn(jasmineUnderTest, 'getGlobal').and.returnValue(global);
@@ -524,11 +532,15 @@ describe('Env integration', function() {
it('routes async errors to a running suite', async function() {
const global = {
...browserEventMethods(),
setTimeout: function(fn, delay) {
return setTimeout(fn, delay);
},
clearTimeout: function(fn, delay) {
clearTimeout(fn, delay);
},
queueMicrotask: function(fn) {
queueMicrotask(fn);
}
};
spyOn(jasmineUnderTest, 'getGlobal').and.returnValue(global);
@@ -545,8 +557,8 @@ describe('Env integration', function() {
env.it('fails', function(specDone) {
setTimeout(function() {
specDone();
setTimeout(function() {
setTimeout(function() {
queueMicrotask(function() {
queueMicrotask(function() {
global.onerror('fail');
});
});
@@ -573,11 +585,15 @@ describe('Env integration', function() {
describe('When the running suite has reported suiteDone', function() {
it('routes async errors to an ancestor suite', async function() {
const global = {
...browserEventMethods(),
setTimeout: function(fn, delay) {
return setTimeout(fn, delay);
},
clearTimeout: function(fn, delay) {
clearTimeout(fn, delay);
},
queueMicrotask: function(fn) {
queueMicrotask(fn);
}
};
spyOn(jasmineUnderTest, 'getGlobal').and.returnValue(global);
@@ -633,11 +649,15 @@ describe('Env integration', function() {
describe('When the env has started reporting jasmineDone', function() {
it('logs the error to the console', async function() {
const global = {
...browserEventMethods(),
setTimeout: function(fn, delay) {
return setTimeout(fn, delay);
},
clearTimeout: function(fn, delay) {
clearTimeout(fn, delay);
},
queueMicrotask: function(fn) {
queueMicrotask(fn);
}
};
spyOn(jasmineUnderTest, 'getGlobal').and.returnValue(global);
@@ -672,11 +692,15 @@ describe('Env integration', function() {
it('routes all errors that occur during stack clearing somewhere', async function() {
const global = {
...browserEventMethods(),
setTimeout: function(fn, delay) {
return setTimeout(fn, delay);
},
clearTimeout: function(fn) {
clearTimeout(fn);
},
queueMicrotask: function(fn) {
queueMicrotask(fn);
}
};
spyOn(jasmineUnderTest, 'getGlobal').and.returnValue(global);
@@ -1427,8 +1451,8 @@ describe('Env integration', function() {
global: {
setTimeout: globalSetTimeout,
clearTimeout: clearTimeout,
setImmediate: function(cb) {
return setTimeout(cb, 0);
queueMicrotask: function(fn) {
queueMicrotask(fn);
}
}
});
@@ -1501,8 +1525,8 @@ describe('Env integration', function() {
clearTimeout: clearTimeout,
setInterval: setInterval,
clearInterval: clearInterval,
setImmediate: function(cb) {
return realSetTimeout(cb, 0);
queueMicrotask: function(fn) {
queueMicrotask(fn);
}
}
});
@@ -1909,11 +1933,17 @@ describe('Env integration', function() {
'specStarted',
'specDone'
]);
const suiteFullNameToId = {};
reporter.suiteStarted.and.callFake(function(e) {
suiteFullNameToId[e.fullName] = e.id;
});
env.addReporter(reporter);
env.it('a top level spec', function() {});
env.describe('A Suite', function() {
env.it('with a top level spec', function() {
env.it('with a spec', function() {
env.expect(true).toBe(true);
});
env.describe('with a nested suite', function() {
@@ -1936,37 +1966,109 @@ describe('Env integration', function() {
await env.execute();
expect(reporter.jasmineStarted).toHaveBeenCalledWith({
totalSpecsDefined: 5,
totalSpecsDefined: 6,
order: jasmine.any(jasmineUnderTest.Order)
});
expect(reporter.specDone.calls.count()).toBe(5);
expect(reporter.specStarted.calls.count()).toBe(6);
expect(reporter.specDone.calls.count()).toBe(6);
expect(reporter.specDone).toHaveBeenCalledWith(
expect(reporter.specStarted).toHaveBeenCalledWith(
jasmine.objectContaining({
description: 'with a top level spec',
status: 'passed'
description: 'a top level spec',
parentSuiteId: null
})
);
expect(reporter.specDone).toHaveBeenCalledWith(
jasmine.objectContaining({
description: "with an x'ed spec",
status: 'pending'
description: 'a top level spec',
status: 'passed',
parentSuiteId: null
})
);
expect(reporter.specStarted).toHaveBeenCalledWith(
jasmine.objectContaining({
description: 'with a spec',
parentSuiteId: suiteFullNameToId['A Suite']
})
);
expect(reporter.specDone).toHaveBeenCalledWith(
jasmine.objectContaining({
description: 'with a spec',
status: 'failed'
status: 'passed',
parentSuiteId: suiteFullNameToId['A Suite']
})
);
expect(reporter.specStarted).toHaveBeenCalledWith(
jasmine.objectContaining({
description: "with an x'ed spec",
parentSuiteId: suiteFullNameToId['A Suite with a nested suite']
})
);
expect(reporter.specDone).toHaveBeenCalledWith(
jasmine.objectContaining({
description: "with an x'ed spec",
status: 'pending',
parentSuiteId: suiteFullNameToId['A Suite with a nested suite']
})
);
expect(reporter.specStarted).toHaveBeenCalledWith(
jasmine.objectContaining({
description: 'with a spec',
parentSuiteId: suiteFullNameToId['A Suite with a nested suite']
})
);
expect(reporter.specDone).toHaveBeenCalledWith(
jasmine.objectContaining({
description: 'with a spec',
status: 'failed',
parentSuiteId: suiteFullNameToId['A Suite with a nested suite']
})
);
expect(reporter.specStarted).toHaveBeenCalledWith(
jasmine.objectContaining({
description: 'is pending',
parentSuiteId:
suiteFullNameToId['A Suite with only non-executable specs']
})
);
expect(reporter.specDone).toHaveBeenCalledWith(
jasmine.objectContaining({
description: 'is pending',
status: 'pending'
status: 'pending',
parentSuiteId:
suiteFullNameToId['A Suite with only non-executable specs']
})
);
expect(reporter.suiteStarted).toHaveBeenCalledWith(
jasmine.objectContaining({
description: 'A Suite',
parentSuiteId: null
})
);
expect(reporter.suiteDone).toHaveBeenCalledWith(
jasmine.objectContaining({
description: 'A Suite',
status: 'passed',
parentSuiteId: null
})
);
expect(reporter.suiteStarted).toHaveBeenCalledWith(
jasmine.objectContaining({
description: 'with a nested suite',
parentSuiteId: suiteFullNameToId['A Suite']
})
);
expect(reporter.suiteDone).toHaveBeenCalledWith(
jasmine.objectContaining({
description: 'with a nested suite',
status: 'passed',
parentSuiteId: suiteFullNameToId['A Suite']
})
);
@@ -1977,6 +2079,89 @@ describe('Env integration', function() {
expect(suiteResult.description).toEqual('A Suite');
});
it('reports focused specs and suites as expected', async function() {
const reporter = jasmine.createSpyObj('fakeReporter', [
'suiteStarted',
'suiteDone',
'specStarted',
'specDone'
]);
const suiteFullNameToId = {};
reporter.suiteStarted.and.callFake(function(e) {
suiteFullNameToId[e.fullName] = e.id;
});
env.fit('a focused top level spec', function() {});
env.describe('a suite', function() {
env.fdescribe('a focused suite', function() {
env.fit('a focused spec', function() {});
});
});
env.addReporter(reporter);
await env.execute();
expect(reporter.specStarted).toHaveBeenCalledTimes(2);
expect(reporter.specDone).toHaveBeenCalledTimes(2);
expect(reporter.specStarted).toHaveBeenCalledWith(
jasmine.objectContaining({
description: 'a focused top level spec',
parentSuiteId: null
})
);
expect(reporter.specDone).toHaveBeenCalledWith(
jasmine.objectContaining({
description: 'a focused top level spec',
status: 'passed',
parentSuiteId: null
})
);
expect(reporter.specStarted).toHaveBeenCalledWith(
jasmine.objectContaining({
description: 'a focused spec',
parentSuiteId: suiteFullNameToId['a suite a focused suite']
})
);
expect(reporter.specDone).toHaveBeenCalledWith(
jasmine.objectContaining({
description: 'a focused spec',
status: 'passed',
parentSuiteId: suiteFullNameToId['a suite a focused suite']
})
);
expect(reporter.suiteStarted).toHaveBeenCalledWith(
jasmine.objectContaining({
description: 'a suite',
parentSuiteId: null
})
);
expect(reporter.suiteDone).toHaveBeenCalledWith(
jasmine.objectContaining({
description: 'a suite',
status: 'passed',
parentSuiteId: null
})
);
expect(reporter.suiteStarted).toHaveBeenCalledWith(
jasmine.objectContaining({
description: 'a focused suite',
parentSuiteId: suiteFullNameToId['a suite']
})
);
expect(reporter.suiteDone).toHaveBeenCalledWith(
jasmine.objectContaining({
description: 'a focused suite',
status: 'passed',
parentSuiteId: suiteFullNameToId['a suite']
})
);
});
it('should report the random seed at the beginning and end of execution', async function() {
const reporter = jasmine.createSpyObj('fakeReporter', [
'jasmineStarted',
@@ -2002,6 +2187,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']);
@@ -2619,12 +2827,16 @@ describe('Env integration', function() {
it('reports errors that occur during loading', async function() {
const global = {
...browserEventMethods(),
setTimeout: function(fn, delay) {
return setTimeout(fn, delay);
},
clearTimeout: function(fn, delay) {
clearTimeout(fn, delay);
},
queueMicrotask: function(fn) {
queueMicrotask(fn);
},
onerror: function() {}
};
spyOn(jasmineUnderTest, 'getGlobal').and.returnValue(global);
@@ -2674,12 +2886,16 @@ describe('Env integration', function() {
it('does not install a global error handler during loading', async function() {
const originalOnerror = jasmine.createSpy('original onerror');
const global = {
...browserEventMethods(),
setTimeout: function(fn, delay) {
return setTimeout(fn, delay);
},
clearTimeout: function(fn, delay) {
clearTimeout(fn, delay);
},
queueMicrotask: function(fn) {
queueMicrotask(fn);
},
onerror: originalOnerror
};
spyOn(jasmineUnderTest, 'getGlobal').and.returnValue(global);
@@ -2864,11 +3080,15 @@ describe('Env integration', function() {
describe('When there are load errors', function() {
it('is "failed"', async function() {
const global = {
...browserEventMethods(),
setTimeout: function(fn, delay) {
return setTimeout(fn, delay);
},
clearTimeout: function(fn, delay) {
return clearTimeout(fn, delay);
},
queueMicrotask: function(fn) {
queueMicrotask(fn);
}
};
spyOn(jasmineUnderTest, 'getGlobal').and.returnValue(global);
@@ -3906,4 +4126,142 @@ 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')
})
]
})
);
});
it('reports suite and spec filenames', async function() {
const methods = ['suiteStarted', 'suiteDone', 'specStarted', 'specDone'];
const reporter = jasmine.createSpyObj('reporter', methods);
env.addReporter(reporter);
// Simulate calling through global it and describe,
// which add another stack frame vs calling env methods directly
function describeShim(name, fn) {
env.describe(name, fn);
}
function itShim(name, fn) {
env.it(name, fn);
}
describeShim('a suite', function() {
itShim('a spec', function() {});
});
await env.execute();
for (const method of methods) {
expect(reporter[method])
.withContext(method)
.toHaveBeenCalledWith(
jasmine.objectContaining({
filename: jasmine.stringMatching(/EnvSpec\.js$/)
})
);
}
});
it('reports skipped suite and spec filenames', async function() {
const methods = ['suiteStarted', 'suiteDone', 'specStarted', 'specDone'];
const reporter = jasmine.createSpyObj('reporter', methods);
env.addReporter(reporter);
// Simulate calling through global it and describe,
// which add another stack frame vs calling env methods directly
function xdescribeShim(name, fn) {
env.xdescribe(name, fn);
}
function xitShim(name, fn) {
env.xit(name, fn);
}
xdescribeShim('a suite', function() {
xitShim('a spec', function() {});
});
await env.execute();
for (const method of methods) {
expect(reporter[method])
.withContext(method)
.toHaveBeenCalledWith(
jasmine.objectContaining({
filename: jasmine.stringMatching(/EnvSpec\.js$/)
})
);
}
});
it('reports focused suite and spec filenames', async function() {
const methods = ['suiteStarted', 'suiteDone', 'specStarted', 'specDone'];
const reporter = jasmine.createSpyObj('reporter', methods);
env.addReporter(reporter);
// Simulate calling through global it and describe,
// which add another stack frame vs calling env methods directly
function fdescribeShim(name, fn) {
env.fdescribe(name, fn);
}
function fitShim(name, fn) {
env.fit(name, fn);
}
fdescribeShim('a suite', function() {
fitShim('a spec', function() {});
});
await env.execute();
for (const method of methods) {
expect(reporter[method])
.withContext(method)
.toHaveBeenCalledWith(
jasmine.objectContaining({
filename: jasmine.stringMatching(/EnvSpec\.js$/)
})
);
}
});
function browserEventMethods() {
return {
addEventListener() {},
removeEventListener() {}
};
}
});

View File

@@ -1,8 +1,32 @@
getJasmineRequireObj().clearStack = function(j$) {
const maxInlineCallCount = 10;
function messageChannelImpl(global, setTimeout) {
const channel = new global.MessageChannel();
function browserQueueMicrotaskImpl(global) {
const { setTimeout, queueMicrotask } = global;
let currentCallCount = 0;
return function clearStack(fn) {
currentCallCount++;
if (currentCallCount < maxInlineCallCount) {
queueMicrotask(fn);
} else {
currentCallCount = 0;
setTimeout(fn);
}
};
}
function nodeQueueMicrotaskImpl(global) {
const { queueMicrotask } = global;
return function(fn) {
queueMicrotask(fn);
};
}
function messageChannelImpl(global) {
const { MessageChannel, setTimeout } = global;
const channel = new MessageChannel();
let head = {};
let tail = head;
@@ -13,7 +37,7 @@ getJasmineRequireObj().clearStack = function(j$) {
delete head.task;
if (taskRunning) {
global.setTimeout(task, 0);
setTimeout(task, 0);
} else {
try {
taskRunning = true;
@@ -39,29 +63,32 @@ getJasmineRequireObj().clearStack = function(j$) {
}
function getClearStack(global) {
let currentCallCount = 0;
const realSetTimeout = global.setTimeout;
const setTimeoutImpl = function clearStack(fn) {
Function.prototype.apply.apply(realSetTimeout, [global, [fn, 0]]);
};
const NODE_JS =
global.process &&
global.process.versions &&
typeof global.process.versions.node === 'string';
if (j$.isFunction_(global.setImmediate)) {
const realSetImmediate = global.setImmediate;
return function(fn) {
currentCallCount++;
const SAFARI =
global.navigator &&
/^((?!chrome|android).)*safari/i.test(global.navigator.userAgent);
if (currentCallCount < maxInlineCallCount) {
realSetImmediate(fn);
} else {
currentCallCount = 0;
setTimeoutImpl(fn);
}
};
} else if (!j$.util.isUndefined(global.MessageChannel)) {
return messageChannelImpl(global, setTimeoutImpl);
if (NODE_JS) {
// Unlike browsers, Node doesn't require us to do a periodic setTimeout
// so we avoid the overhead.
return nodeQueueMicrotaskImpl(global);
} else if (
SAFARI ||
j$.util.isUndefined(global.MessageChannel) /* tests */
) {
// 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);
} else {
return setTimeoutImpl;
// MessageChannel is faster than queueMicrotask in supported browsers
// other than Safari.
return messageChannelImpl(global);
}
}

View File

@@ -674,17 +674,23 @@ getJasmineRequireObj().Env = function(j$) {
this.describe = function(description, definitionFn) {
ensureIsNotNested('describe');
return suiteBuilder.describe(description, definitionFn).metadata;
const filename = callerCallerFilename();
return suiteBuilder.describe(description, definitionFn, filename)
.metadata;
};
this.xdescribe = function(description, definitionFn) {
ensureIsNotNested('xdescribe');
return suiteBuilder.xdescribe(description, definitionFn).metadata;
const filename = callerCallerFilename();
return suiteBuilder.xdescribe(description, definitionFn, filename)
.metadata;
};
this.fdescribe = function(description, definitionFn) {
ensureIsNotNested('fdescribe');
return suiteBuilder.fdescribe(description, definitionFn).metadata;
const filename = callerCallerFilename();
return suiteBuilder.fdescribe(description, definitionFn, filename)
.metadata;
};
function specResultCallback(spec, result, next) {
@@ -701,27 +707,30 @@ 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) {
ensureIsNotNested('it');
return suiteBuilder.it(description, fn, timeout).metadata;
const filename = callerCallerFilename();
return suiteBuilder.it(description, fn, timeout, filename).metadata;
};
this.xit = function(description, fn, timeout) {
ensureIsNotNested('xit');
return suiteBuilder.xit(description, fn, timeout).metadata;
const filename = callerCallerFilename();
return suiteBuilder.xit(description, fn, timeout, filename).metadata;
};
this.fit = function(description, fn, timeout) {
ensureIsNotNested('fit');
return suiteBuilder.fit(description, fn, timeout).metadata;
const filename = callerCallerFilename();
return suiteBuilder.fit(description, fn, timeout, filename).metadata;
};
/**
@@ -861,5 +870,9 @@ getJasmineRequireObj().Env = function(j$) {
};
}
function callerCallerFilename() {
return new j$.StackTrace(new Error()).frames[3].file;
}
return Env;
};

View File

@@ -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) {

View File

@@ -109,21 +109,14 @@ getJasmineRequireObj().GlobalErrors = function(j$) {
}
};
if (global.addEventListener) {
global.addEventListener(
'unhandledrejection',
browserRejectionHandler
);
}
global.addEventListener('unhandledrejection', browserRejectionHandler);
this.uninstall = function uninstall() {
global.onerror = originalHandler;
if (global.removeEventListener) {
global.removeEventListener(
'unhandledrejection',
browserRejectionHandler
);
}
global.removeEventListener(
'unhandledrejection',
browserRejectionHandler
);
};
}
};

View File

@@ -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.'
)
);
}
});
});
}

View File

@@ -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,

View File

@@ -4,6 +4,8 @@ getJasmineRequireObj().Spec = function(j$) {
this.asyncExpectationFactory = attrs.asyncExpectationFactory;
this.resultCallback = attrs.resultCallback || function() {};
this.id = attrs.id;
this.filename = attrs.filename;
this.parentSuiteId = attrs.parentSuiteId;
this.description = attrs.description || '';
this.queueableFn = attrs.queueableFn;
this.beforeAndAfterFns =
@@ -37,35 +39,7 @@ getJasmineRequireObj().Spec = function(j$) {
this.exclude();
}
/**
* @typedef SpecResult
* @property {Int} 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.
* @property {Expectation[]} passedExpectations - The list of expectations that passed during execution of this spec.
* @property {Expectation[]} deprecationWarnings - The list of deprecation warnings that occurred during execution this spec.
* @property {String} pendingReason - If the spec is {@link pending}, this will be the reason.
* @property {String} status - Once the spec has completed, this string represents the pass/fail status of this spec.
* @property {number} duration - The time in ms used by the spec execution, including any before/afterEach.
* @property {Object} properties - User-supplied properties, if any, that were set using {@link Env#setSpecProperty}
* @property {DebugLogEntry[]|null} debugLogs - Messages, if any, that were logged using {@link jasmine.debugLog} during a failing spec.
* @since 2.0.0
*/
this.result = {
id: this.id,
description: this.description,
fullName: this.getFullName(),
failedExpectations: [],
passedExpectations: [],
deprecationWarnings: [],
pendingReason: '',
duration: null,
properties: null,
debugLogs: null
};
this.reportedDone = false;
this.reset();
}
Spec.prototype.addExpectationResult = function(passed, data, isError) {
@@ -175,14 +149,33 @@ getJasmineRequireObj().Spec = function(j$) {
};
Spec.prototype.reset = function() {
/**
* @typedef SpecResult
* @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 {String|null} parentSuiteId - The ID of the suite containing this spec, or null if this spec is not in a describe().
* @property {String} filename - The name of the file the spec was defined in.
* @property {Expectation[]} failedExpectations - The list of expectations that failed during execution of this spec.
* @property {Expectation[]} passedExpectations - The list of expectations that passed during execution of this spec.
* @property {Expectation[]} deprecationWarnings - The list of deprecation warnings that occurred during execution this spec.
* @property {String} pendingReason - If the spec is {@link pending}, this will be the reason.
* @property {String} status - Once the spec has completed, this string represents the pass/fail status of this spec.
* @property {number} duration - The time in ms used by the spec execution, including any before/afterEach.
* @property {Object} properties - User-supplied properties, if any, that were set using {@link Env#setSpecProperty}
* @property {DebugLogEntry[]|null} debugLogs - Messages, if any, that were logged using {@link jasmine.debugLog} during a failing spec.
* @since 2.0.0
*/
this.result = {
id: this.id,
description: this.description,
fullName: this.getFullName(),
parentSuiteId: this.parentSuiteId,
filename: this.filename,
failedExpectations: [],
passedExpectations: [],
deprecationWarnings: [],
pendingReason: this.excludeMessage,
pendingReason: this.excludeMessage || '',
duration: null,
properties: null,
debugLogs: null

View File

@@ -4,6 +4,8 @@ getJasmineRequireObj().Suite = function(j$) {
this.id = attrs.id;
this.parentSuite = attrs.parentSuite;
this.description = attrs.description;
this.reportedParentSuiteId = attrs.reportedParentSuiteId;
this.filename = attrs.filename;
this.expectationFactory = attrs.expectationFactory;
this.asyncExpectationFactory = attrs.asyncExpectationFactory;
this.throwOnExpectationFailure = !!attrs.throwOnExpectationFailure;
@@ -106,9 +108,11 @@ 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 {String|null} parentSuiteId - The ID of the suite containing this suite, or null if this is not in another describe().
* @property {String} filename - The name of the file the suite was defined in.
* @property {Expectation[]} failedExpectations - The list of expectations that failed in an {@link afterAll} for this suite.
* @property {Expectation[]} deprecationWarnings - The list of deprecation warnings that occurred on this suite.
* @property {String} status - Once the suite has completed, this string represents the pass/fail status of this suite.
@@ -120,6 +124,8 @@ getJasmineRequireObj().Suite = function(j$) {
id: this.id,
description: this.description,
fullName: this.getFullName(),
parentSuiteId: this.reportedParentSuiteId,
filename: this.filename,
failedExpectations: [],
deprecationWarnings: [],
duration: null,

View File

@@ -22,9 +22,9 @@ getJasmineRequireObj().SuiteBuilder = function(j$) {
this.focusedRunables = [];
}
describe(description, definitionFn) {
describe(description, definitionFn, filename) {
ensureIsFunction(definitionFn, 'describe');
const suite = this.suiteFactory_(description);
const suite = this.suiteFactory_(description, filename);
if (definitionFn.length > 0) {
throw new Error('describe does not expect any arguments');
}
@@ -32,17 +32,12 @@ 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;
}
fdescribe(description, definitionFn) {
fdescribe(description, definitionFn, filename) {
ensureIsFunction(definitionFn, 'fdescribe');
const suite = this.suiteFactory_(description);
const suite = this.suiteFactory_(description, filename);
suite.isFocused = true;
this.focusedRunables.push(suite.id);
@@ -52,37 +47,37 @@ getJasmineRequireObj().SuiteBuilder = function(j$) {
return suite;
}
xdescribe(description, definitionFn) {
xdescribe(description, definitionFn, filename) {
ensureIsFunction(definitionFn, 'xdescribe');
const suite = this.suiteFactory_(description);
const suite = this.suiteFactory_(description, filename);
suite.exclude();
this.addSpecsToSuite_(suite, definitionFn);
return suite;
}
it(description, fn, timeout) {
it(description, fn, timeout, filename) {
// it() sometimes doesn't have a fn argument, so only check the type if
// it's given.
if (arguments.length > 1 && typeof fn !== 'undefined') {
ensureIsFunctionOrAsync(fn, 'it');
}
return this.it_(description, fn, timeout);
return this.it_(description, fn, timeout, filename);
}
xit(description, fn, timeout) {
xit(description, fn, timeout, filename) {
// xit(), like it(), doesn't always have a fn argument, so only check the
// type when needed.
if (arguments.length > 1 && typeof fn !== 'undefined') {
ensureIsFunctionOrAsync(fn, 'xit');
}
const spec = this.it_(description, fn, timeout);
const spec = this.it_(description, fn, timeout, filename);
spec.exclude('Temporarily disabled with xit');
return spec;
}
fit(description, fn, timeout) {
fit(description, fn, timeout, filename) {
// Unlike it and xit, the function is required because it doesn't make
// sense to focus on nothing.
ensureIsFunctionOrAsync(fn, 'fit');
@@ -90,7 +85,7 @@ getJasmineRequireObj().SuiteBuilder = function(j$) {
if (timeout) {
j$.util.validateTimeout(timeout);
}
const spec = this.specFactory_(description, fn, timeout);
const spec = this.specFactory_(description, fn, timeout, filename);
this.currentDeclarationSuite_.addChild(spec);
this.focusedRunables.push(spec.id);
this.unfocusAncestor_();
@@ -150,12 +145,12 @@ getJasmineRequireObj().SuiteBuilder = function(j$) {
});
}
it_(description, fn, timeout) {
it_(description, fn, timeout, filename) {
if (timeout) {
j$.util.validateTimeout(timeout);
}
const spec = this.specFactory_(description, fn, timeout);
const spec = this.specFactory_(description, fn, timeout, filename);
if (this.currentDeclarationSuite_.markedExcluding) {
spec.exclude();
}
@@ -164,12 +159,17 @@ getJasmineRequireObj().SuiteBuilder = function(j$) {
return spec;
}
suiteFactory_(description) {
suiteFactory_(description, filename) {
const config = this.env_.configuration();
const parentSuite = this.currentDeclarationSuite_;
const reportedParentSuiteId =
parentSuite === this.topSuite ? null : parentSuite.id;
return new j$.Suite({
id: 'suite' + this.nextSuiteId_++,
description,
parentSuite: this.currentDeclarationSuite_,
filename,
parentSuite,
reportedParentSuiteId,
timer: new j$.Timer(),
expectationFactory: this.expectationFactory_,
asyncExpectationFactory: this.suiteAsyncExpectationFactory_,
@@ -183,22 +183,33 @@ 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;
}
specFactory_(description, fn, timeout) {
specFactory_(description, fn, timeout, filename) {
this.totalSpecsDefined++;
const config = this.env_.configuration();
const suite = this.currentDeclarationSuite_;
const parentSuiteId = suite === this.topSuite ? null : suite.id;
const spec = new j$.Spec({
id: 'spec' + this.nextSpecId_++,
filename,
parentSuiteId,
beforeAndAfterFns: beforeAndAfterFns(suite),
expectationFactory: this.expectationFactory_,
asyncExpectationFactory: this.specAsyncExpectationFactory_,

View File

@@ -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
});
});
};

View File

@@ -652,7 +652,7 @@ getJasmineRequireObj().MatchersUtil = function(j$) {
* };
* }
*
* var actual = {
* const actual = {
* n: 2,
* otherFields: "don't care"
* };

View File

@@ -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');
*/