Compare commits
133 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cd1b7ce9c7 | ||
|
|
3653d6e0ef | ||
|
|
c3387f8dbf | ||
|
|
3d54184c7f | ||
|
|
6f23e706d7 | ||
|
|
cc69edf92c | ||
|
|
ba7560f65e | ||
|
|
c3960c4a96 | ||
|
|
5c21f94bb1 | ||
|
|
8cd7c94490 | ||
|
|
6a6fa7b29a | ||
|
|
4984548cab | ||
|
|
6941bde7e2 | ||
|
|
1504f25ced | ||
|
|
99e350ac85 | ||
|
|
1624b07589 | ||
|
|
d06dce4614 | ||
|
|
03098e81f8 | ||
|
|
726c152f6e | ||
|
|
409d2e29e5 | ||
|
|
01e2bd5050 | ||
|
|
96033e38ea | ||
|
|
ed75290ef7 | ||
|
|
a14dbf012a | ||
|
|
17c11ba7b9 | ||
|
|
2a1daca1ca | ||
|
|
f0db5ce350 | ||
|
|
39f9c2e1a0 | ||
|
|
bff612a169 | ||
|
|
4ba42f3746 | ||
|
|
58bee05c36 | ||
|
|
c1871b0f0c | ||
|
|
c16974b091 | ||
|
|
bfedda9764 | ||
|
|
a67b7276be | ||
|
|
47f3105ef0 | ||
|
|
aeb56539c9 | ||
|
|
75d45efa16 | ||
|
|
59d1c5bebb | ||
|
|
040983c979 | ||
|
|
2ddb344bac | ||
|
|
e56bd3918b | ||
|
|
59600a1c29 | ||
|
|
f8e4ea868f | ||
|
|
0ff56c53b1 | ||
|
|
b617d983de | ||
|
|
8e0f0e8e8c | ||
|
|
d745d6b5f0 | ||
|
|
fc2c2a477d | ||
|
|
ff93277c0f | ||
|
|
90741b3cee | ||
|
|
d1de59f0ed | ||
|
|
73f8e001ad | ||
|
|
390cc45af2 | ||
|
|
33118ac6e2 | ||
|
|
31ff9a300c | ||
|
|
5cc739d879 | ||
|
|
1e7f07259e | ||
|
|
c36a5cfd96 | ||
|
|
299fd1f770 | ||
|
|
656427d328 | ||
|
|
621522fdd4 | ||
|
|
6e3589bf52 | ||
|
|
df2d9b282e | ||
|
|
726d35c5c5 | ||
|
|
f509078020 | ||
|
|
8308515210 | ||
|
|
ed838b3cbf | ||
|
|
04fac300e8 | ||
|
|
ff237f4b66 | ||
|
|
e42e3d9e00 | ||
|
|
61505f4c59 | ||
|
|
86eddb05b4 | ||
|
|
8af5509581 | ||
|
|
166e5f4d6c | ||
|
|
6ad8d20694 | ||
|
|
cbc03feb52 | ||
|
|
bc3a495160 | ||
|
|
b323631611 | ||
|
|
e8767ba660 | ||
|
|
af9a4114f4 | ||
|
|
75f97961f5 | ||
|
|
25a7168286 | ||
|
|
494e81f436 | ||
|
|
169a2a8ad2 | ||
|
|
b267029301 | ||
|
|
cf574634b8 | ||
|
|
f8c01574e6 | ||
|
|
481f1e7c5c | ||
|
|
5e650953cd | ||
|
|
ed5e902106 | ||
|
|
87f9ab29df | ||
|
|
47c64a86d5 | ||
|
|
bb497beeff | ||
|
|
e14d9c4be3 | ||
|
|
89e0b35c53 | ||
|
|
1e7b68236b | ||
|
|
394068f863 | ||
|
|
b831e81074 | ||
|
|
4c13c2b00b | ||
|
|
dd98a45003 | ||
|
|
fe6762b470 | ||
|
|
fa16b74500 | ||
|
|
4cd190b232 | ||
|
|
6ada55ff77 | ||
|
|
735ce6f758 | ||
|
|
430324885b | ||
|
|
7c2e8ce7ca | ||
|
|
d4025999b7 | ||
|
|
871111424d | ||
|
|
44f331f43d | ||
|
|
213144413f | ||
|
|
2272f9aead | ||
|
|
59848ca151 | ||
|
|
c14bfe3e5f | ||
|
|
26c48ab324 | ||
|
|
4c8d57e14c | ||
|
|
543689e206 | ||
|
|
ee524831f4 | ||
|
|
cfecab9f79 | ||
|
|
b3d9435dbb | ||
|
|
fec8dd37b0 | ||
|
|
0690500a0d | ||
|
|
0bfbda720d | ||
|
|
4fcdbd39fb | ||
|
|
f934e6d816 | ||
|
|
79c6bbc189 | ||
|
|
2e80ec0c22 | ||
|
|
588283cfe5 | ||
|
|
3a43871901 | ||
|
|
fcbab02b2d | ||
|
|
5f3475342e | ||
|
|
21f25972bb |
@@ -4,25 +4,17 @@
|
|||||||
version: 2.1
|
version: 2.1
|
||||||
|
|
||||||
executors:
|
executors:
|
||||||
|
node22:
|
||||||
|
docker:
|
||||||
|
- image: cimg/node:22.0.0
|
||||||
|
working_directory: ~/workspace
|
||||||
|
node20:
|
||||||
|
docker:
|
||||||
|
- image: cimg/node:20.0.0
|
||||||
|
working_directory: ~/workspace
|
||||||
node18:
|
node18:
|
||||||
docker:
|
docker:
|
||||||
- image: cimg/node:18.0.0 # Latest 18.x
|
- image: cimg/node:18.0.0
|
||||||
working_directory: ~/workspace
|
|
||||||
node16:
|
|
||||||
docker:
|
|
||||||
- image: cimg/node:16.14.2 # Latest 16.x
|
|
||||||
working_directory: ~/workspace
|
|
||||||
node14:
|
|
||||||
docker:
|
|
||||||
- image: cimg/node:14.17.4 # Latest 14.x
|
|
||||||
working_directory: ~/workspace
|
|
||||||
node12_latest:
|
|
||||||
docker:
|
|
||||||
- image: cimg/node:12.22.10 # Latest 12.x
|
|
||||||
working_directory: ~/workspace
|
|
||||||
node12_17:
|
|
||||||
docker:
|
|
||||||
- image: cimg/node:12.17.0 # Oldest version supported by Jasmine
|
|
||||||
working_directory: ~/workspace
|
working_directory: ~/workspace
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
@@ -59,8 +51,20 @@ jobs:
|
|||||||
name: Run tests
|
name: Run tests
|
||||||
command: npm test
|
command: npm test
|
||||||
|
|
||||||
|
test_parallel: &test_parallel
|
||||||
|
parameters:
|
||||||
|
executor:
|
||||||
|
type: executor
|
||||||
|
executor: << parameters.executor >>
|
||||||
|
steps:
|
||||||
|
- attach_workspace:
|
||||||
|
at: .
|
||||||
|
- run:
|
||||||
|
name: Run tests in parallel
|
||||||
|
command: npx grunt execSpecsInParallel
|
||||||
|
|
||||||
test_browsers: &test_browsers
|
test_browsers: &test_browsers
|
||||||
executor: node14
|
executor: node18
|
||||||
steps:
|
steps:
|
||||||
- attach_workspace:
|
- attach_workspace:
|
||||||
at: .
|
at: .
|
||||||
@@ -80,7 +84,7 @@ jobs:
|
|||||||
# cleanly if we kill it from a different step than it started in.
|
# cleanly if we kill it from a different step than it started in.
|
||||||
|
|
||||||
export PATH=$PATH:$HOME/workspace/bin
|
export PATH=$PATH:$HOME/workspace/bin
|
||||||
export SAUCE_TUNNEL_IDENTIFIER=$CIRCLE_BUILD_NUM
|
export SAUCE_TUNNEL_IDENTIFIER=$CIRCLE_WORKFLOW_JOB_ID
|
||||||
scripts/start-sauce-connect sauce-pidfile
|
scripts/start-sauce-connect sauce-pidfile
|
||||||
set +o errexit
|
set +o errexit
|
||||||
scripts/run-all-browsers
|
scripts/run-all-browsers
|
||||||
@@ -94,49 +98,48 @@ workflows:
|
|||||||
|
|
||||||
push:
|
push:
|
||||||
jobs:
|
jobs:
|
||||||
|
- build:
|
||||||
|
executor: node22
|
||||||
|
name: build_node_22
|
||||||
|
- build:
|
||||||
|
executor: node20
|
||||||
|
name: build_node_20
|
||||||
- build:
|
- build:
|
||||||
executor: node18
|
executor: node18
|
||||||
name: build_node_18
|
name: build_node_18
|
||||||
- build:
|
- test_node:
|
||||||
executor: node16
|
executor: node22
|
||||||
name: build_node_16
|
name: test_node_22
|
||||||
- build:
|
requires:
|
||||||
executor: node14
|
- build_node_22
|
||||||
name: build_node_14
|
- test_node:
|
||||||
- build:
|
executor: node20
|
||||||
executor: node12_latest
|
name: test_node_20
|
||||||
name: build_node_12_latest
|
requires:
|
||||||
- build:
|
- build_node_20
|
||||||
executor: node12_17
|
|
||||||
name: build_node_12_17
|
|
||||||
- test_node:
|
- test_node:
|
||||||
executor: node18
|
executor: node18
|
||||||
name: test_node_18
|
name: test_node_18
|
||||||
requires:
|
requires:
|
||||||
- build_node_18
|
- build_node_18
|
||||||
- test_node:
|
- test_parallel:
|
||||||
executor: node16
|
executor: node18
|
||||||
name: test_node_16
|
name: test_parallel_node_18
|
||||||
requires:
|
requires:
|
||||||
- build_node_16
|
- build_node_18
|
||||||
- test_node:
|
- test_parallel:
|
||||||
executor: node14
|
executor: node22
|
||||||
name: test_node_14
|
name: test_parallel_node_22
|
||||||
requires:
|
requires:
|
||||||
- build_node_14
|
- build_node_22
|
||||||
- test_node:
|
- test_parallel:
|
||||||
executor: node12_latest
|
executor: node20
|
||||||
name: test_node_12_latest
|
name: test_parallel_node_20
|
||||||
requires:
|
requires:
|
||||||
- build_node_12_latest
|
- build_node_20
|
||||||
- test_node:
|
|
||||||
executor: node12_17
|
|
||||||
name: test_node_12_17
|
|
||||||
requires:
|
|
||||||
- build_node_12_17
|
|
||||||
- test_browsers:
|
- test_browsers:
|
||||||
requires:
|
requires:
|
||||||
- build_node_14
|
- build_node_18
|
||||||
filters:
|
filters:
|
||||||
branches:
|
branches:
|
||||||
ignore: /pull\/.*/ # Don't run on pull requests.
|
ignore: /pull\/.*/ # Don't run on pull requests.
|
||||||
|
|||||||
46
.eslintrc
Normal file
46
.eslintrc
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
{
|
||||||
|
"extends": [
|
||||||
|
"plugin:compat/recommended"
|
||||||
|
],
|
||||||
|
"env": {
|
||||||
|
"browser": true,
|
||||||
|
"node": true,
|
||||||
|
"es2017": true
|
||||||
|
},
|
||||||
|
"parserOptions": {
|
||||||
|
"ecmaVersion": 2018
|
||||||
|
},
|
||||||
|
"rules": {
|
||||||
|
"quotes": [
|
||||||
|
"error",
|
||||||
|
"single",
|
||||||
|
{
|
||||||
|
"avoidEscape": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"no-unused-vars": [
|
||||||
|
"error",
|
||||||
|
{
|
||||||
|
"args": "none"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"no-implicit-globals": "error",
|
||||||
|
"block-spacing": "error",
|
||||||
|
"func-call-spacing": [
|
||||||
|
"error",
|
||||||
|
"never"
|
||||||
|
],
|
||||||
|
"key-spacing": "error",
|
||||||
|
"no-tabs": "error",
|
||||||
|
"no-trailing-spaces": "error",
|
||||||
|
"no-whitespace-before-property": "error",
|
||||||
|
"semi": [
|
||||||
|
"error",
|
||||||
|
"always"
|
||||||
|
],
|
||||||
|
"space-before-blocks": "error",
|
||||||
|
"no-eval": "error",
|
||||||
|
"no-var": "error",
|
||||||
|
"no-debugger": "error"
|
||||||
|
}
|
||||||
|
}
|
||||||
29
.github/CONTRIBUTING.md
vendored
29
.github/CONTRIBUTING.md
vendored
@@ -1,19 +1,10 @@
|
|||||||
# Developing for Jasmine Core
|
# Contributing to Jasmine
|
||||||
|
|
||||||
We welcome your contributions! Thanks for helping make Jasmine a better project
|
We welcome your contributions! Thanks for helping make Jasmine a better project
|
||||||
for everyone. Please review the backlog and discussion lists before starting
|
for everyone. If you want to contribute but don't know what to work on,
|
||||||
work. What you're looking for may already have been done. If it hasn't, the
|
[issues tagged help needed](https://github.com/issues?q=is%3Aopen+is%3Aissue+org%3Ajasmine+label%3A%22help+needed%22+)
|
||||||
community can help make your contribution better. If you want to contribute but
|
|
||||||
don't know what to work on,
|
|
||||||
[issues tagged help needed](https://github.com/jasmine/jasmine/labels/help%20needed)
|
|
||||||
should have enough detail to get started.
|
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
|
## Before Submitting a Pull Request
|
||||||
|
|
||||||
1. Ensure all specs are green in browsers *and* node.
|
1. Ensure all specs are green in browsers *and* node.
|
||||||
@@ -95,13 +86,6 @@ Or, How to make a successful pull request
|
|||||||
without test-driving it.
|
without test-driving it.
|
||||||
* _Write code in the style of the rest of the repo_ - Jasmine should look like
|
* _Write code in the style of the rest of the repo_ - Jasmine should look like
|
||||||
a cohesive whole.
|
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.
|
|
||||||
|
|
||||||
* _Ensure the *entire* test suite is green_ in all the big browsers, Node, and
|
* _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.
|
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
|
$ 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.
|
||||||
|
|||||||
47
.github/ISSUE_TEMPLATE.md
vendored
47
.github/ISSUE_TEMPLATE.md
vendored
@@ -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
98
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
name: Bug Report
|
||||||
|
description: I think I've found a bug in Jasmine
|
||||||
|
labels: ["bug report"]
|
||||||
|
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
14
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal 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.
|
||||||
31
.github/ISSUE_TEMPLATE/feature_proposal.yml
vendored
Normal file
31
.github/ISSUE_TEMPLATE/feature_proposal.yml
vendored
Normal 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.
|
||||||
73
.github/ISSUE_TEMPLATE/support_request.yml
vendored
Normal file
73
.github/ISSUE_TEMPLATE/support_request.yml
vendored
Normal 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
|
||||||
3
.prettierrc
Normal file
3
.prettierrc
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"singleQuote": true
|
||||||
|
}
|
||||||
46
Gruntfile.js
46
Gruntfile.js
@@ -30,9 +30,9 @@ module.exports = function(grunt) {
|
|||||||
function() {
|
function() {
|
||||||
verifyNoGlobals(() => require('./lib/jasmine-core.js').noGlobals());
|
verifyNoGlobals(() => require('./lib/jasmine-core.js').noGlobals());
|
||||||
const done = this.async(),
|
const done = this.async(),
|
||||||
Jasmine = require('jasmine'),
|
Jasmine = require('jasmine'),
|
||||||
jasmineCore = require('./lib/jasmine-core.js'),
|
jasmineCore = require('./lib/jasmine-core.js'),
|
||||||
jasmine = new Jasmine({jasmineCore: jasmineCore});
|
jasmine = new Jasmine({jasmineCore: jasmineCore});
|
||||||
|
|
||||||
jasmine.loadConfigFile('./spec/support/jasmine.json');
|
jasmine.loadConfigFile('./spec/support/jasmine.json');
|
||||||
jasmine.exitOnCompletion = false;
|
jasmine.exitOnCompletion = false;
|
||||||
@@ -40,12 +40,50 @@ module.exports = function(grunt) {
|
|||||||
result => done(result.overallStatus === 'passed'),
|
result => done(result.overallStatus === 'passed'),
|
||||||
err => {
|
err => {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
exit(1);
|
done(false);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
grunt.registerTask("execSpecsInParallel",
|
||||||
|
"Run Jasmine core specs in parallel in Node.js",
|
||||||
|
function() {
|
||||||
|
// Need to require this here rather than at the top of the file
|
||||||
|
// so that we don't break verifyNoGlobals above by loading jasmine-core
|
||||||
|
// too early
|
||||||
|
const ParallelRunner = require('jasmine/parallel');
|
||||||
|
let numWorkers = require('os').cpus().length;
|
||||||
|
|
||||||
|
if (process.env['CIRCLECI']) {
|
||||||
|
// On Circle CI, the above gives the number of CPU cores on the host
|
||||||
|
// computer, which is unrelated to the resources actually available
|
||||||
|
// to the container. 2 workers gives peak performance with our current
|
||||||
|
// configuration, but 4 might increase the odds of discovering any
|
||||||
|
// parallel-specific bugs.
|
||||||
|
numWorkers = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
const done = this.async();
|
||||||
|
const runner = new ParallelRunner({
|
||||||
|
jasmineCore: require('./lib/jasmine-core.js'),
|
||||||
|
numWorkers
|
||||||
|
});
|
||||||
|
|
||||||
|
runner.loadConfigFile('./spec/support/jasmine.json')
|
||||||
|
.then(() => {
|
||||||
|
runner.exitOnCompletion = false;
|
||||||
|
return runner.execute();
|
||||||
|
}).then(
|
||||||
|
jasmineDoneInfo => done(jasmineDoneInfo.overallStatus === 'passed'),
|
||||||
|
err => {
|
||||||
|
console.error(err);
|
||||||
|
done(false);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
grunt.registerTask("execSpecsInNode:performance",
|
grunt.registerTask("execSpecsInNode:performance",
|
||||||
"Run Jasmine performance specs in Node.js",
|
"Run Jasmine performance specs in Node.js",
|
||||||
function() {
|
function() {
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
Copyright (c) 2008-2019 Pivotal Labs
|
Copyright (c) 2008-2019 Pivotal Labs
|
||||||
|
Copyright (c) 2008-2023 The Jasmine developers
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining
|
Permission is hereby granted, free of charge, to any person obtaining
|
||||||
a copy of this software and associated documentation files (the
|
a copy of this software and associated documentation files (the
|
||||||
23
README.md
23
README.md
@@ -1,13 +1,10 @@
|
|||||||
<a name="README">[<img src="https://rawgithub.com/jasmine/jasmine/main/images/jasmine-horizontal.svg" width="400px" />](http://jasmine.github.io)</a>
|
<a name="README">[<img src="https://rawgithub.com/jasmine/jasmine/main/images/jasmine-horizontal.svg" width="400px" />](http://jasmine.github.io)</a>
|
||||||
|
|
||||||
[](https://circleci.com/gh/jasmine/jasmine)
|
|
||||||
[](https://www.codetriage.com/jasmine/jasmine)
|
|
||||||
|
|
||||||
# A JavaScript Testing Framework
|
# A JavaScript Testing Framework
|
||||||
|
|
||||||
Jasmine is a Behavior Driven Development testing framework for JavaScript. It does not rely on browsers, DOM, or any JavaScript framework. Thus it's suited for websites, [Node.js](http://nodejs.org) projects, or anywhere that JavaScript can run.
|
Jasmine is a Behavior Driven Development testing framework for JavaScript. It does not rely on browsers, DOM, or any JavaScript framework. Thus it's suited for websites, [Node.js](http://nodejs.org) projects, or anywhere that JavaScript can run.
|
||||||
|
|
||||||
Upgrading from Jasmine 3.x? Check out the [upgrade guide](https://jasmine.github.io/tutorials/upgrading_to_Jasmine_4.0).
|
Upgrading from Jasmine 4.x? Check out the [upgrade guide](https://jasmine.github.io/tutorials/upgrading_to_Jasmine_5.0).
|
||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
|
|
||||||
@@ -30,13 +27,13 @@ for information on writing specs, and [the FAQ](https://jasmine.github.io/pages/
|
|||||||
Jasmine tests itself across popular browsers (Safari, Chrome, Firefox, and
|
Jasmine tests itself across popular browsers (Safari, Chrome, Firefox, and
|
||||||
Microsoft Edge) as well as Node.
|
Microsoft Edge) as well as Node.
|
||||||
|
|
||||||
| Environment | Supported versions |
|
| Environment | Supported versions |
|
||||||
|-------------------|--------------------|
|
|-------------------|---------------------|
|
||||||
| Node | 12.17+, 14, 16, 18 |
|
| Node | 18, 20, 22 |
|
||||||
| Safari | 14-15 |
|
| Safari | 15-17 |
|
||||||
| Chrome | Evergreen |
|
| Chrome | Evergreen |
|
||||||
| Firefox | Evergreen, 91 |
|
| Firefox | Evergreen, 102, 115 |
|
||||||
| Edge | Evergreen |
|
| Edge | Evergreen |
|
||||||
|
|
||||||
For evergreen browsers, each version of Jasmine is tested against the version of the browser that is available to us
|
For evergreen browsers, each version of Jasmine is tested against the version of the browser that is available to us
|
||||||
at the time of release. Other browsers, as well as older & newer versions of some supported browsers, are likely to work.
|
at the time of release. Other browsers, as well as older & newer versions of some supported browsers, are likely to work.
|
||||||
@@ -58,4 +55,6 @@ To find out what environments work with a particular Jasmine release, see the [r
|
|||||||
* [Christian Williams](mailto:antixian666@gmail.com)
|
* [Christian Williams](mailto:antixian666@gmail.com)
|
||||||
* Sheel Choksi
|
* Sheel Choksi
|
||||||
|
|
||||||
Copyright (c) 2008-2022 Jasmine Maintainers. This software is licensed under the [MIT License](https://github.com/jasmine/jasmine/blob/main/MIT.LICENSE).
|
Copyright (c) 2008-2019 Pivotal Labs<br>
|
||||||
|
Copyright (c) 2008-2023 The Jasmine developers<br>
|
||||||
|
This software is licensed under the [MIT License](https://github.com/jasmine/jasmine/blob/main/LICENSE).
|
||||||
|
|||||||
28
RELEASE.md
28
RELEASE.md
@@ -18,12 +18,11 @@ copied to `jasmine.js` when the distribution is built. When releasing a new
|
|||||||
version, update `package.json` with the new version and `npm run build` to
|
version, update `package.json` with the new version and `npm run build` to
|
||||||
update the gem version number.
|
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)
|
When `jasmine-core` revs its major or minor version, the `jasmine` NPM package
|
||||||
* Fixes for regressions.
|
should also rev to that version.
|
||||||
|
|
||||||
When jasmine-core revs its major or minor version, the binding libraries should also rev to that version.
|
|
||||||
|
|
||||||
## Release
|
## Release
|
||||||
|
|
||||||
@@ -61,20 +60,13 @@ for instructions.
|
|||||||
1. `rake release[${version}]` to copy the current edge docs to the new version
|
1. `rake release[${version}]` to copy the current edge docs to the new version
|
||||||
1. Commit and push.
|
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
|
### Publish the GitHub release
|
||||||
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`.)
|
|
||||||
|
|
||||||
### Finally
|
|
||||||
|
|
||||||
For each of the above GitHub repos:
|
|
||||||
1. Visit the releases page and find the tag just published.
|
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.
|
2. Paste in a link to the correct release notes for this release.
|
||||||
1. If it is a pre-release, mark it as such.
|
3. If it is a pre-release, mark it as such.
|
||||||
1. For core, attach the standalone zipfile.
|
4. Attach the standalone zipfile.
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ module.exports = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
files: [
|
files: [
|
||||||
{ src: [ root("MIT.LICENSE") ] },
|
{ src: [ root("LICENSE") ] },
|
||||||
{
|
{
|
||||||
src: [ "jasmine_favicon.png"],
|
src: [ "jasmine_favicon.png"],
|
||||||
dest: standaloneLibDir,
|
dest: standaloneLibDir,
|
||||||
|
|||||||
@@ -45,10 +45,6 @@ module.exports = {
|
|||||||
src: ['src/boot/boot1.js'],
|
src: ['src/boot/boot1.js'],
|
||||||
dest: 'lib/jasmine-core/boot1.js'
|
dest: 'lib/jasmine-core/boot1.js'
|
||||||
},
|
},
|
||||||
nodeBoot: {
|
|
||||||
src: ['src/boot/node_boot.js'],
|
|
||||||
dest: 'lib/jasmine-core/node_boot.js'
|
|
||||||
},
|
|
||||||
options: {
|
options: {
|
||||||
banner: license(),
|
banner: license(),
|
||||||
process: {
|
process: {
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
Copyright (c) 2008-<%= currentYear %> Pivotal Labs
|
Copyright (c) 2008-2019 Pivotal Labs
|
||||||
|
Copyright (c) 2008-<%= currentYear %> The Jasmine developers
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining
|
Permission is hereby granted, free of charge, to any person obtaining
|
||||||
a copy of this software and associated documentation files (the
|
a copy of this software and associated documentation files (the
|
||||||
|
|||||||
@@ -6,47 +6,61 @@
|
|||||||
const jasmineRequire = require('./jasmine-core/jasmine.js');
|
const jasmineRequire = require('./jasmine-core/jasmine.js');
|
||||||
module.exports = jasmineRequire;
|
module.exports = jasmineRequire;
|
||||||
|
|
||||||
/**
|
const boot = (function() {
|
||||||
* Boots a copy of Jasmine and returns an object as described in {@link jasmine}.
|
let jasmine, jasmineInterface;
|
||||||
* @type {function}
|
|
||||||
* @return {jasmine}
|
|
||||||
*/
|
|
||||||
module.exports.boot = require('./jasmine-core/node_boot.js');
|
|
||||||
|
|
||||||
/**
|
return function bootWithoutGlobals(reinitialize) {
|
||||||
* Boots a copy of Jasmine and returns an object containing the properties
|
if (!jasmineInterface || reinitialize === true) {
|
||||||
* that would normally be added to the global object. If noGlobals is called
|
jasmine = jasmineRequire.core(jasmineRequire);
|
||||||
* multiple times, the same object is returned every time.
|
|
||||||
*
|
|
||||||
* Do not call boot() if you also call noGlobals().
|
|
||||||
*
|
|
||||||
* @example
|
|
||||||
* const {describe, beforeEach, it, expect, jasmine} = require('jasmine-core').noGlobals();
|
|
||||||
*/
|
|
||||||
module.exports.noGlobals = (function() {
|
|
||||||
let jasmineInterface;
|
|
||||||
|
|
||||||
return function bootWithoutGlobals() {
|
|
||||||
if (!jasmineInterface) {
|
|
||||||
const jasmine = jasmineRequire.core(jasmineRequire);
|
|
||||||
const env = jasmine.getEnv({ suppressLoadErrors: true });
|
const env = jasmine.getEnv({ suppressLoadErrors: true });
|
||||||
jasmineInterface = jasmineRequire.interface(jasmine, env);
|
jasmineInterface = jasmineRequire.interface(jasmine, env);
|
||||||
}
|
}
|
||||||
|
|
||||||
return jasmineInterface;
|
return {jasmine, jasmineInterface};
|
||||||
};
|
};
|
||||||
}());
|
}());
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Boots a copy of Jasmine and returns an object as described in {@link jasmine}.
|
||||||
|
* If boot is called multiple times, the same object is returned every time
|
||||||
|
* unless true is passed.
|
||||||
|
* @param {boolean} [reinitialize=false] Whether to create a new copy of Jasmine if one already exists
|
||||||
|
* @type {function}
|
||||||
|
* @return {jasmine}
|
||||||
|
*/
|
||||||
|
module.exports.boot = function(reinitialize) {
|
||||||
|
const {jasmine, jasmineInterface} = boot(reinitialize);
|
||||||
|
|
||||||
|
for (const k in jasmineInterface) {
|
||||||
|
global[k] = jasmineInterface[k];
|
||||||
|
}
|
||||||
|
|
||||||
|
return jasmine;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Boots a copy of Jasmine and returns an object containing the properties
|
||||||
|
* that would normally be added to the global object. If noGlobals is called
|
||||||
|
* multiple times, the same object is returned every time unless true is passed.
|
||||||
|
*
|
||||||
|
* @param {boolean} [reinitialize=false] Whether to create a new copy of Jasmine if one already exists
|
||||||
|
* @example
|
||||||
|
* const {describe, beforeEach, it, expect, jasmine} = require('jasmine-core').noGlobals();
|
||||||
|
*/
|
||||||
|
module.exports.noGlobals = function(reinitialize) {
|
||||||
|
const {jasmineInterface} = boot(reinitialize);
|
||||||
|
return jasmineInterface;
|
||||||
|
};
|
||||||
|
|
||||||
const path = require('path'),
|
const path = require('path'),
|
||||||
fs = require('fs');
|
fs = require('fs');
|
||||||
|
|
||||||
const rootPath = path.join(__dirname, 'jasmine-core'),
|
const rootPath = path.join(__dirname, 'jasmine-core'),
|
||||||
bootFiles = ['boot0.js', 'boot1.js'],
|
bootFiles = ['boot0.js', 'boot1.js'],
|
||||||
legacyBootFiles = ['boot.js'],
|
legacyBootFiles = ['boot.js'],
|
||||||
nodeBootFiles = ['node_boot.js'],
|
|
||||||
cssFiles = [],
|
cssFiles = [],
|
||||||
jsFiles = [],
|
jsFiles = [],
|
||||||
jsFilesToSkip = ['jasmine.js'].concat(bootFiles, legacyBootFiles, nodeBootFiles);
|
jsFilesToSkip = ['jasmine.js'].concat(bootFiles, legacyBootFiles);
|
||||||
|
|
||||||
fs.readdirSync(rootPath).forEach(function(file) {
|
fs.readdirSync(rootPath).forEach(function(file) {
|
||||||
if(fs.statSync(path.join(rootPath, file)).isFile()) {
|
if(fs.statSync(path.join(rootPath, file)).isFile()) {
|
||||||
@@ -56,18 +70,18 @@ fs.readdirSync(rootPath).forEach(function(file) {
|
|||||||
break;
|
break;
|
||||||
case '.js':
|
case '.js':
|
||||||
if (jsFilesToSkip.indexOf(file) < 0) {
|
if (jsFilesToSkip.indexOf(file) < 0) {
|
||||||
jsFiles.push(file);
|
jsFiles.push(file);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
module.exports.files = {
|
module.exports.files = {
|
||||||
|
self: __filename,
|
||||||
path: rootPath,
|
path: rootPath,
|
||||||
bootDir: rootPath,
|
bootDir: rootPath,
|
||||||
bootFiles: bootFiles,
|
bootFiles: bootFiles,
|
||||||
nodeBootFiles: nodeBootFiles,
|
|
||||||
cssFiles: cssFiles,
|
cssFiles: cssFiles,
|
||||||
jsFiles: ['jasmine.js'].concat(jsFiles),
|
jsFiles: ['jasmine.js'].concat(jsFiles),
|
||||||
imagesDir: path.join(__dirname, '../images')
|
imagesDir: path.join(__dirname, '../images')
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
Copyright (c) 2008-2022 Pivotal Labs
|
Copyright (c) 2008-2019 Pivotal Labs
|
||||||
|
Copyright (c) 2008-2024 The Jasmine developers
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining
|
Permission is hereby granted, free of charge, to any person obtaining
|
||||||
a copy of this software and associated documentation files (the
|
a copy of this software and associated documentation files (the
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
Copyright (c) 2008-2022 Pivotal Labs
|
Copyright (c) 2008-2019 Pivotal Labs
|
||||||
|
Copyright (c) 2008-2024 The Jasmine developers
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining
|
Permission is hereby granted, free of charge, to any person obtaining
|
||||||
a copy of this software and associated documentation files (the
|
a copy of this software and associated documentation files (the
|
||||||
|
|||||||
@@ -1,24 +1,24 @@
|
|||||||
function Player() {
|
class Player {
|
||||||
}
|
play(song) {
|
||||||
Player.prototype.play = function(song) {
|
this.currentlyPlayingSong = song;
|
||||||
this.currentlyPlayingSong = song;
|
this.isPlaying = true;
|
||||||
this.isPlaying = true;
|
|
||||||
};
|
|
||||||
|
|
||||||
Player.prototype.pause = function() {
|
|
||||||
this.isPlaying = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
Player.prototype.resume = function() {
|
|
||||||
if (this.isPlaying) {
|
|
||||||
throw new Error("song is already playing");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.isPlaying = true;
|
pause() {
|
||||||
};
|
this.isPlaying = false;
|
||||||
|
}
|
||||||
|
|
||||||
Player.prototype.makeFavorite = function() {
|
resume() {
|
||||||
this.currentlyPlayingSong.persistFavoriteStatus(true);
|
if (this.isPlaying) {
|
||||||
};
|
throw new Error('song is already playing');
|
||||||
|
}
|
||||||
|
|
||||||
|
this.isPlaying = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
makeFavorite() {
|
||||||
|
this.currentlyPlayingSong.persistFavoriteStatus(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
module.exports = Player;
|
module.exports = Player;
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
function Song() {
|
class Song {
|
||||||
|
persistFavoriteStatus(value) {
|
||||||
|
// something complicated
|
||||||
|
throw new Error('not yet implemented');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Song.prototype.persistFavoriteStatus = function(value) {
|
|
||||||
// something complicated
|
|
||||||
throw new Error("not yet implemented");
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports = Song;
|
module.exports = Song;
|
||||||
|
|||||||
@@ -3,11 +3,11 @@ beforeEach(function () {
|
|||||||
toBePlaying: function () {
|
toBePlaying: function () {
|
||||||
return {
|
return {
|
||||||
compare: function (actual, expected) {
|
compare: function (actual, expected) {
|
||||||
var player = actual;
|
const player = actual;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
pass: player.currentlyPlayingSong === expected && player.isPlaying
|
pass: player.currentlyPlayingSong === expected && player.isPlaying
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,36 +1,37 @@
|
|||||||
describe("Player", function() {
|
const Player = require('../../lib/jasmine_examples/Player');
|
||||||
var Player = require('../../lib/jasmine_examples/Player');
|
const Song = require('../../lib/jasmine_examples/Song');
|
||||||
var Song = require('../../lib/jasmine_examples/Song');
|
|
||||||
var player;
|
describe('Player', function() {
|
||||||
var song;
|
let player;
|
||||||
|
let song;
|
||||||
|
|
||||||
beforeEach(function() {
|
beforeEach(function() {
|
||||||
player = new Player();
|
player = new Player();
|
||||||
song = new Song();
|
song = new Song();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should be able to play a Song", function() {
|
it('should be able to play a Song', function() {
|
||||||
player.play(song);
|
player.play(song);
|
||||||
expect(player.currentlyPlayingSong).toEqual(song);
|
expect(player.currentlyPlayingSong).toEqual(song);
|
||||||
|
|
||||||
//demonstrates use of custom matcher
|
// demonstrates use of custom matcher
|
||||||
expect(player).toBePlaying(song);
|
expect(player).toBePlaying(song);
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("when song has been paused", function() {
|
describe('when song has been paused', function() {
|
||||||
beforeEach(function() {
|
beforeEach(function() {
|
||||||
player.play(song);
|
player.play(song);
|
||||||
player.pause();
|
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();
|
expect(player.isPlaying).toBeFalsy();
|
||||||
|
|
||||||
// demonstrates use of 'not' with a custom matcher
|
// demonstrates use of 'not' with a custom matcher
|
||||||
expect(player).not.toBePlaying(song);
|
expect(player).not.toBePlaying(song);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should be possible to resume", function() {
|
it('should be possible to resume', function() {
|
||||||
player.resume();
|
player.resume();
|
||||||
expect(player.isPlaying).toBeTruthy();
|
expect(player.isPlaying).toBeTruthy();
|
||||||
expect(player.currentlyPlayingSong).toEqual(song);
|
expect(player.currentlyPlayingSong).toEqual(song);
|
||||||
@@ -38,7 +39,7 @@ describe("Player", function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// demonstrates use of spies to intercept and test method calls
|
// 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');
|
spyOn(song, 'persistFavoriteStatus');
|
||||||
|
|
||||||
player.play(song);
|
player.play(song);
|
||||||
@@ -48,13 +49,13 @@ describe("Player", function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
//demonstrates use of expected exceptions
|
//demonstrates use of expected exceptions
|
||||||
describe("#resume", function() {
|
describe('#resume', function() {
|
||||||
it("should throw an exception if song is already playing", function() {
|
it('should throw an exception if song is already playing', function() {
|
||||||
player.play(song);
|
player.play(song);
|
||||||
|
|
||||||
expect(function() {
|
expect(function() {
|
||||||
player.resume();
|
player.resume();
|
||||||
}).toThrowError("song is already playing");
|
}).toThrowError('song is already playing');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,34 +1,34 @@
|
|||||||
describe("Player", function() {
|
describe('Player', function() {
|
||||||
var player;
|
let player;
|
||||||
var song;
|
let song;
|
||||||
|
|
||||||
beforeEach(function() {
|
beforeEach(function() {
|
||||||
player = new Player();
|
player = new Player();
|
||||||
song = new Song();
|
song = new Song();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should be able to play a Song", function() {
|
it('should be able to play a Song', function() {
|
||||||
player.play(song);
|
player.play(song);
|
||||||
expect(player.currentlyPlayingSong).toEqual(song);
|
expect(player.currentlyPlayingSong).toEqual(song);
|
||||||
|
|
||||||
//demonstrates use of custom matcher
|
// demonstrates use of custom matcher
|
||||||
expect(player).toBePlaying(song);
|
expect(player).toBePlaying(song);
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("when song has been paused", function() {
|
describe('when song has been paused', function() {
|
||||||
beforeEach(function() {
|
beforeEach(function() {
|
||||||
player.play(song);
|
player.play(song);
|
||||||
player.pause();
|
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();
|
expect(player.isPlaying).toBeFalsy();
|
||||||
|
|
||||||
// demonstrates use of 'not' with a custom matcher
|
// demonstrates use of 'not' with a custom matcher
|
||||||
expect(player).not.toBePlaying(song);
|
expect(player).not.toBePlaying(song);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should be possible to resume", function() {
|
it('should be possible to resume', function() {
|
||||||
player.resume();
|
player.resume();
|
||||||
expect(player.isPlaying).toBeTruthy();
|
expect(player.isPlaying).toBeTruthy();
|
||||||
expect(player.currentlyPlayingSong).toEqual(song);
|
expect(player.currentlyPlayingSong).toEqual(song);
|
||||||
@@ -36,7 +36,7 @@ describe("Player", function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// demonstrates use of spies to intercept and test method calls
|
// 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');
|
spyOn(song, 'persistFavoriteStatus');
|
||||||
|
|
||||||
player.play(song);
|
player.play(song);
|
||||||
@@ -46,13 +46,13 @@ describe("Player", function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
//demonstrates use of expected exceptions
|
//demonstrates use of expected exceptions
|
||||||
describe("#resume", function() {
|
describe('#resume', function() {
|
||||||
it("should throw an exception if song is already playing", function() {
|
it('should throw an exception if song is already playing', function() {
|
||||||
player.play(song);
|
player.play(song);
|
||||||
|
|
||||||
expect(function() {
|
expect(function() {
|
||||||
player.resume();
|
player.resume();
|
||||||
}).toThrowError("song is already playing");
|
}).toThrowError('song is already playing');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ beforeEach(function () {
|
|||||||
toBePlaying: function () {
|
toBePlaying: function () {
|
||||||
return {
|
return {
|
||||||
compare: function (actual, expected) {
|
compare: function (actual, expected) {
|
||||||
var player = actual;
|
const player = actual;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
pass: player.currentlyPlayingSong === expected && player.isPlaying
|
pass: player.currentlyPlayingSong === expected && player.isPlaying
|
||||||
|
|||||||
@@ -1,22 +1,22 @@
|
|||||||
function Player() {
|
class Player {
|
||||||
}
|
play(song) {
|
||||||
Player.prototype.play = function(song) {
|
this.currentlyPlayingSong = song;
|
||||||
this.currentlyPlayingSong = song;
|
this.isPlaying = true;
|
||||||
this.isPlaying = true;
|
|
||||||
};
|
|
||||||
|
|
||||||
Player.prototype.pause = function() {
|
|
||||||
this.isPlaying = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
Player.prototype.resume = function() {
|
|
||||||
if (this.isPlaying) {
|
|
||||||
throw new Error("song is already playing");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.isPlaying = true;
|
pause() {
|
||||||
};
|
this.isPlaying = false;
|
||||||
|
}
|
||||||
|
|
||||||
Player.prototype.makeFavorite = function() {
|
resume() {
|
||||||
this.currentlyPlayingSong.persistFavoriteStatus(true);
|
if (this.isPlaying) {
|
||||||
};
|
throw new Error('song is already playing');
|
||||||
|
}
|
||||||
|
|
||||||
|
this.isPlaying = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
makeFavorite() {
|
||||||
|
this.currentlyPlayingSong.persistFavoriteStatus(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
function Song() {
|
class Song {
|
||||||
|
persistFavoriteStatus(value) {
|
||||||
|
// something complicated
|
||||||
|
throw new Error('not yet implemented');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Song.prototype.persistFavoriteStatus = function(value) {
|
|
||||||
// something complicated
|
|
||||||
throw new Error("not yet implemented");
|
|
||||||
};
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
Copyright (c) 2008-2022 Pivotal Labs
|
Copyright (c) 2008-2019 Pivotal Labs
|
||||||
|
Copyright (c) 2008-2024 The Jasmine developers
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining
|
Permission is hereby granted, free of charge, to any person obtaining
|
||||||
a copy of this software and associated documentation files (the
|
a copy of this software and associated documentation files (the
|
||||||
@@ -462,7 +463,11 @@ jasmineRequire.HtmlReporter = function(j$) {
|
|||||||
'tr',
|
'tr',
|
||||||
{},
|
{},
|
||||||
createDom('td', {}, entry.timestamp.toString()),
|
createDom('td', {}, entry.timestamp.toString()),
|
||||||
createDom('td', {}, entry.message)
|
createDom(
|
||||||
|
'td',
|
||||||
|
{ className: 'jasmine-debug-log-msg' },
|
||||||
|
entry.message
|
||||||
|
)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@@ -530,14 +535,13 @@ jasmineRequire.HtmlReporter = function(j$) {
|
|||||||
if (noExpectations(resultNode.result)) {
|
if (noExpectations(resultNode.result)) {
|
||||||
specDescription = 'SPEC HAS NO EXPECTATIONS ' + specDescription;
|
specDescription = 'SPEC HAS NO EXPECTATIONS ' + specDescription;
|
||||||
}
|
}
|
||||||
if (
|
if (resultNode.result.status === 'pending') {
|
||||||
resultNode.result.status === 'pending' &&
|
if (resultNode.result.pendingReason !== '') {
|
||||||
resultNode.result.pendingReason !== ''
|
specDescription +=
|
||||||
) {
|
' PENDING WITH MESSAGE: ' + resultNode.result.pendingReason;
|
||||||
specDescription =
|
} else {
|
||||||
specDescription +
|
specDescription += ' PENDING';
|
||||||
' PENDING WITH MESSAGE: ' +
|
}
|
||||||
resultNode.result.pendingReason;
|
|
||||||
}
|
}
|
||||||
specListNode.appendChild(
|
specListNode.appendChild(
|
||||||
createDom(
|
createDom(
|
||||||
|
|||||||
@@ -55,9 +55,6 @@ body {
|
|||||||
position: fixed;
|
position: fixed;
|
||||||
right: 100%;
|
right: 100%;
|
||||||
}
|
}
|
||||||
.jasmine_html-reporter .jasmine-version {
|
|
||||||
color: #aaa;
|
|
||||||
}
|
|
||||||
.jasmine_html-reporter .jasmine-banner {
|
.jasmine_html-reporter .jasmine-banner {
|
||||||
margin-top: 14px;
|
margin-top: 14px;
|
||||||
}
|
}
|
||||||
@@ -169,10 +166,11 @@ body {
|
|||||||
}
|
}
|
||||||
.jasmine_html-reporter .jasmine-bar.jasmine-menu {
|
.jasmine_html-reporter .jasmine-bar.jasmine-menu {
|
||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
color: #aaa;
|
color: #000;
|
||||||
}
|
}
|
||||||
.jasmine_html-reporter .jasmine-bar.jasmine-menu a {
|
.jasmine_html-reporter .jasmine-bar.jasmine-menu a {
|
||||||
color: #333;
|
color: blue;
|
||||||
|
text-decoration: underline;
|
||||||
}
|
}
|
||||||
.jasmine_html-reporter .jasmine-bar a {
|
.jasmine_html-reporter .jasmine-bar a {
|
||||||
color: white;
|
color: white;
|
||||||
@@ -298,3 +296,6 @@ body {
|
|||||||
.jasmine_html-reporter .jasmine-debug-log table, .jasmine_html-reporter .jasmine-debug-log th, .jasmine_html-reporter .jasmine-debug-log td {
|
.jasmine_html-reporter .jasmine-debug-log table, .jasmine_html-reporter .jasmine-debug-log th, .jasmine_html-reporter .jasmine-debug-log td {
|
||||||
border: 1px solid #ddd;
|
border: 1px solid #ddd;
|
||||||
}
|
}
|
||||||
|
.jasmine_html-reporter .jasmine-debug-log .jasmine-debug-log-msg {
|
||||||
|
white-space: pre;
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,38 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright (c) 2008-2022 Pivotal Labs
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining
|
|
||||||
a copy of this software and associated documentation files (the
|
|
||||||
"Software"), to deal in the Software without restriction, including
|
|
||||||
without limitation the rights to use, copy, modify, merge, publish,
|
|
||||||
distribute, sublicense, and/or sell copies of the Software, and to
|
|
||||||
permit persons to whom the Software is furnished to do so, subject to
|
|
||||||
the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be
|
|
||||||
included in all copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
||||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
||||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
||||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
||||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
||||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
||||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
module.exports = function(jasmineRequire) {
|
|
||||||
const jasmine = jasmineRequire.core(jasmineRequire);
|
|
||||||
|
|
||||||
const env = jasmine.getEnv({ suppressLoadErrors: true });
|
|
||||||
|
|
||||||
const jasmineInterface = jasmineRequire.interface(jasmine, env);
|
|
||||||
|
|
||||||
extend(global, jasmineInterface);
|
|
||||||
|
|
||||||
function extend(destination, source) {
|
|
||||||
for (const property in source) destination[property] = source[property];
|
|
||||||
return destination;
|
|
||||||
}
|
|
||||||
|
|
||||||
return jasmine;
|
|
||||||
};
|
|
||||||
69
package.json
69
package.json
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "jasmine-core",
|
"name": "jasmine-core",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"version": "4.3.0",
|
"version": "5.2.0",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/jasmine/jasmine.git"
|
"url": "https://github.com/jasmine/jasmine.git"
|
||||||
@@ -27,84 +27,35 @@
|
|||||||
"homepage": "https://jasmine.github.io",
|
"homepage": "https://jasmine.github.io",
|
||||||
"main": "./lib/jasmine-core.js",
|
"main": "./lib/jasmine-core.js",
|
||||||
"files": [
|
"files": [
|
||||||
"MIT.LICENSE",
|
"LICENSE",
|
||||||
"README.md",
|
"README.md",
|
||||||
"images/*.{png,svg}",
|
"images/*.{png,svg}",
|
||||||
"lib/**/*.{js,css}",
|
"lib/**/*.{js,css}",
|
||||||
"package.json"
|
"package.json"
|
||||||
],
|
],
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"eslint": "^7.32.0",
|
"eslint": "^8.36.0",
|
||||||
"eslint-plugin-compat": "^4.0.0",
|
"eslint-plugin-compat": "^4.0.0",
|
||||||
"glob": "^7.2.0",
|
"glob": "^10.2.3",
|
||||||
"grunt": "^1.0.4",
|
"grunt": "^1.0.4",
|
||||||
"grunt-cli": "^1.3.2",
|
"grunt-cli": "^1.3.2",
|
||||||
"grunt-contrib-compress": "^2.0.0",
|
"grunt-contrib-compress": "^2.0.0",
|
||||||
"grunt-contrib-concat": "^2.0.0",
|
"grunt-contrib-concat": "^2.0.0",
|
||||||
"grunt-css-url-embed": "^1.11.1",
|
"grunt-css-url-embed": "^1.11.1",
|
||||||
"grunt-sass": "^3.0.2",
|
"grunt-sass": "^3.0.2",
|
||||||
"jasmine": "^4.1.0",
|
"jasmine": "^5.0.0",
|
||||||
"jasmine-browser-runner": "^1.0.0",
|
"jasmine-browser-runner": "github:jasmine/jasmine-browser-runner",
|
||||||
"jsdom": "^19.0.0",
|
"jsdom": "^22.0.0",
|
||||||
"load-grunt-tasks": "^5.1.0",
|
"load-grunt-tasks": "^5.1.0",
|
||||||
"prettier": "1.17.1",
|
"prettier": "1.17.1",
|
||||||
"sass": "^1.45.1",
|
"sass": "^1.58.3",
|
||||||
"shelljs": "^0.8.3",
|
"shelljs": "^0.8.3",
|
||||||
"temp": "^0.9.0"
|
"temp": "^0.9.0"
|
||||||
},
|
},
|
||||||
"prettier": {
|
|
||||||
"singleQuote": true
|
|
||||||
},
|
|
||||||
"eslintConfig": {
|
|
||||||
"extends": [
|
|
||||||
"plugin:compat/recommended"
|
|
||||||
],
|
|
||||||
"env": {
|
|
||||||
"browser": true,
|
|
||||||
"node": true,
|
|
||||||
"es2017": true
|
|
||||||
},
|
|
||||||
"parserOptions": {
|
|
||||||
"ecmaVersion": 2018
|
|
||||||
},
|
|
||||||
"rules": {
|
|
||||||
"quotes": [
|
|
||||||
"error",
|
|
||||||
"single",
|
|
||||||
{
|
|
||||||
"avoidEscape": true
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"no-unused-vars": [
|
|
||||||
"error",
|
|
||||||
{
|
|
||||||
"args": "none"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"no-implicit-globals": "error",
|
|
||||||
"block-spacing": "error",
|
|
||||||
"func-call-spacing": [
|
|
||||||
"error",
|
|
||||||
"never"
|
|
||||||
],
|
|
||||||
"key-spacing": "error",
|
|
||||||
"no-tabs": "error",
|
|
||||||
"no-trailing-spaces": "error",
|
|
||||||
"no-whitespace-before-property": "error",
|
|
||||||
"semi": [
|
|
||||||
"error",
|
|
||||||
"always"
|
|
||||||
],
|
|
||||||
"space-before-blocks": "error",
|
|
||||||
"no-eval": "error",
|
|
||||||
"no-var": "error"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"browserslist": [
|
"browserslist": [
|
||||||
"Safari >= 14",
|
"Safari >= 15",
|
||||||
|
"Firefox >= 102",
|
||||||
"last 2 Chrome versions",
|
"last 2 Chrome versions",
|
||||||
"last 2 Firefox versions",
|
|
||||||
"Firefox >= 91",
|
|
||||||
"last 2 Edge versions"
|
"last 2 Edge versions"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
28
release_notes/4.4.0.md
Normal file
28
release_notes/4.4.0.md
Normal 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
40
release_notes/4.5.0.md
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
# Jasmine 4.5.0 Release Notes
|
||||||
|
|
||||||
|
## New Features
|
||||||
|
|
||||||
|
* Added Safari 16 to supported browsers
|
||||||
|
* Include inner exceptions in stack traces
|
||||||
|
|
||||||
|
## Bug Fixes
|
||||||
|
|
||||||
|
* Report exceptions thrown by a describe before any it calls
|
||||||
|
* Coerce the random string to a seed before sending it to reporters
|
||||||
|
* This fixes an error in HTMLReporter when the configured seed is a
|
||||||
|
number rather than a string, which has been allowed since 3.8.0
|
||||||
|
|
||||||
|
## Documentation updates
|
||||||
|
|
||||||
|
* Fixed the jsdoc types of SuiteResult and SpecResult ids
|
||||||
|
* Replaced var with const in API doc examples
|
||||||
|
* Updated the style of the examples that are included in jasmine-core
|
||||||
|
|
||||||
|
## Internal improvements
|
||||||
|
|
||||||
|
* Converted TreeProcessor to async/await
|
||||||
|
* Converted ReportDispatcher to promises
|
||||||
|
|
||||||
|
## Supported environments
|
||||||
|
|
||||||
|
jasmine-core 4.5.0 has been tested in the following environments.
|
||||||
|
|
||||||
|
| Environment | Supported versions |
|
||||||
|
|-------------------|--------------------|
|
||||||
|
| Node | 12.17+, 14, 16, 18 |
|
||||||
|
| Safari | 14-16 |
|
||||||
|
| Chrome | 107 |
|
||||||
|
| Firefox | 91, 102, 106 |
|
||||||
|
| Edge | 106 |
|
||||||
|
|
||||||
|
------
|
||||||
|
|
||||||
|
_Release Notes generated with _[Anchorman](http://github.com/infews/anchorman)_
|
||||||
19
release_notes/4.6.0.md
Normal file
19
release_notes/4.6.0.md
Normal 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)_
|
||||||
51
release_notes/4.6.1.md
Normal file
51
release_notes/4.6.1.md
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
# Jasmine Core 4.6.1 Release Notes
|
||||||
|
|
||||||
|
## Summary
|
||||||
|
|
||||||
|
This is a one-time backport of bug fixes from 5.x, for the benefit of Karma
|
||||||
|
users who may not be aware that they're still using 4.x.
|
||||||
|
|
||||||
|
No further 4.x releases are planned. If possible, you should upgrade to the
|
||||||
|
latest 5.x instead of 4.6.1. If you're using Karma, you can do this by
|
||||||
|
installing jasmine-core 5.x and adding an override to package.json:
|
||||||
|
|
||||||
|
```
|
||||||
|
{
|
||||||
|
// ...
|
||||||
|
"overrides": {
|
||||||
|
"karma-jasmine": {
|
||||||
|
"jasmine-core": "^5.0.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Bug Fixes
|
||||||
|
|
||||||
|
* Removed unnecessary throw when building stack trace
|
||||||
|
|
||||||
|
* Fixed error when formatting Error object with non-Error cause property
|
||||||
|
|
||||||
|
Merges [#2013](https://github.com/jasmine/jasmine/pull/2013) from @angrycat9000.
|
||||||
|
|
||||||
|
Fixes [#2011](https://github.com/jasmine/jasmine/issues/2011).
|
||||||
|
|
||||||
|
* Accessibility: Always provide a non-color indication that a spec is pending
|
||||||
|
|
||||||
|
* Accessibility: Improved contrast of version number and inactive tab links
|
||||||
|
|
||||||
|
## Supported environments
|
||||||
|
|
||||||
|
jasmine-core 4.6.1 has been tested in the following environments.
|
||||||
|
|
||||||
|
| Environment | Supported versions |
|
||||||
|
|-------------------|--------------------|
|
||||||
|
| Node | 12.17+, 14, 16, 18 |
|
||||||
|
| Safari | 14-16 |
|
||||||
|
| Chrome | 125 |
|
||||||
|
| Firefox | 91, 102, 126 |
|
||||||
|
| Edge | 124 |
|
||||||
|
|
||||||
|
------
|
||||||
|
|
||||||
|
_Release Notes generated with _[Anchorman](http://github.com/infews/anchorman)_
|
||||||
68
release_notes/5.0.0-alpha.0.md
Normal file
68
release_notes/5.0.0-alpha.0.md
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
# Jasmine Core 5.0.0-alpha.0 Release Notes
|
||||||
|
|
||||||
|
## Summary
|
||||||
|
|
||||||
|
This release primarily adds support for parallel execution via the `jasmine`
|
||||||
|
package. Please see its release notes and the
|
||||||
|
[parallel documentation](https://jasmine.github.io/tutorials/running_specs_in_parallel)
|
||||||
|
for more information. Additionally, this release cleans up a few outdated
|
||||||
|
interfaces.
|
||||||
|
|
||||||
|
This is a pre-release for a major version. It contains breaking changes, and
|
||||||
|
there may be further breaking changes between this release and the final 5.0.0
|
||||||
|
release.
|
||||||
|
|
||||||
|
## Breaking changes
|
||||||
|
|
||||||
|
* Use addEventListener in browsers rather than setting window.onerror
|
||||||
|
|
||||||
|
This simplifies error handling in browsers, makes Jasmine's own integration
|
||||||
|
tests easier to debug, and provides stack traces for more unhandled
|
||||||
|
exceptions. However, some browsers will provide less error information when
|
||||||
|
the error comes from a file:// URL. Additionally, Jasmine will no longer
|
||||||
|
override existing onerror handlers, and setting window.onerror will no longer
|
||||||
|
override Jasmine's global error handling. (Use `jasmine.spyOnGlobalErrors`
|
||||||
|
instead.)
|
||||||
|
|
||||||
|
* Made Env#execute async
|
||||||
|
* Env#execute no longer takes a callback
|
||||||
|
* The `boot` function exported by the core module returns the same object
|
||||||
|
every time it's called.
|
||||||
|
* Removed node_boot.js. Use the exported `boot` function instead.
|
||||||
|
|
||||||
|
### Changes to supported environments
|
||||||
|
|
||||||
|
The following previously supported environments are no longer supported:
|
||||||
|
|
||||||
|
* Node <16.14
|
||||||
|
* Safari 14
|
||||||
|
* Firefox 91
|
||||||
|
|
||||||
|
Although this release may still work in some of those environments, we no
|
||||||
|
longer test against them and won't try to maintain compatibility with them in
|
||||||
|
future releases.
|
||||||
|
|
||||||
|
## New features
|
||||||
|
|
||||||
|
* Support for parallel execution in Node.js using the `jasmine` package
|
||||||
|
|
||||||
|
## Bug fixes
|
||||||
|
|
||||||
|
* The global error handler is uninstalled at the end of env execution.
|
||||||
|
|
||||||
|
## Supported environments
|
||||||
|
|
||||||
|
jasmine-core 5.0.0-alpha.0 has been tested in the following environments.
|
||||||
|
|
||||||
|
| Environment | Supported versions |
|
||||||
|
|-------------------|--------------------|
|
||||||
|
| Node | 16.14+, 18 |
|
||||||
|
| Safari | 15-16 |
|
||||||
|
| Chrome | 111 |
|
||||||
|
| Firefox | 102, 111 |
|
||||||
|
| Edge | 111 |
|
||||||
|
|
||||||
|
|
||||||
|
------
|
||||||
|
|
||||||
|
_Release Notes generated with _[Anchorman](http://github.com/infews/anchorman)_
|
||||||
39
release_notes/5.0.0-alpha.1.md
Normal file
39
release_notes/5.0.0-alpha.1.md
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
# Jasmine Core 5.0.0-alpha.1 Release Notes
|
||||||
|
|
||||||
|
## Summary
|
||||||
|
|
||||||
|
This release provides improved support for parallel execution via the `jasmine`
|
||||||
|
package. Please see its release notes and the
|
||||||
|
[parallel documentation](https://jasmine.github.io/tutorials/running_specs_in_parallel)
|
||||||
|
for more information.
|
||||||
|
|
||||||
|
## New features and bug fixes
|
||||||
|
|
||||||
|
* Parallel: Cleaner interface for reporter dispatching
|
||||||
|
* When Env#config is called from spec and helper files in parallel mode, throw
|
||||||
|
an error rather than behaving unpredictably.
|
||||||
|
|
||||||
|
## Documentation improvements
|
||||||
|
|
||||||
|
* API reference docs for parallel support APIs that jasmine-npm uses
|
||||||
|
|
||||||
|
## Internal improvements
|
||||||
|
|
||||||
|
* Updated dev dependencies
|
||||||
|
|
||||||
|
## Supported environments
|
||||||
|
|
||||||
|
jasmine-core 5.0.0-alpha.1 has been tested in the following environments.
|
||||||
|
|
||||||
|
| Environment | Supported versions |
|
||||||
|
|-------------------|--------------------|
|
||||||
|
| Node | 16.14+, 18 |
|
||||||
|
| Safari | 15-16 |
|
||||||
|
| Chrome | 112 |
|
||||||
|
| Firefox | 102, 111 |
|
||||||
|
| Edge | 111 |
|
||||||
|
|
||||||
|
|
||||||
|
------
|
||||||
|
|
||||||
|
_Release Notes generated with _[Anchorman](http://github.com/infews/anchorman)_
|
||||||
28
release_notes/5.0.0-beta.0.md
Normal file
28
release_notes/5.0.0-beta.0.md
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
# Jasmine Core 5.0.0-beta.0 Release Notes
|
||||||
|
|
||||||
|
This release supports the 5.0.0-beta-0 release of the `jasmine` package.
|
||||||
|
|
||||||
|
## Breaking changes
|
||||||
|
|
||||||
|
* Dropped support for Node 16
|
||||||
|
|
||||||
|
## New features
|
||||||
|
|
||||||
|
* Added support for Node 20
|
||||||
|
|
||||||
|
## Supported environments
|
||||||
|
|
||||||
|
jasmine-core 5.0.0-beta.0 has been tested in the following environments.
|
||||||
|
|
||||||
|
| Environment | Supported versions |
|
||||||
|
|-------------------|--------------------|
|
||||||
|
| Node | 18, 20 |
|
||||||
|
| Safari | 15-16 |
|
||||||
|
| Chrome | 112 |
|
||||||
|
| Firefox | 102, 112 |
|
||||||
|
| Edge | 112 |
|
||||||
|
|
||||||
|
|
||||||
|
------
|
||||||
|
|
||||||
|
_Release Notes generated with _[Anchorman](http://github.com/infews/anchorman)_
|
||||||
55
release_notes/5.0.0.md
Normal file
55
release_notes/5.0.0.md
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
# Jasmine Core 5.0.0 Release Notes
|
||||||
|
|
||||||
|
## Summary
|
||||||
|
|
||||||
|
This is a major release that includes breaking changes. It primarily adds
|
||||||
|
support for parallel execution in Node via the `jasmine` package. Most users
|
||||||
|
should be able to upgrade without changes, but please read the list of breaking
|
||||||
|
changes below and see the [migration guide](https://jasmine.github.io/tutorials/upgrading_to_Jasmine_5.0)
|
||||||
|
for more information.
|
||||||
|
|
||||||
|
## Breaking changes
|
||||||
|
|
||||||
|
* Dropped support for Node 12, 14, and 16
|
||||||
|
* Dropped support for Safari 14 and Firefox 91
|
||||||
|
* Made Env#execute async and removed the callback parameter
|
||||||
|
* Global errors are detected via addEventListener rather than setting window.onerror
|
||||||
|
* The `boot` function exported by the core module returns the same object
|
||||||
|
every time it's called.
|
||||||
|
* Removed node_boot.js. Use the exported `boot` function instead.
|
||||||
|
|
||||||
|
## New features
|
||||||
|
|
||||||
|
* Support for parallel execution in Node via the `jasmine` package
|
||||||
|
See the [parallel guide](https://jasmine.github.io/tutorials/running_specs_in_parallel)
|
||||||
|
for more information.
|
||||||
|
* Added Node 20 to supported environments
|
||||||
|
|
||||||
|
## Bug fixes
|
||||||
|
|
||||||
|
* Accessibility: Always provide a non-color indication that a spec is pending
|
||||||
|
* Accessibility: Improved contrast of version number and inactive tab links
|
||||||
|
* Uninstall the global error handler at the end of env execution
|
||||||
|
|
||||||
|
## Internal improvements
|
||||||
|
|
||||||
|
* Updated dev dependencies
|
||||||
|
* Dogfood parallel execution feature in CI
|
||||||
|
|
||||||
|
## Supported environments
|
||||||
|
|
||||||
|
jasmine-core 5.0.0 has been tested in the following environments.
|
||||||
|
|
||||||
|
| Environment | Supported versions |
|
||||||
|
|-------------------|--------------------|
|
||||||
|
| Node | 18, 20 |
|
||||||
|
| Safari | 15-16 |
|
||||||
|
| Chrome | 113 |
|
||||||
|
| Firefox | 102, 113 |
|
||||||
|
| Edge | 113 |
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
------
|
||||||
|
|
||||||
|
_Release Notes generated with _[Anchorman](http://github.com/infews/anchorman)_
|
||||||
25
release_notes/5.0.1.md
Normal file
25
release_notes/5.0.1.md
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
# Jasmine Core 5.0.1 Release Notes
|
||||||
|
|
||||||
|
## Changes
|
||||||
|
|
||||||
|
* Optionally restore the pre-5.0 behavior of boot() always creating a new instance
|
||||||
|
|
||||||
|
This is needed by jasmine-npm (and likely other tools like it) that may
|
||||||
|
need to create and use multiple envs in sequence.
|
||||||
|
|
||||||
|
## Supported environments
|
||||||
|
|
||||||
|
jasmine-core 5.0.1 has been tested in the following environments.
|
||||||
|
|
||||||
|
| Environment | Supported versions |
|
||||||
|
|-------------------|--------------------|
|
||||||
|
| Node | 18, 20 |
|
||||||
|
| Safari | 15-16 |
|
||||||
|
| Chrome | 114 |
|
||||||
|
| Firefox | 102, 113 |
|
||||||
|
| Edge | 113 |
|
||||||
|
|
||||||
|
|
||||||
|
------
|
||||||
|
|
||||||
|
_Release Notes generated with _[Anchorman](http://github.com/infews/anchorman)_
|
||||||
39
release_notes/5.1.0.md
Normal file
39
release_notes/5.1.0.md
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
# Jasmine Core 5.1.0 Release Notes
|
||||||
|
|
||||||
|
## Changes
|
||||||
|
|
||||||
|
* Exclude inherited Error properties from stack trace
|
||||||
|
|
||||||
|
* Fixed error when formatting Error object with non-Error cause property
|
||||||
|
|
||||||
|
Merges [#2013](https://github.com/jasmine/jasmine/pull/2013) from @angrycat9000.
|
||||||
|
|
||||||
|
Fixes [#2011](https://github.com/jasmine/jasmine/issues/2011).
|
||||||
|
|
||||||
|
* Added `throwUnless` and `throwUnlessAsync`
|
||||||
|
|
||||||
|
These are similar to `expect` and `expectAsync` except that they throw
|
||||||
|
exceptions rather than recording matcher failures as spec/suite failures.
|
||||||
|
They're intended to support using Jasmine matchers in [testing-library's](https://testing-library.com/)
|
||||||
|
`waitFor`, and also provide a way to integration-test custom matchers.
|
||||||
|
|
||||||
|
Fixes [#2003](https://github.com/jasmine/jasmine/issues/2003).
|
||||||
|
|
||||||
|
Fixes [#1980](https://github.com/jasmine/jasmine/issues/1980).
|
||||||
|
|
||||||
|
## Supported environments
|
||||||
|
|
||||||
|
jasmine-core 5.1.0 has been tested in the following environments.
|
||||||
|
|
||||||
|
| Environment | Supported versions |
|
||||||
|
|-------------------|--------------------|
|
||||||
|
| Node | 18, 20 |
|
||||||
|
| Safari | 15-16 |
|
||||||
|
| Chrome | 114 |
|
||||||
|
| Firefox | 102, 113 |
|
||||||
|
| Edge | 113 |
|
||||||
|
|
||||||
|
|
||||||
|
------
|
||||||
|
|
||||||
|
_Release Notes generated with _[Anchorman](http://github.com/infews/anchorman)_
|
||||||
28
release_notes/5.1.1.md
Normal file
28
release_notes/5.1.1.md
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
# Jasmine Core 5.1.1 Release Notes
|
||||||
|
|
||||||
|
## Bug Fixes
|
||||||
|
|
||||||
|
* Fixed global variable leak in the main process when running in parallel mode
|
||||||
|
* Removed unnecessary throw when building expectation results
|
||||||
|
|
||||||
|
## Documentation Improvements
|
||||||
|
|
||||||
|
* Improved jsdocs for originalFn argument to createSpy
|
||||||
|
* Link to 5.0 upgrade guide in README, not 4.0
|
||||||
|
|
||||||
|
## Supported environments
|
||||||
|
|
||||||
|
jasmine-core 5.1.1 has been tested in the following environments.
|
||||||
|
|
||||||
|
| Environment | Supported versions |
|
||||||
|
|-------------------|--------------------|
|
||||||
|
| Node | 18, 20 |
|
||||||
|
| Safari | 15-16 |
|
||||||
|
| Chrome | 116 |
|
||||||
|
| Firefox | 102, 116 |
|
||||||
|
| Edge | 115 |
|
||||||
|
|
||||||
|
|
||||||
|
------
|
||||||
|
|
||||||
|
_Release Notes generated with _[Anchorman](http://github.com/infews/anchorman)_
|
||||||
27
release_notes/5.1.2.md
Normal file
27
release_notes/5.1.2.md
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
# Jasmine Core 5.1.2 Release Notes
|
||||||
|
|
||||||
|
## Bug Fixes
|
||||||
|
|
||||||
|
* Fixed `throwUnlessAsync`
|
||||||
|
* Fixes [#2026](https://github.com/jasmine/jasmine/issues/2026)
|
||||||
|
|
||||||
|
# Documentation improvements
|
||||||
|
|
||||||
|
* Added Safari 17 to supported browsers
|
||||||
|
* Added Firefox 115 (current ESR) to supported browsers
|
||||||
|
|
||||||
|
## Supported environments
|
||||||
|
|
||||||
|
This version has been tested in the following environments.
|
||||||
|
|
||||||
|
| Environment | Supported versions |
|
||||||
|
|-------------------|--------------------|
|
||||||
|
| Node | 18, 20 |
|
||||||
|
| Safari | 15-17 |
|
||||||
|
| Chrome | 121 |
|
||||||
|
| Firefox | 102, 115, 122 |
|
||||||
|
| Edge | 121 |
|
||||||
|
|
||||||
|
------
|
||||||
|
|
||||||
|
_Release Notes generated with _[Anchorman](http://github.com/infews/anchorman)_
|
||||||
35
release_notes/5.2.0.md
Normal file
35
release_notes/5.2.0.md
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
# Jasmine Core 5.2.0 Release Notes
|
||||||
|
|
||||||
|
## Bug Fixes
|
||||||
|
|
||||||
|
* Fixed stack trace filtering in FF when the developer tools are open
|
||||||
|
* Fixed handling of browser `error` events with message but no error
|
||||||
|
|
||||||
|
## New Features
|
||||||
|
|
||||||
|
* Improved the error message of the toHaveSize matcher.
|
||||||
|
* Merges [#2033](https://github.com/jasmine/jasmine/pull/2033) from @stephanreiter
|
||||||
|
* HTML reporter: show debug logs with white-space: pre
|
||||||
|
|
||||||
|
## Documentation improvements
|
||||||
|
|
||||||
|
* Improved discoverability of asymmetric equality testers
|
||||||
|
* Added an example for withContext()
|
||||||
|
* Clarified spyOnGlobalErrorsAsync API docs
|
||||||
|
* Added Node 22 to supported environments
|
||||||
|
|
||||||
|
## Supported environments
|
||||||
|
|
||||||
|
This version has been tested in the following environments.
|
||||||
|
|
||||||
|
| Environment | Supported versions |
|
||||||
|
|-------------------|--------------------|
|
||||||
|
| Node | 18, 20, 22 |
|
||||||
|
| Safari | 15-17 |
|
||||||
|
| Chrome | 126 |
|
||||||
|
| Firefox | 102, 115, 128 |
|
||||||
|
| Edge | 126 |
|
||||||
|
|
||||||
|
------
|
||||||
|
|
||||||
|
_Release Notes generated with _[Anchorman](http://github.com/infews/anchorman)_
|
||||||
@@ -3,6 +3,7 @@
|
|||||||
run_browser() {
|
run_browser() {
|
||||||
browser=$1
|
browser=$1
|
||||||
version=$2
|
version=$2
|
||||||
|
os="$3"
|
||||||
description="$browser $version"
|
description="$browser $version"
|
||||||
if [ $version = "latest" ]; then
|
if [ $version = "latest" ]; then
|
||||||
version=""
|
version=""
|
||||||
@@ -12,7 +13,7 @@ run_browser() {
|
|||||||
echo
|
echo
|
||||||
echo "Running $description"
|
echo "Running $description"
|
||||||
echo
|
echo
|
||||||
USE_SAUCE=true JASMINE_BROWSER=$browser SAUCE_BROWSER_VERSION=$version npm run ci
|
USE_SAUCE=true JASMINE_BROWSER=$browser SAUCE_BROWSER_VERSION=$version SAUCE_OS="$os" npm run ci
|
||||||
|
|
||||||
if [ $? -eq 0 ]; then
|
if [ $? -eq 0 ]; then
|
||||||
echo "PASS: $description" >> "$passfile"
|
echo "PASS: $description" >> "$passfile"
|
||||||
@@ -23,12 +24,23 @@ run_browser() {
|
|||||||
|
|
||||||
passfile=`mktemp -t jasmine-results.XXXXXX` || exit 1
|
passfile=`mktemp -t jasmine-results.XXXXXX` || exit 1
|
||||||
failfile=`mktemp -t jasmine-results.XXXXXX` || exit 1
|
failfile=`mktemp -t jasmine-results.XXXXXX` || exit 1
|
||||||
run_browser chrome latest
|
|
||||||
|
# As of 2023-09-30, Sauce Connect doesn't work with the latest Chrome version
|
||||||
|
# on the default Linux. Run on Mac OS instead. The OS specification may need to
|
||||||
|
# be updated or removed when new Chrome versions stop being available on Mac OS
|
||||||
|
# 12, although historically this has taken several major OS versions.
|
||||||
|
# See <https://saucelabs.com/products/supported-browsers-devices>.
|
||||||
|
# On Saucelabs, the test suite frequently runs ~30s slower on Mac OS than it
|
||||||
|
# does on Linux, so it's probably worth removing the OS specification once Sauce
|
||||||
|
# Connect works with Chrome latest on Linux again.
|
||||||
|
run_browser chrome latest "macOS 12"
|
||||||
|
|
||||||
run_browser firefox latest
|
run_browser firefox latest
|
||||||
|
run_browser firefox 115
|
||||||
run_browser firefox 102
|
run_browser firefox 102
|
||||||
run_browser firefox 91
|
run_browser safari 17
|
||||||
|
run_browser safari 16
|
||||||
run_browser safari 15
|
run_browser safari 15
|
||||||
run_browser safari 14
|
|
||||||
run_browser MicrosoftEdge latest
|
run_browser MicrosoftEdge latest
|
||||||
|
|
||||||
echo
|
echo
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ describe('AsyncExpectation', function() {
|
|||||||
|
|
||||||
it('converts a fail to a pass', function() {
|
it('converts a fail to a pass', function() {
|
||||||
const addExpectationResult = jasmine.createSpy('addExpectationResult'),
|
const addExpectationResult = jasmine.createSpy('addExpectationResult'),
|
||||||
actual = Promise.reject(),
|
actual = Promise.reject(new Error('nope')),
|
||||||
expectation = jasmineUnderTest.Expectation.asyncFactory({
|
expectation = jasmineUnderTest.Expectation.asyncFactory({
|
||||||
matchersUtil: new jasmineUnderTest.MatchersUtil({
|
matchersUtil: new jasmineUnderTest.MatchersUtil({
|
||||||
pp: function() {}
|
pp: function() {}
|
||||||
@@ -138,7 +138,7 @@ describe('AsyncExpectation', function() {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
addExpectationResult = jasmine.createSpy('addExpectationResult'),
|
addExpectationResult = jasmine.createSpy('addExpectationResult'),
|
||||||
actual = Promise.reject(),
|
actual = Promise.reject(new Error('nope')),
|
||||||
expectation = jasmineUnderTest.Expectation.asyncFactory({
|
expectation = jasmineUnderTest.Expectation.asyncFactory({
|
||||||
actual: actual,
|
actual: actual,
|
||||||
addExpectationResult: addExpectationResult,
|
addExpectationResult: addExpectationResult,
|
||||||
|
|||||||
@@ -9,160 +9,216 @@ describe('ClearStack', function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('uses setImmediate when available', function() {
|
describe('in Safari', function() {
|
||||||
const setImmediate = jasmine
|
usesQueueMicrotaskWithSetTimeout(function() {
|
||||||
.createSpy('setImmediate')
|
return {
|
||||||
.and.callFake(function(fn) {
|
navigator: {
|
||||||
fn();
|
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'
|
||||||
global = { setImmediate: setImmediate },
|
},
|
||||||
clearStack = jasmineUnderTest.getClearStack(global);
|
// queueMicrotask should be used even though MessageChannel is present
|
||||||
let called = false;
|
MessageChannel: fakeMessageChannel
|
||||||
|
};
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
clearStack(function() {
|
describe('in browsers other than Safari', function() {
|
||||||
called = true;
|
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);
|
describe('when MessageChannel is unavailable', function() {
|
||||||
expect(setImmediate).toHaveBeenCalled();
|
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() {
|
describe('in Node', function() {
|
||||||
const setImmediate = jasmine.createSpy('setImmediate'),
|
usesQueueMicrotaskWithoutSetTimeout(function() {
|
||||||
setTimeout = jasmine.createSpy('setTimeout'),
|
return {
|
||||||
global = { setImmediate: setImmediate, setTimeout: setTimeout },
|
process: {
|
||||||
clearStack = jasmineUnderTest.getClearStack(global);
|
versions: {
|
||||||
|
node: '3.1415927'
|
||||||
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();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
global = {
|
});
|
||||||
MessageChannel: function() {
|
});
|
||||||
return fakeChannel;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
clearStack = jasmineUnderTest.getClearStack(global);
|
|
||||||
let called = false;
|
|
||||||
|
|
||||||
clearStack(function() {
|
function usesMessageChannel(makeGlobal) {
|
||||||
called = true;
|
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 = fakeMessageChannel();
|
||||||
|
spyOn(fakeChannel.port2, 'postMessage');
|
||||||
it('uses setTimeout instead of MessageChannel every 10 calls to make sure we release the CPU', function() {
|
const setTimeout = jasmine.createSpy('setTimeout');
|
||||||
const fakeChannel = {
|
const global = {
|
||||||
port1: {},
|
...makeGlobal(),
|
||||||
port2: {
|
setTimeout,
|
||||||
postMessage: jasmine
|
|
||||||
.createSpy('postMessage')
|
|
||||||
.and.callFake(function() {
|
|
||||||
fakeChannel.port1.onmessage();
|
|
||||||
})
|
|
||||||
}
|
|
||||||
},
|
|
||||||
setTimeout = jasmine.createSpy('setTimeout'),
|
|
||||||
global = {
|
|
||||||
MessageChannel: function() {
|
MessageChannel: function() {
|
||||||
return fakeChannel;
|
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'),
|
const clearStack = jasmineUnderTest.getClearStack(global);
|
||||||
global = {
|
|
||||||
MessageChannel: function() {
|
|
||||||
return fakeChannel;
|
|
||||||
},
|
|
||||||
setTimeout: setTimeout
|
|
||||||
},
|
|
||||||
clearStack = jasmineUnderTest.getClearStack(global),
|
|
||||||
fn = jasmine.createSpy('second clearStack function');
|
|
||||||
|
|
||||||
clearStack(function() {
|
for (let i = 0; i < 9; i++) {
|
||||||
clearStack(fn);
|
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();
|
it('calls setTimeout when onmessage is called recursively', function() {
|
||||||
expect(setTimeout).toHaveBeenCalledWith(fn, 0);
|
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() {
|
clearStack(function() {
|
||||||
const setTimeout = jasmine
|
clearStack(fn);
|
||||||
.createSpy('setTimeout')
|
});
|
||||||
.and.callFake(function(fn) {
|
|
||||||
|
expect(fn).not.toHaveBeenCalled();
|
||||||
|
expect(setTimeout).toHaveBeenCalledWith(fn, 0);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function usesQueueMicrotaskWithSetTimeout(makeGlobal) {
|
||||||
|
it('uses queueMicrotask', function() {
|
||||||
|
const global = {
|
||||||
|
...makeGlobal(),
|
||||||
|
queueMicrotask: function(fn) {
|
||||||
fn();
|
fn();
|
||||||
}),
|
}
|
||||||
global = { setTimeout: setTimeout },
|
};
|
||||||
clearStack = jasmineUnderTest.getClearStack(global);
|
const clearStack = jasmineUnderTest.getClearStack(global);
|
||||||
let called = false;
|
let called = false;
|
||||||
|
|
||||||
clearStack(function() {
|
clearStack(function() {
|
||||||
called = true;
|
called = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(called).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(called).toBe(true);
|
it('uses setTimeout instead of queueMicrotask every 10 calls to make sure we release the CPU', function() {
|
||||||
expect(setTimeout).toHaveBeenCalledWith(jasmine.any(Function), 0);
|
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;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -80,6 +80,19 @@ describe('Env', function() {
|
|||||||
);
|
);
|
||||||
expect(suite.children[1].children[1].children[0].children).toBeFalsy();
|
expect(suite.children[1].children[1].children[0].children).toBeFalsy();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('throws if called in parallel mode', function() {
|
||||||
|
env.setParallelLoadingState('helpers');
|
||||||
|
check();
|
||||||
|
env.setParallelLoadingState('specs');
|
||||||
|
check();
|
||||||
|
|
||||||
|
function check() {
|
||||||
|
expect(function() {
|
||||||
|
env.topSuite();
|
||||||
|
}).toThrowError("'topSuite' is not available in parallel mode");
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('accepts its own current configureation', function() {
|
it('accepts its own current configureation', function() {
|
||||||
@@ -251,6 +264,15 @@ describe('Env', function() {
|
|||||||
|
|
||||||
describe('#fdescribe', function() {
|
describe('#fdescribe', function() {
|
||||||
behavesLikeDescribe('fdescribe');
|
behavesLikeDescribe('fdescribe');
|
||||||
|
|
||||||
|
it('throws an error in parallel mode', function() {
|
||||||
|
env.setParallelLoadingState('specs');
|
||||||
|
expect(function() {
|
||||||
|
env.fdescribe('a suite', function() {
|
||||||
|
env.it('a spec');
|
||||||
|
});
|
||||||
|
}).toThrowError("'fdescribe' is not available in parallel mode");
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('xdescribe', function() {
|
describe('xdescribe', function() {
|
||||||
@@ -372,6 +394,13 @@ describe('Env', function() {
|
|||||||
env.fit('huge timeout', function() {}, 2147483648);
|
env.fit('huge timeout', function() {}, 2147483648);
|
||||||
}).toThrowError('Timeout value cannot be greater than 2147483647');
|
}).toThrowError('Timeout value cannot be greater than 2147483647');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('throws an error in parallel mode', function() {
|
||||||
|
env.setParallelLoadingState('specs');
|
||||||
|
expect(function() {
|
||||||
|
env.fit('a spec', function() {});
|
||||||
|
}).toThrowError("'fit' is not available in parallel mode");
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('#beforeEach', function() {
|
describe('#beforeEach', function() {
|
||||||
@@ -394,6 +423,28 @@ describe('Env', function() {
|
|||||||
env.beforeEach(function() {}, 2147483648);
|
env.beforeEach(function() {}, 2147483648);
|
||||||
}).toThrowError('Timeout value cannot be greater than 2147483647');
|
}).toThrowError('Timeout value cannot be greater than 2147483647');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('throws when called at the top level in a spec file in parallel mode', function() {
|
||||||
|
env.setParallelLoadingState('specs');
|
||||||
|
expect(function() {
|
||||||
|
env.beforeEach(function() {});
|
||||||
|
}).toThrowError(
|
||||||
|
'In parallel mode, beforeEach must be in a describe block or in a helper file'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does not throw when called at the top level in a helper file in parallel mode', function() {
|
||||||
|
env.setParallelLoadingState('helpers');
|
||||||
|
env.beforeEach(function() {});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does not throw when called in a describe in a spec file in parallel mode', function() {
|
||||||
|
env.setParallelLoadingState('specs');
|
||||||
|
env.describe('a suite', function() {
|
||||||
|
env.beforeEach(function() {});
|
||||||
|
env.it('a spec');
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('#beforeAll', function() {
|
describe('#beforeAll', function() {
|
||||||
@@ -416,6 +467,47 @@ describe('Env', function() {
|
|||||||
env.beforeAll(function() {}, 2147483648);
|
env.beforeAll(function() {}, 2147483648);
|
||||||
}).toThrowError('Timeout value cannot be greater than 2147483647');
|
}).toThrowError('Timeout value cannot be greater than 2147483647');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('in parallel mode', function() {
|
||||||
|
it('throws an error when called at the top level', function() {
|
||||||
|
env.setParallelLoadingState('helpers');
|
||||||
|
check();
|
||||||
|
env.setParallelLoadingState('specs');
|
||||||
|
check();
|
||||||
|
|
||||||
|
function check() {
|
||||||
|
expect(function() {
|
||||||
|
env.beforeAll(function() {});
|
||||||
|
}).toThrowError(
|
||||||
|
"In parallel mode, 'beforeAll' must be in a describe block. " +
|
||||||
|
'Use the globalSetup config property for exactly-once setup in' +
|
||||||
|
' parallel mode.'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does not throw an error when called in a describe', function() {
|
||||||
|
env.setParallelLoadingState('helpers');
|
||||||
|
check();
|
||||||
|
env.setParallelLoadingState('specs');
|
||||||
|
check();
|
||||||
|
|
||||||
|
function check() {
|
||||||
|
let done = false;
|
||||||
|
|
||||||
|
env.describe('a suite', function() {
|
||||||
|
expect(function() {
|
||||||
|
env.it('a spec');
|
||||||
|
env.beforeAll(function() {});
|
||||||
|
}).not.toThrow();
|
||||||
|
|
||||||
|
done = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(done).toBeTrue();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('#afterEach', function() {
|
describe('#afterEach', function() {
|
||||||
@@ -438,6 +530,28 @@ describe('Env', function() {
|
|||||||
env.afterEach(function() {}, 2147483648);
|
env.afterEach(function() {}, 2147483648);
|
||||||
}).toThrowError('Timeout value cannot be greater than 2147483647');
|
}).toThrowError('Timeout value cannot be greater than 2147483647');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('throws when called at the top level in a spec file in parallel mode', function() {
|
||||||
|
env.setParallelLoadingState('specs');
|
||||||
|
expect(function() {
|
||||||
|
env.afterEach(function() {});
|
||||||
|
}).toThrowError(
|
||||||
|
'In parallel mode, afterEach must be in a describe block or in a helper file'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does not throw when called at the top level in a helper file in parallel mode', function() {
|
||||||
|
env.setParallelLoadingState('helpers');
|
||||||
|
env.afterEach(function() {});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does not throw when called in a describe in a spec file in parallel mode', function() {
|
||||||
|
env.setParallelLoadingState('specs');
|
||||||
|
env.describe('a suite', function() {
|
||||||
|
env.afterEach(function() {});
|
||||||
|
env.it('a spec');
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('#afterAll', function() {
|
describe('#afterAll', function() {
|
||||||
@@ -460,6 +574,47 @@ describe('Env', function() {
|
|||||||
env.afterAll(function() {}, 2147483648);
|
env.afterAll(function() {}, 2147483648);
|
||||||
}).toThrowError('Timeout value cannot be greater than 2147483647');
|
}).toThrowError('Timeout value cannot be greater than 2147483647');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('in parallel mode', function() {
|
||||||
|
it('throws an error when called at the top level', function() {
|
||||||
|
env.setParallelLoadingState('helpers');
|
||||||
|
check();
|
||||||
|
env.setParallelLoadingState('specs');
|
||||||
|
check();
|
||||||
|
|
||||||
|
function check() {
|
||||||
|
expect(function() {
|
||||||
|
env.afterAll(function() {});
|
||||||
|
}).toThrowError(
|
||||||
|
"In parallel mode, 'afterAll' must be in a describe block. " +
|
||||||
|
'Use the globalTeardown config property for exactly-once ' +
|
||||||
|
'teardown in parallel mode.'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does not throw an error when called in a describe', function() {
|
||||||
|
env.setParallelLoadingState('helpers');
|
||||||
|
check();
|
||||||
|
env.setParallelLoadingState('specs');
|
||||||
|
check();
|
||||||
|
|
||||||
|
function check() {
|
||||||
|
let done = false;
|
||||||
|
|
||||||
|
env.describe('a suite', function() {
|
||||||
|
expect(function() {
|
||||||
|
env.it('a spec');
|
||||||
|
env.afterAll(function() {});
|
||||||
|
}).not.toThrow();
|
||||||
|
|
||||||
|
done = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(done).toBeTrue();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('when not constructed with suppressLoadErrors: true', function() {
|
describe('when not constructed with suppressLoadErrors: true', function() {
|
||||||
@@ -592,6 +747,32 @@ describe('Env', function() {
|
|||||||
expect(id).toEqual(env.topSuite().id);
|
expect(id).toEqual(env.topSuite().id);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should not reset the topSuite if parallelReset was called since the last run', async function() {
|
||||||
|
await env.execute();
|
||||||
|
env.parallelReset();
|
||||||
|
spyOn(jasmineUnderTest.Suite.prototype, 'reset');
|
||||||
|
await env.execute();
|
||||||
|
expect(jasmineUnderTest.Suite.prototype.reset).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('In parallel mode', function() {
|
||||||
|
it('rejects if random is set to false', async function() {
|
||||||
|
env.configure({ random: false });
|
||||||
|
env.setParallelLoadingState('specs');
|
||||||
|
await expectAsync(env.execute()).toBeRejectedWithError(
|
||||||
|
'Randomization cannot be disabled in parallel mode'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('rejects if seed is set', async function() {
|
||||||
|
env.configure({ seed: 1234 });
|
||||||
|
env.setParallelLoadingState('specs');
|
||||||
|
await expectAsync(env.execute()).toBeRejectedWithError(
|
||||||
|
'Random seed cannot be set in parallel mode'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('#spyOnGlobalErrorsAsync', function() {
|
describe('#spyOnGlobalErrorsAsync', function() {
|
||||||
@@ -608,4 +789,46 @@ describe('Env', function() {
|
|||||||
).toBeRejectedWithError(msg);
|
).toBeRejectedWithError(msg);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('#addReporter', function() {
|
||||||
|
it('throws when called in parallel mode', function() {
|
||||||
|
env.setParallelLoadingState('helpers');
|
||||||
|
expect(function() {
|
||||||
|
env.addReporter({});
|
||||||
|
}).toThrowError('Reporters cannot be added via Env in parallel mode');
|
||||||
|
|
||||||
|
env.setParallelLoadingState('specs');
|
||||||
|
expect(function() {
|
||||||
|
env.addReporter({});
|
||||||
|
}).toThrowError('Reporters cannot be added via Env in parallel mode');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('#clearReporters', function() {
|
||||||
|
it('throws when called in parallel mode', function() {
|
||||||
|
env.setParallelLoadingState('helpers');
|
||||||
|
expect(function() {
|
||||||
|
env.clearReporters();
|
||||||
|
}).toThrowError('Reporters cannot be removed via Env in parallel mode');
|
||||||
|
|
||||||
|
env.setParallelLoadingState('specs');
|
||||||
|
expect(function() {
|
||||||
|
env.clearReporters();
|
||||||
|
}).toThrowError('Reporters cannot be removed via Env in parallel mode');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('#configure', function() {
|
||||||
|
it('throws when called in parallel mode', function() {
|
||||||
|
env.setParallelLoadingState('helpers');
|
||||||
|
expect(function() {
|
||||||
|
env.configure({});
|
||||||
|
}).toThrowError('Jasmine cannot be configured via Env in parallel mode');
|
||||||
|
|
||||||
|
env.setParallelLoadingState('specs');
|
||||||
|
expect(function() {
|
||||||
|
env.configure({});
|
||||||
|
}).toThrowError('Jasmine cannot be configured via Env in parallel mode');
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -153,12 +153,33 @@ describe('ExceptionFormatter', function() {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('filters Jasmine stack frames with Firefox async annotations', function() {
|
||||||
|
const error = {
|
||||||
|
stack:
|
||||||
|
'http://localhost:8888/__spec__/core/UtilSpec.js:115:28\n' +
|
||||||
|
'promise callback*fn1@http://localhost:8888/__jasmine__/jasmine.js:4320:27\n' +
|
||||||
|
'setTimeout handler*fn2@http://localhost:8888/__jasmine__/jasmine.js:4320:27\n' +
|
||||||
|
'http://localhost:8888/__spec__/core/UtilSpec.js:115:28'
|
||||||
|
};
|
||||||
|
const subject = new jasmineUnderTest.ExceptionFormatter({
|
||||||
|
jasmineFile: 'http://localhost:8888/__jasmine__/jasmine.js'
|
||||||
|
});
|
||||||
|
const result = subject.stack(error);
|
||||||
|
expect(result).toEqual(
|
||||||
|
'http://localhost:8888/__spec__/core/UtilSpec.js:115:28\n' +
|
||||||
|
'<Jasmine>\n' +
|
||||||
|
'http://localhost:8888/__spec__/core/UtilSpec.js:115:28'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
it('filters Jasmine stack frames in this environment', function() {
|
it('filters Jasmine stack frames in this environment', function() {
|
||||||
const error = new Error('an error');
|
const error = new Error('an error');
|
||||||
const subject = new jasmineUnderTest.ExceptionFormatter({
|
const subject = new jasmineUnderTest.ExceptionFormatter({
|
||||||
jasmineFile: jasmine.util.jasmineFile()
|
jasmineFile: jasmine.util.jasmineFile()
|
||||||
});
|
});
|
||||||
const result = subject.stack(error);
|
const result = subject.stack(error);
|
||||||
|
jasmine.debugLog('Original stack trace: ' + error.stack);
|
||||||
|
jasmine.debugLog('Filtered stack trace: ' + result);
|
||||||
const lines = result.split('\n');
|
const lines = result.split('\n');
|
||||||
|
|
||||||
if (lines[0].match(/an error/)) {
|
if (lines[0].match(/an error/)) {
|
||||||
@@ -197,7 +218,7 @@ describe('ExceptionFormatter', function() {
|
|||||||
expect(new jasmineUnderTest.ExceptionFormatter().stack()).toBeNull();
|
expect(new jasmineUnderTest.ExceptionFormatter().stack()).toBeNull();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('includes error properties in stack', function() {
|
it("includes the error's own properties in stack", function() {
|
||||||
const error = new Error('an error');
|
const error = new Error('an error');
|
||||||
error.someProperty = 'hello there';
|
error.someProperty = 'hello there';
|
||||||
|
|
||||||
@@ -206,6 +227,19 @@ describe('ExceptionFormatter', function() {
|
|||||||
expect(result).toMatch(/error properties:.*someProperty.*hello there/);
|
expect(result).toMatch(/error properties:.*someProperty.*hello there/);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('does not include inherited error properties', function() {
|
||||||
|
function CustomError(msg) {
|
||||||
|
Error.call(this, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
CustomError.prototype = new Error();
|
||||||
|
CustomError.prototype.anInheritedProp = 'something';
|
||||||
|
const error = new CustomError('nope');
|
||||||
|
|
||||||
|
const result = new jasmineUnderTest.ExceptionFormatter().stack(error);
|
||||||
|
expect(result).not.toContain('anInheritedProp');
|
||||||
|
});
|
||||||
|
|
||||||
describe('When omitMessage is true', function() {
|
describe('When omitMessage is true', function() {
|
||||||
it('filters the message from V8-style stack traces', function() {
|
it('filters the message from V8-style stack traces', function() {
|
||||||
const error = {
|
const error = {
|
||||||
@@ -256,5 +290,61 @@ describe('ExceptionFormatter', function() {
|
|||||||
expect(result).not.toContain('an error');
|
expect(result).not.toContain('an error');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('when the error has a cause property', function() {
|
||||||
|
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');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does not throw if cause is a non Error', function() {
|
||||||
|
const formatter = new jasmineUnderTest.ExceptionFormatter();
|
||||||
|
|
||||||
|
expect(function() {
|
||||||
|
formatter.stack(
|
||||||
|
new Error('error', {
|
||||||
|
cause: function() {}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}).not.toThrowError();
|
||||||
|
|
||||||
|
expect(function() {
|
||||||
|
formatter.stack(
|
||||||
|
new Error('error', {
|
||||||
|
cause: 'another error'
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}).not.toThrowError();
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,75 +1,65 @@
|
|||||||
describe('GlobalErrors', function() {
|
describe('GlobalErrors', function() {
|
||||||
it('calls the added handler on error', function() {
|
it('calls the added handler on error', function() {
|
||||||
const fakeGlobal = { onerror: null },
|
const fakeGlobal = browserGlobal();
|
||||||
handler = jasmine.createSpy('errorHandler'),
|
const handler = jasmine.createSpy('errorHandler');
|
||||||
errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
|
const errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
|
||||||
|
|
||||||
errors.install();
|
errors.install();
|
||||||
errors.pushListener(handler);
|
errors.pushListener(handler);
|
||||||
|
|
||||||
fakeGlobal.onerror('foo');
|
const error = new Error('nope');
|
||||||
|
dispatchErrorEvent(fakeGlobal, { error });
|
||||||
expect(handler).toHaveBeenCalledWith('foo');
|
|
||||||
});
|
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
errors.install();
|
|
||||||
errors.pushListener(handler);
|
|
||||||
|
|
||||||
fakeGlobal.onerror = hijackHandler;
|
|
||||||
|
|
||||||
fakeGlobal.onerror('foo');
|
|
||||||
|
|
||||||
expect(hijackHandler).toHaveBeenCalledWith('foo');
|
|
||||||
expect(handler).not.toHaveBeenCalled();
|
|
||||||
});
|
|
||||||
|
|
||||||
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');
|
|
||||||
|
|
||||||
errors.install();
|
|
||||||
errors.pushListener(handler);
|
|
||||||
|
|
||||||
fakeGlobal.onerror(fooError.message, 'foo.js', 1, 1, fooError);
|
|
||||||
|
|
||||||
expect(handler).toHaveBeenCalledWith(
|
expect(handler).toHaveBeenCalledWith(
|
||||||
fooError.message,
|
jasmine.is(error),
|
||||||
'foo.js',
|
jasmine.objectContaining({ error: jasmine.is(error) })
|
||||||
1,
|
);
|
||||||
1,
|
});
|
||||||
fooError
|
|
||||||
|
it('is not affected by overriding global.onerror', function() {
|
||||||
|
const fakeGlobal = browserGlobal();
|
||||||
|
const handler = jasmine.createSpy('errorHandler');
|
||||||
|
const errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
|
||||||
|
|
||||||
|
errors.install();
|
||||||
|
errors.pushListener(handler);
|
||||||
|
|
||||||
|
fakeGlobal.onerror = () => {};
|
||||||
|
|
||||||
|
const error = new Error('nope');
|
||||||
|
dispatchErrorEvent(fakeGlobal, { error });
|
||||||
|
|
||||||
|
expect(handler).toHaveBeenCalledWith(
|
||||||
|
jasmine.is(error),
|
||||||
|
jasmine.objectContaining({ error: jasmine.is(error) })
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('only calls the most recent handler', function() {
|
it('only calls the most recent handler', function() {
|
||||||
const fakeGlobal = { onerror: null },
|
const fakeGlobal = browserGlobal();
|
||||||
handler1 = jasmine.createSpy('errorHandler1'),
|
const handler1 = jasmine.createSpy('errorHandler1');
|
||||||
handler2 = jasmine.createSpy('errorHandler2'),
|
const handler2 = jasmine.createSpy('errorHandler2');
|
||||||
errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
|
const errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
|
||||||
|
|
||||||
errors.install();
|
errors.install();
|
||||||
errors.pushListener(handler1);
|
errors.pushListener(handler1);
|
||||||
errors.pushListener(handler2);
|
errors.pushListener(handler2);
|
||||||
|
|
||||||
fakeGlobal.onerror('foo');
|
const error = new Error('nope');
|
||||||
|
dispatchErrorEvent(fakeGlobal, { error });
|
||||||
|
|
||||||
expect(handler1).not.toHaveBeenCalled();
|
expect(handler1).not.toHaveBeenCalled();
|
||||||
expect(handler2).toHaveBeenCalledWith('foo');
|
expect(handler2).toHaveBeenCalledWith(
|
||||||
|
jasmine.is(error),
|
||||||
|
jasmine.objectContaining({ error: jasmine.is(error) })
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('calls previous handlers when one is removed', function() {
|
it('calls previous handlers when one is removed', function() {
|
||||||
const fakeGlobal = { onerror: null },
|
const fakeGlobal = browserGlobal();
|
||||||
handler1 = jasmine.createSpy('errorHandler1'),
|
const handler1 = jasmine.createSpy('errorHandler1');
|
||||||
handler2 = jasmine.createSpy('errorHandler2'),
|
const handler2 = jasmine.createSpy('errorHandler2');
|
||||||
errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
|
const errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
|
||||||
|
|
||||||
errors.install();
|
errors.install();
|
||||||
errors.pushListener(handler1);
|
errors.pushListener(handler1);
|
||||||
@@ -77,9 +67,13 @@ describe('GlobalErrors', function() {
|
|||||||
|
|
||||||
errors.popListener(handler2);
|
errors.popListener(handler2);
|
||||||
|
|
||||||
fakeGlobal.onerror('foo');
|
const error = new Error('nope');
|
||||||
|
dispatchErrorEvent(fakeGlobal, { error });
|
||||||
|
|
||||||
expect(handler1).toHaveBeenCalledWith('foo');
|
expect(handler1).toHaveBeenCalledWith(
|
||||||
|
jasmine.is(error),
|
||||||
|
jasmine.objectContaining({ error: jasmine.is(error) })
|
||||||
|
);
|
||||||
expect(handler2).not.toHaveBeenCalled();
|
expect(handler2).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -90,31 +84,27 @@ describe('GlobalErrors', function() {
|
|||||||
}).toThrowError('popListener expects a listener');
|
}).toThrowError('popListener expects a listener');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('uninstalls itself, putting back a previous callback', function() {
|
it('uninstalls itself', function() {
|
||||||
const originalCallback = jasmine.createSpy('error'),
|
const fakeGlobal = browserGlobal();
|
||||||
fakeGlobal = { onerror: originalCallback },
|
const errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
|
||||||
errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
|
function unrelatedListener() {}
|
||||||
|
|
||||||
expect(fakeGlobal.onerror).toBe(originalCallback);
|
|
||||||
|
|
||||||
errors.install();
|
errors.install();
|
||||||
|
fakeGlobal.addEventListener('error', unrelatedListener);
|
||||||
expect(fakeGlobal.onerror).not.toBe(originalCallback);
|
|
||||||
|
|
||||||
errors.uninstall();
|
errors.uninstall();
|
||||||
|
|
||||||
expect(fakeGlobal.onerror).toBe(originalCallback);
|
expect(fakeGlobal.listeners_.error).toEqual([unrelatedListener]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('rethrows the original error when there is no handler', function() {
|
it('rethrows the original error when there is no handler', function() {
|
||||||
const fakeGlobal = {},
|
const fakeGlobal = browserGlobal();
|
||||||
errors = new jasmineUnderTest.GlobalErrors(fakeGlobal),
|
const errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
|
||||||
originalError = new Error('nope');
|
const originalError = new Error('nope');
|
||||||
|
|
||||||
errors.install();
|
errors.install();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
fakeGlobal.onerror(originalError);
|
dispatchErrorEvent(fakeGlobal, { error: originalError });
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
expect(e).toBe(originalError);
|
expect(e).toBe(originalError);
|
||||||
}
|
}
|
||||||
@@ -286,128 +276,117 @@ describe('GlobalErrors', function() {
|
|||||||
|
|
||||||
describe('Reporting unhandled promise rejections in the browser', function() {
|
describe('Reporting unhandled promise rejections in the browser', function() {
|
||||||
it('subscribes and unsubscribes from the unhandledrejection event', function() {
|
it('subscribes and unsubscribes from the unhandledrejection event', function() {
|
||||||
const fakeGlobal = jasmine.createSpyObj('globalErrors', [
|
const fakeGlobal = browserGlobal();
|
||||||
'addEventListener',
|
const errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
|
||||||
'removeEventListener',
|
|
||||||
'onerror'
|
|
||||||
]),
|
|
||||||
errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
|
|
||||||
|
|
||||||
errors.install();
|
errors.install();
|
||||||
expect(fakeGlobal.addEventListener).toHaveBeenCalledWith(
|
expect(fakeGlobal.listeners_.unhandledrejection).toEqual([
|
||||||
'unhandledrejection',
|
|
||||||
jasmine.any(Function)
|
jasmine.any(Function)
|
||||||
);
|
]);
|
||||||
|
|
||||||
const addedListener = fakeGlobal.addEventListener.calls.argsFor(0)[1];
|
|
||||||
errors.uninstall();
|
errors.uninstall();
|
||||||
|
expect(fakeGlobal.listeners_.unhandledrejection).toEqual([]);
|
||||||
expect(fakeGlobal.removeEventListener).toHaveBeenCalledWith(
|
|
||||||
'unhandledrejection',
|
|
||||||
addedListener
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('reports rejections whose reason is a string', function() {
|
it('reports rejections whose reason is a string', function() {
|
||||||
const fakeGlobal = jasmine.createSpyObj('globalErrors', [
|
const fakeGlobal = browserGlobal();
|
||||||
'addEventListener',
|
const handler = jasmine.createSpy('errorHandler');
|
||||||
'removeEventListener',
|
const errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
|
||||||
'onerror'
|
|
||||||
]),
|
|
||||||
handler = jasmine.createSpy('errorHandler'),
|
|
||||||
errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
|
|
||||||
|
|
||||||
errors.install();
|
errors.install();
|
||||||
errors.pushListener(handler);
|
errors.pushListener(handler);
|
||||||
|
|
||||||
const addedListener = fakeGlobal.addEventListener.calls.argsFor(0)[1];
|
const event = { reason: 'nope' };
|
||||||
addedListener({ reason: 'nope' });
|
dispatchUnhandledRejectionEvent(fakeGlobal, event);
|
||||||
|
|
||||||
expect(handler).toHaveBeenCalledWith('Unhandled promise rejection: nope');
|
expect(handler).toHaveBeenCalledWith(
|
||||||
|
'Unhandled promise rejection: nope',
|
||||||
|
event
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('reports rejections whose reason is an Error', function() {
|
it('reports rejections whose reason is an Error', function() {
|
||||||
const fakeGlobal = jasmine.createSpyObj('globalErrors', [
|
const fakeGlobal = browserGlobal();
|
||||||
'addEventListener',
|
const handler = jasmine.createSpy('errorHandler');
|
||||||
'removeEventListener',
|
const errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
|
||||||
'onerror'
|
|
||||||
]),
|
|
||||||
handler = jasmine.createSpy('errorHandler'),
|
|
||||||
errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
|
|
||||||
|
|
||||||
errors.install();
|
errors.install();
|
||||||
errors.pushListener(handler);
|
errors.pushListener(handler);
|
||||||
|
|
||||||
const addedListener = fakeGlobal.addEventListener.calls.argsFor(0)[1];
|
|
||||||
const reason = new Error('bar');
|
const reason = new Error('bar');
|
||||||
|
const event = { reason };
|
||||||
addedListener({ reason: reason });
|
dispatchUnhandledRejectionEvent(fakeGlobal, event);
|
||||||
|
|
||||||
expect(handler).toHaveBeenCalledWith(
|
expect(handler).toHaveBeenCalledWith(
|
||||||
jasmine.objectContaining({
|
jasmine.objectContaining({
|
||||||
jasmineMessage: 'Unhandled promise rejection: Error: bar',
|
jasmineMessage: 'Unhandled promise rejection: Error: bar',
|
||||||
message: reason.message,
|
message: reason.message,
|
||||||
stack: reason.stack
|
stack: reason.stack
|
||||||
})
|
}),
|
||||||
|
event
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Reporting uncaught exceptions in node.js', function() {
|
||||||
|
it('prepends a descriptive message when the error is not an `Error`', function() {
|
||||||
|
const fakeGlobal = {
|
||||||
|
process: {
|
||||||
|
on: jasmine.createSpy('process.on'),
|
||||||
|
removeListener: function() {},
|
||||||
|
listeners: function() {
|
||||||
|
return [];
|
||||||
|
},
|
||||||
|
removeAllListeners: function() {}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const handler = jasmine.createSpy('errorHandler');
|
||||||
|
const errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
|
||||||
|
|
||||||
|
errors.install();
|
||||||
|
errors.pushListener(handler);
|
||||||
|
|
||||||
|
uncaughtExceptionListener(fakeGlobal)(17);
|
||||||
|
|
||||||
|
expect(handler).toHaveBeenCalledWith(new Error('Uncaught exception: 17'));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('substitutes a descriptive message when the error is falsy', function() {
|
||||||
|
const fakeGlobal = {
|
||||||
|
process: {
|
||||||
|
on: jasmine.createSpy('process.on'),
|
||||||
|
removeListener: function() {},
|
||||||
|
listeners: function() {
|
||||||
|
return [];
|
||||||
|
},
|
||||||
|
removeAllListeners: function() {}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const handler = jasmine.createSpy('errorHandler');
|
||||||
|
const errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
|
||||||
|
|
||||||
|
errors.install();
|
||||||
|
errors.pushListener(handler);
|
||||||
|
|
||||||
|
uncaughtExceptionListener(fakeGlobal)();
|
||||||
|
|
||||||
|
expect(handler).toHaveBeenCalledWith(
|
||||||
|
new Error('Uncaught exception with no error or message')
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Enabling external interception of reported rejections by overriding global.onerror', function() {
|
function uncaughtExceptionListener(global) {
|
||||||
it('overriding global.onerror intercepts rejections whose reason is a string', function() {
|
// Grab the right listener
|
||||||
const fakeGlobal = jasmine.createSpyObj('globalErrors', [
|
expect(global.process.on.calls.argsFor(0)[0]).toEqual(
|
||||||
'addEventListener'
|
'uncaughtException'
|
||||||
]),
|
);
|
||||||
handler = jasmine.createSpy('errorHandler'),
|
return global.process.on.calls.argsFor(0)[1];
|
||||||
hijackHandler = jasmine.createSpy('hijackErrorHandler'),
|
}
|
||||||
errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
|
|
||||||
|
|
||||||
errors.install();
|
|
||||||
errors.pushListener(handler);
|
|
||||||
|
|
||||||
fakeGlobal.onerror = hijackHandler;
|
|
||||||
|
|
||||||
const addedListener = fakeGlobal.addEventListener.calls.argsFor(0)[1];
|
|
||||||
addedListener({ reason: 'nope' });
|
|
||||||
|
|
||||||
expect(hijackHandler).toHaveBeenCalledWith(
|
|
||||||
'Unhandled promise rejection: nope'
|
|
||||||
);
|
|
||||||
expect(handler).not.toHaveBeenCalled();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('overriding global.onerror intercepts rejections whose reason is an Error', function() {
|
|
||||||
const fakeGlobal = jasmine.createSpyObj('globalErrors', [
|
|
||||||
'addEventListener'
|
|
||||||
]),
|
|
||||||
handler = jasmine.createSpy('errorHandler'),
|
|
||||||
hijackHandler = jasmine.createSpy('hijackErrorHandler'),
|
|
||||||
errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
|
|
||||||
|
|
||||||
errors.install();
|
|
||||||
errors.pushListener(handler);
|
|
||||||
|
|
||||||
fakeGlobal.onerror = hijackHandler;
|
|
||||||
|
|
||||||
const addedListener = fakeGlobal.addEventListener.calls.argsFor(0)[1];
|
|
||||||
const reason = new Error('bar');
|
|
||||||
|
|
||||||
addedListener({ reason: reason });
|
|
||||||
|
|
||||||
expect(hijackHandler).toHaveBeenCalledWith(
|
|
||||||
jasmine.objectContaining({
|
|
||||||
jasmineMessage: 'Unhandled promise rejection: Error: bar',
|
|
||||||
message: reason.message,
|
|
||||||
stack: reason.stack
|
|
||||||
})
|
|
||||||
);
|
|
||||||
expect(handler).not.toHaveBeenCalled();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('#setOverrideListener', function() {
|
describe('#setOverrideListener', function() {
|
||||||
it('overrides the existing handlers in browsers until removed', function() {
|
it('overrides the existing handlers in browsers until removed', function() {
|
||||||
const fakeGlobal = { onerror: null };
|
const fakeGlobal = browserGlobal();
|
||||||
const handler0 = jasmine.createSpy('handler0');
|
const handler0 = jasmine.createSpy('handler0');
|
||||||
const handler1 = jasmine.createSpy('handler1');
|
const handler1 = jasmine.createSpy('handler1');
|
||||||
const overrideHandler = jasmine.createSpy('overrideHandler');
|
const overrideHandler = jasmine.createSpy('overrideHandler');
|
||||||
@@ -417,19 +396,18 @@ describe('GlobalErrors', function() {
|
|||||||
errors.pushListener(handler0);
|
errors.pushListener(handler0);
|
||||||
errors.setOverrideListener(overrideHandler, () => {});
|
errors.setOverrideListener(overrideHandler, () => {});
|
||||||
errors.pushListener(handler1);
|
errors.pushListener(handler1);
|
||||||
fakeGlobal.onerror('foo');
|
dispatchErrorEvent(fakeGlobal, { error: 'foo' });
|
||||||
fakeGlobal.onerror(null, null, null, null, new Error('bar'));
|
|
||||||
|
|
||||||
expect(overrideHandler).toHaveBeenCalledWith('foo');
|
expect(overrideHandler).toHaveBeenCalledWith('foo');
|
||||||
expect(overrideHandler).toHaveBeenCalledWith(new Error('bar'));
|
|
||||||
expect(handler0).not.toHaveBeenCalled();
|
expect(handler0).not.toHaveBeenCalled();
|
||||||
expect(handler1).not.toHaveBeenCalled();
|
expect(handler1).not.toHaveBeenCalled();
|
||||||
|
|
||||||
errors.removeOverrideListener();
|
errors.removeOverrideListener();
|
||||||
|
|
||||||
fakeGlobal.onerror('baz');
|
const event = { error: 'baz' };
|
||||||
|
dispatchErrorEvent(fakeGlobal, event);
|
||||||
expect(overrideHandler).not.toHaveBeenCalledWith('baz');
|
expect(overrideHandler).not.toHaveBeenCalledWith('baz');
|
||||||
expect(handler1).toHaveBeenCalledWith('baz');
|
expect(handler1).toHaveBeenCalledWith('baz', event);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('overrides the existing handlers in Node until removed', function() {
|
it('overrides the existing handlers in Node until removed', function() {
|
||||||
@@ -529,8 +507,7 @@ describe('GlobalErrors', function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('throws if there is already an override handler', function() {
|
it('throws if there is already an override handler', function() {
|
||||||
const fakeGlobal = { onerror: null };
|
const errors = new jasmineUnderTest.GlobalErrors(browserGlobal());
|
||||||
const errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
|
|
||||||
|
|
||||||
errors.setOverrideListener(() => {}, () => {});
|
errors.setOverrideListener(() => {}, () => {});
|
||||||
expect(function() {
|
expect(function() {
|
||||||
@@ -541,9 +518,8 @@ describe('GlobalErrors', function() {
|
|||||||
|
|
||||||
describe('#removeOverrideListener', function() {
|
describe('#removeOverrideListener', function() {
|
||||||
it("calls the handler's onRemove callback", function() {
|
it("calls the handler's onRemove callback", function() {
|
||||||
const fakeGlobal = { onerror: null };
|
|
||||||
const onRemove = jasmine.createSpy('onRemove');
|
const onRemove = jasmine.createSpy('onRemove');
|
||||||
const errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
|
const errors = new jasmineUnderTest.GlobalErrors(browserGlobal());
|
||||||
|
|
||||||
errors.setOverrideListener(() => {}, onRemove);
|
errors.setOverrideListener(() => {}, onRemove);
|
||||||
errors.removeOverrideListener();
|
errors.removeOverrideListener();
|
||||||
@@ -552,10 +528,43 @@ describe('GlobalErrors', function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('does not throw if there is no handler', function() {
|
it('does not throw if there is no handler', function() {
|
||||||
const fakeGlobal = { onerror: null };
|
const errors = new jasmineUnderTest.GlobalErrors(browserGlobal());
|
||||||
const errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
|
|
||||||
|
|
||||||
expect(() => errors.removeOverrideListener()).not.toThrow();
|
expect(() => errors.removeOverrideListener()).not.toThrow();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function browserGlobal() {
|
||||||
|
return {
|
||||||
|
listeners_: { error: [], unhandledrejection: [] },
|
||||||
|
addEventListener(eventName, listener) {
|
||||||
|
this.listeners_[eventName].push(listener);
|
||||||
|
},
|
||||||
|
removeEventListener(eventName, listener) {
|
||||||
|
this.listeners_[eventName] = this.listeners_[eventName].filter(
|
||||||
|
l => l !== listener
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function dispatchErrorEvent(global, event) {
|
||||||
|
expect(global.listeners_.error.length)
|
||||||
|
.withContext('number of error listeners')
|
||||||
|
.toBeGreaterThan(0);
|
||||||
|
|
||||||
|
for (const l of global.listeners_.error) {
|
||||||
|
l(event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function dispatchUnhandledRejectionEvent(global, event) {
|
||||||
|
expect(global.listeners_.unhandledrejection.length)
|
||||||
|
.withContext('number of unhandledrejection listeners')
|
||||||
|
.toBeGreaterThan(0);
|
||||||
|
|
||||||
|
for (const l of global.listeners_.unhandledrejection) {
|
||||||
|
l(event);
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
176
spec/core/ParallelReportDispatcherSpec.js
Normal file
176
spec/core/ParallelReportDispatcherSpec.js
Normal file
@@ -0,0 +1,176 @@
|
|||||||
|
describe('ParallelReportDispatcher', function() {
|
||||||
|
it('dispatches the standard reporter events', async function() {
|
||||||
|
const subject = new jasmineUnderTest.ParallelReportDispatcher(() => {}, {
|
||||||
|
globalErrors: mockGlobalErrors()
|
||||||
|
});
|
||||||
|
const events = [
|
||||||
|
'jasmineStarted',
|
||||||
|
'jasmineDone',
|
||||||
|
'suiteStarted',
|
||||||
|
'suiteDone',
|
||||||
|
'specStarted',
|
||||||
|
'specDone'
|
||||||
|
];
|
||||||
|
const reporter = jasmine.createSpyObj('reporter', events);
|
||||||
|
subject.addReporter(reporter);
|
||||||
|
|
||||||
|
for (const eventName of events) {
|
||||||
|
const payload = { payloadFor: eventName };
|
||||||
|
await subject[eventName](payload);
|
||||||
|
expect(reporter[eventName]).toHaveBeenCalledWith(payload);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('installs and uninstalls the global error handler', function() {
|
||||||
|
const globalErrors = mockGlobalErrors();
|
||||||
|
const subject = new jasmineUnderTest.ParallelReportDispatcher(() => {}, {
|
||||||
|
globalErrors
|
||||||
|
});
|
||||||
|
|
||||||
|
subject.installGlobalErrors();
|
||||||
|
expect(globalErrors.install).toHaveBeenCalled();
|
||||||
|
|
||||||
|
subject.uninstallGlobalErrors();
|
||||||
|
expect(globalErrors.uninstall).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('handles global errors from async reporters', async function() {
|
||||||
|
const globalErrors = mockGlobalErrors();
|
||||||
|
const onError = jasmine.createSpy('onError');
|
||||||
|
const subject = new jasmineUnderTest.ParallelReportDispatcher(onError, {
|
||||||
|
globalErrors
|
||||||
|
});
|
||||||
|
const reporter = jasmine.createSpyObj('reporter', [
|
||||||
|
'jasmineStarted',
|
||||||
|
'jasmineDone'
|
||||||
|
]);
|
||||||
|
let resolveStarted;
|
||||||
|
reporter.jasmineStarted.and.callFake(function() {
|
||||||
|
return new Promise(function(res) {
|
||||||
|
resolveStarted = res;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
subject.addReporter(reporter);
|
||||||
|
|
||||||
|
const promise = subject.jasmineStarted({});
|
||||||
|
expect(globalErrors.pushListener).toHaveBeenCalled();
|
||||||
|
expect(globalErrors.popListener).not.toHaveBeenCalled();
|
||||||
|
const error = new Error('nope');
|
||||||
|
globalErrors.pushListener.calls.argsFor(0)[0](error);
|
||||||
|
expect(onError).toHaveBeenCalledWith(error);
|
||||||
|
|
||||||
|
resolveStarted();
|
||||||
|
await promise;
|
||||||
|
expect(globalErrors.popListener).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('handles done(error) from callback-style async reporters', function() {
|
||||||
|
const globalErrors = mockGlobalErrors();
|
||||||
|
const onError = jasmine.createSpy('onError');
|
||||||
|
const subject = new jasmineUnderTest.ParallelReportDispatcher(onError, {
|
||||||
|
globalErrors
|
||||||
|
});
|
||||||
|
const reporter = jasmine.createSpyObj('reporter', [
|
||||||
|
'jasmineStarted',
|
||||||
|
'jasmineDone'
|
||||||
|
]);
|
||||||
|
let callback;
|
||||||
|
reporter.jasmineStarted = function(event, cb) {
|
||||||
|
callback = cb;
|
||||||
|
};
|
||||||
|
subject.addReporter(reporter);
|
||||||
|
|
||||||
|
subject.jasmineStarted({});
|
||||||
|
|
||||||
|
expect(callback).toBeInstanceOf(Function);
|
||||||
|
const error = new Error('nope');
|
||||||
|
callback(error);
|
||||||
|
|
||||||
|
expect(onError).toHaveBeenCalledWith(error);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('handles done.fail() from callback-style async reporters', function() {
|
||||||
|
const globalErrors = mockGlobalErrors();
|
||||||
|
const onError = jasmine.createSpy('onError');
|
||||||
|
const subject = new jasmineUnderTest.ParallelReportDispatcher(onError, {
|
||||||
|
globalErrors
|
||||||
|
});
|
||||||
|
const reporter = jasmine.createSpyObj('reporter', [
|
||||||
|
'jasmineStarted',
|
||||||
|
'jasmineDone'
|
||||||
|
]);
|
||||||
|
let callback;
|
||||||
|
reporter.jasmineStarted = function(event, cb) {
|
||||||
|
callback = cb;
|
||||||
|
};
|
||||||
|
subject.addReporter(reporter);
|
||||||
|
|
||||||
|
subject.jasmineStarted({});
|
||||||
|
|
||||||
|
expect(callback).toBeInstanceOf(Function);
|
||||||
|
const error = new Error('nope');
|
||||||
|
callback.fail(error);
|
||||||
|
expect(onError).toHaveBeenCalledWith(error);
|
||||||
|
onError.calls.reset();
|
||||||
|
|
||||||
|
callback.fail();
|
||||||
|
expect(onError).toHaveBeenCalledWith(
|
||||||
|
new Error('A reporter called done.fail()')
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('handles errors due to mixed async style in reporters', async function() {
|
||||||
|
const globalErrors = mockGlobalErrors();
|
||||||
|
const onError = jasmine.createSpy('onError');
|
||||||
|
const subject = new jasmineUnderTest.ParallelReportDispatcher(onError, {
|
||||||
|
globalErrors
|
||||||
|
});
|
||||||
|
subject.addReporter({
|
||||||
|
async jasmineStarted(event, done) {
|
||||||
|
done();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
await subject.jasmineStarted({});
|
||||||
|
expect(onError).toHaveBeenCalledWith(
|
||||||
|
new Error(
|
||||||
|
'An asynchronous before/it/after function took a done callback but also returned a promise. Either remove the done callback (recommended) or change the function to not return a promise.'
|
||||||
|
)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('handles errors due to multiple done calls in reporters', async function() {
|
||||||
|
const globalErrors = mockGlobalErrors();
|
||||||
|
const onError = jasmine.createSpy('onError');
|
||||||
|
const subject = new jasmineUnderTest.ParallelReportDispatcher(onError, {
|
||||||
|
globalErrors
|
||||||
|
});
|
||||||
|
subject.addReporter({
|
||||||
|
jasmineStarted(event, done) {
|
||||||
|
done();
|
||||||
|
done();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
await subject.jasmineStarted({});
|
||||||
|
expect(onError).toHaveBeenCalledWith(
|
||||||
|
new Error(
|
||||||
|
"An asynchronous reporter callback called its 'done' callback more than once."
|
||||||
|
)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
function mockGlobalErrors() {
|
||||||
|
const globalErrors = jasmine.createSpyObj('globalErrors', [
|
||||||
|
'install',
|
||||||
|
'pushListener',
|
||||||
|
'popListener'
|
||||||
|
]);
|
||||||
|
|
||||||
|
globalErrors.install.and.callFake(function() {
|
||||||
|
globalErrors.uninstall = jasmine.createSpy('globalErrors.uninstall');
|
||||||
|
});
|
||||||
|
|
||||||
|
return globalErrors;
|
||||||
|
}
|
||||||
|
});
|
||||||
@@ -185,43 +185,20 @@ describe('QueueRunner', function() {
|
|||||||
|
|
||||||
queueRunner.execute();
|
queueRunner.execute();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('does not log a deprecation', function(done) {
|
|
||||||
const err = new Error('foo'),
|
|
||||||
queueableFn1 = {
|
|
||||||
fn: function() {
|
|
||||||
return Promise.resolve(err);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
deprecated = jasmine.createSpy('deprecated'),
|
|
||||||
queueRunner = new jasmineUnderTest.QueueRunner({
|
|
||||||
queueableFns: [queueableFn1],
|
|
||||||
deprecated: deprecated,
|
|
||||||
onComplete: function() {
|
|
||||||
expect(deprecated).not.toHaveBeenCalled();
|
|
||||||
done();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
queueRunner.execute();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('and the argument is not an Error', function() {
|
describe('and the argument is not an Error', function() {
|
||||||
it('does not log a deprecation or report a failure', function(done) {
|
it('does not report a failure', function(done) {
|
||||||
const queueableFn1 = {
|
const queueableFn1 = {
|
||||||
fn: function() {
|
fn: function() {
|
||||||
return Promise.resolve('not an error');
|
return Promise.resolve('not an error');
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
failFn = jasmine.createSpy('fail'),
|
failFn = jasmine.createSpy('fail'),
|
||||||
deprecated = jasmine.createSpy('deprecated'),
|
|
||||||
queueRunner = new jasmineUnderTest.QueueRunner({
|
queueRunner = new jasmineUnderTest.QueueRunner({
|
||||||
queueableFns: [queueableFn1],
|
queueableFns: [queueableFn1],
|
||||||
deprecated: deprecated,
|
|
||||||
fail: failFn,
|
fail: failFn,
|
||||||
onComplete: function() {
|
onComplete: function() {
|
||||||
expect(deprecated).not.toHaveBeenCalled();
|
|
||||||
expect(failFn).not.toHaveBeenCalled();
|
expect(failFn).not.toHaveBeenCalled();
|
||||||
done();
|
done();
|
||||||
}
|
}
|
||||||
@@ -406,17 +383,12 @@ describe('QueueRunner', function() {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
nextQueueableFn = { fn: jasmine.createSpy('nextFn') },
|
nextQueueableFn = { fn: jasmine.createSpy('nextFn') },
|
||||||
deprecated = jasmine.createSpy('deprecated'),
|
|
||||||
queueRunner = new jasmineUnderTest.QueueRunner({
|
queueRunner = new jasmineUnderTest.QueueRunner({
|
||||||
deprecated: deprecated,
|
|
||||||
queueableFns: [queueableFn, nextQueueableFn]
|
queueableFns: [queueableFn, nextQueueableFn]
|
||||||
});
|
});
|
||||||
queueRunner.execute();
|
queueRunner.execute();
|
||||||
jasmine.clock().tick(1);
|
jasmine.clock().tick(1);
|
||||||
expect(nextQueueableFn.fn.calls.count()).toEqual(1);
|
expect(nextQueueableFn.fn.calls.count()).toEqual(1);
|
||||||
// Don't issue a deprecation. The error already tells the user that
|
|
||||||
// something went wrong.
|
|
||||||
expect(deprecated).not.toHaveBeenCalled();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return a null when you call done', function() {
|
it('should return a null when you call done', function() {
|
||||||
@@ -487,6 +459,32 @@ describe('QueueRunner', function() {
|
|||||||
expect(nextQueueableFn.fn).toHaveBeenCalled();
|
expect(nextQueueableFn.fn).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('handles a global error event with a message but no error', function() {
|
||||||
|
const queueableFn = {
|
||||||
|
// eslint-disable-next-line no-unused-vars
|
||||||
|
fn: function(done) {
|
||||||
|
const currentHandler = globalErrors.pushListener.calls.mostRecent()
|
||||||
|
.args[0];
|
||||||
|
currentHandler(undefined, { message: 'nope' });
|
||||||
|
},
|
||||||
|
timeout: 1
|
||||||
|
};
|
||||||
|
const onException = jasmine.createSpy('onException');
|
||||||
|
const globalErrors = {
|
||||||
|
pushListener: jasmine.createSpy('pushListener'),
|
||||||
|
popListener: jasmine.createSpy('popListener')
|
||||||
|
};
|
||||||
|
const queueRunner = new jasmineUnderTest.QueueRunner({
|
||||||
|
queueableFns: [queueableFn],
|
||||||
|
onException: onException,
|
||||||
|
globalErrors: globalErrors
|
||||||
|
});
|
||||||
|
|
||||||
|
queueRunner.execute();
|
||||||
|
|
||||||
|
expect(onException).toHaveBeenCalledWith('nope');
|
||||||
|
});
|
||||||
|
|
||||||
it('handles exceptions thrown while waiting for the stack to clear', function() {
|
it('handles exceptions thrown while waiting for the stack to clear', function() {
|
||||||
const queueableFn = {
|
const queueableFn = {
|
||||||
fn: function(done) {
|
fn: function(done) {
|
||||||
@@ -520,6 +518,40 @@ describe('QueueRunner', function() {
|
|||||||
clearStack.calls.argsFor(0)[0]();
|
clearStack.calls.argsFor(0)[0]();
|
||||||
expect(onException).toHaveBeenCalledWith(error);
|
expect(onException).toHaveBeenCalledWith(error);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('handles a global error event with no error while waiting for the stack to clear', function() {
|
||||||
|
const queueableFn = {
|
||||||
|
fn: function(done) {
|
||||||
|
done();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const errorListeners = [];
|
||||||
|
const globalErrors = {
|
||||||
|
pushListener: function(f) {
|
||||||
|
errorListeners.push(f);
|
||||||
|
},
|
||||||
|
popListener: function() {
|
||||||
|
errorListeners.pop();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const clearStack = jasmine.createSpy('clearStack');
|
||||||
|
const onException = jasmine.createSpy('onException');
|
||||||
|
const queueRunner = new jasmineUnderTest.QueueRunner({
|
||||||
|
queueableFns: [queueableFn],
|
||||||
|
globalErrors: globalErrors,
|
||||||
|
clearStack: clearStack,
|
||||||
|
onException: onException
|
||||||
|
});
|
||||||
|
|
||||||
|
queueRunner.execute();
|
||||||
|
jasmine.clock().tick();
|
||||||
|
expect(clearStack).toHaveBeenCalled();
|
||||||
|
expect(errorListeners.length).toEqual(1);
|
||||||
|
errorListeners[0](undefined, { message: 'nope' });
|
||||||
|
|
||||||
|
clearStack.calls.argsFor(0)[0]();
|
||||||
|
expect(onException).toHaveBeenCalledWith('nope');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('with a function that returns a promise', function() {
|
describe('with a function that returns a promise', function() {
|
||||||
@@ -623,11 +655,13 @@ describe('QueueRunner', function() {
|
|||||||
queueRunner.execute();
|
queueRunner.execute();
|
||||||
|
|
||||||
expect(onException).toHaveBeenCalledWith(
|
expect(onException).toHaveBeenCalledWith(
|
||||||
'An asynchronous ' +
|
new Error(
|
||||||
'before/it/after function took a done callback but also returned a ' +
|
'An asynchronous ' +
|
||||||
'promise. ' +
|
'before/it/after function took a done callback but also returned a ' +
|
||||||
'Either remove the done callback (recommended) or change the function ' +
|
'promise. ' +
|
||||||
'to not return a promise.'
|
'Either remove the done callback (recommended) or change the function ' +
|
||||||
|
'to not return a promise.'
|
||||||
|
)
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -643,15 +677,17 @@ describe('QueueRunner', function() {
|
|||||||
queueRunner.execute();
|
queueRunner.execute();
|
||||||
|
|
||||||
expect(onException).toHaveBeenCalledWith(
|
expect(onException).toHaveBeenCalledWith(
|
||||||
'An asynchronous ' +
|
new Error(
|
||||||
'before/it/after function was defined with the async keyword but ' +
|
'An asynchronous ' +
|
||||||
'also took a done callback. Either remove the done callback ' +
|
'before/it/after function was defined with the async keyword but ' +
|
||||||
'(recommended) or remove the async keyword.'
|
'also took a done callback. Either remove the done callback ' +
|
||||||
|
'(recommended) or remove the async keyword.'
|
||||||
|
)
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('passes the error instance to exception handlers in HTML browsers', function() {
|
it('passes final errors to exception handlers', function() {
|
||||||
const error = new Error('fake error'),
|
const error = new Error('fake error'),
|
||||||
onExceptionCallback = jasmine.createSpy('on exception callback'),
|
onExceptionCallback = jasmine.createSpy('on exception callback'),
|
||||||
queueRunner = new jasmineUnderTest.QueueRunner({
|
queueRunner = new jasmineUnderTest.QueueRunner({
|
||||||
@@ -659,24 +695,11 @@ describe('QueueRunner', function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
queueRunner.execute();
|
queueRunner.execute();
|
||||||
queueRunner.handleFinalError(error.message, 'fake.js', 1, 1, error);
|
queueRunner.handleFinalError(error);
|
||||||
|
|
||||||
expect(onExceptionCallback).toHaveBeenCalledWith(error);
|
expect(onExceptionCallback).toHaveBeenCalledWith(error);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('passes the first argument to exception handlers for compatibility', function() {
|
|
||||||
const error = new Error('fake error'),
|
|
||||||
onExceptionCallback = jasmine.createSpy('on exception callback'),
|
|
||||||
queueRunner = new jasmineUnderTest.QueueRunner({
|
|
||||||
onException: onExceptionCallback
|
|
||||||
});
|
|
||||||
|
|
||||||
queueRunner.execute();
|
|
||||||
queueRunner.handleFinalError(error.message);
|
|
||||||
|
|
||||||
expect(onExceptionCallback).toHaveBeenCalledWith(error.message);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('calls exception handlers when an exception is thrown in a fn', function() {
|
it('calls exception handlers when an exception is thrown in a fn', function() {
|
||||||
const queueableFn = {
|
const queueableFn = {
|
||||||
type: 'queueable',
|
type: 'queueable',
|
||||||
|
|||||||
@@ -18,13 +18,12 @@ describe('ReportDispatcher', function() {
|
|||||||
queueRunnerFactory
|
queueRunnerFactory
|
||||||
),
|
),
|
||||||
reporter = jasmine.createSpyObj('reporter', ['foo', 'bar']),
|
reporter = jasmine.createSpyObj('reporter', ['foo', 'bar']),
|
||||||
anotherReporter = jasmine.createSpyObj('reporter', ['foo', 'bar']),
|
anotherReporter = jasmine.createSpyObj('reporter', ['foo', 'bar']);
|
||||||
completeCallback = jasmine.createSpy('complete');
|
|
||||||
|
|
||||||
dispatcher.addReporter(reporter);
|
dispatcher.addReporter(reporter);
|
||||||
dispatcher.addReporter(anotherReporter);
|
dispatcher.addReporter(anotherReporter);
|
||||||
|
|
||||||
dispatcher.foo(123, 456, completeCallback);
|
dispatcher.foo(123, 456);
|
||||||
|
|
||||||
expect(queueRunnerFactory).toHaveBeenCalledWith(
|
expect(queueRunnerFactory).toHaveBeenCalledWith(
|
||||||
jasmine.objectContaining({
|
jasmine.objectContaining({
|
||||||
@@ -47,7 +46,7 @@ describe('ReportDispatcher', function() {
|
|||||||
|
|
||||||
queueRunnerFactory.calls.reset();
|
queueRunnerFactory.calls.reset();
|
||||||
|
|
||||||
dispatcher.bar('a', 'b', completeCallback);
|
dispatcher.bar('a', 'b');
|
||||||
|
|
||||||
expect(queueRunnerFactory).toHaveBeenCalledWith(
|
expect(queueRunnerFactory).toHaveBeenCalledWith(
|
||||||
jasmine.objectContaining({
|
jasmine.objectContaining({
|
||||||
@@ -91,11 +90,10 @@ describe('ReportDispatcher', function() {
|
|||||||
['foo', 'bar'],
|
['foo', 'bar'],
|
||||||
queueRunnerFactory
|
queueRunnerFactory
|
||||||
),
|
),
|
||||||
reporter = jasmine.createSpyObj('reporter', ['foo', 'bar']),
|
reporter = jasmine.createSpyObj('reporter', ['foo', 'bar']);
|
||||||
completeCallback = jasmine.createSpy('complete');
|
|
||||||
|
|
||||||
dispatcher.provideFallbackReporter(reporter);
|
dispatcher.provideFallbackReporter(reporter);
|
||||||
dispatcher.foo(123, 456, completeCallback);
|
dispatcher.foo(123, 456);
|
||||||
|
|
||||||
expect(queueRunnerFactory).toHaveBeenCalledWith(
|
expect(queueRunnerFactory).toHaveBeenCalledWith(
|
||||||
jasmine.objectContaining({
|
jasmine.objectContaining({
|
||||||
@@ -116,12 +114,11 @@ describe('ReportDispatcher', function() {
|
|||||||
queueRunnerFactory
|
queueRunnerFactory
|
||||||
),
|
),
|
||||||
reporter = jasmine.createSpyObj('reporter', ['foo', 'bar']),
|
reporter = jasmine.createSpyObj('reporter', ['foo', 'bar']),
|
||||||
fallbackReporter = jasmine.createSpyObj('otherReporter', ['foo', 'bar']),
|
fallbackReporter = jasmine.createSpyObj('otherReporter', ['foo', 'bar']);
|
||||||
completeCallback = jasmine.createSpy('complete');
|
|
||||||
|
|
||||||
dispatcher.provideFallbackReporter(fallbackReporter);
|
dispatcher.provideFallbackReporter(fallbackReporter);
|
||||||
dispatcher.addReporter(reporter);
|
dispatcher.addReporter(reporter);
|
||||||
dispatcher.foo(123, 456, completeCallback);
|
dispatcher.foo(123, 456);
|
||||||
|
|
||||||
expect(queueRunnerFactory).toHaveBeenCalledWith(
|
expect(queueRunnerFactory).toHaveBeenCalledWith(
|
||||||
jasmine.objectContaining({
|
jasmine.objectContaining({
|
||||||
@@ -143,11 +140,10 @@ describe('ReportDispatcher', function() {
|
|||||||
queueRunnerFactory
|
queueRunnerFactory
|
||||||
),
|
),
|
||||||
reporter1 = jasmine.createSpyObj('reporter1', ['foo', 'bar']),
|
reporter1 = jasmine.createSpyObj('reporter1', ['foo', 'bar']),
|
||||||
reporter2 = jasmine.createSpyObj('reporter2', ['foo', 'bar']),
|
reporter2 = jasmine.createSpyObj('reporter2', ['foo', 'bar']);
|
||||||
completeCallback = jasmine.createSpy('complete');
|
|
||||||
|
|
||||||
dispatcher.addReporter(reporter1);
|
dispatcher.addReporter(reporter1);
|
||||||
dispatcher.foo(123, completeCallback);
|
dispatcher.foo(123);
|
||||||
expect(queueRunnerFactory).toHaveBeenCalledWith(
|
expect(queueRunnerFactory).toHaveBeenCalledWith(
|
||||||
jasmine.objectContaining({
|
jasmine.objectContaining({
|
||||||
queueableFns: [{ fn: jasmine.any(Function) }],
|
queueableFns: [{ fn: jasmine.any(Function) }],
|
||||||
@@ -161,7 +157,7 @@ describe('ReportDispatcher', function() {
|
|||||||
|
|
||||||
dispatcher.clearReporters();
|
dispatcher.clearReporters();
|
||||||
dispatcher.addReporter(reporter2);
|
dispatcher.addReporter(reporter2);
|
||||||
dispatcher.bar(456, completeCallback);
|
dispatcher.bar(456);
|
||||||
|
|
||||||
expect(queueRunnerFactory).toHaveBeenCalledWith(
|
expect(queueRunnerFactory).toHaveBeenCalledWith(
|
||||||
jasmine.objectContaining({
|
jasmine.objectContaining({
|
||||||
|
|||||||
@@ -195,6 +195,8 @@ describe('Spec', function() {
|
|||||||
onStart: startCallback,
|
onStart: startCallback,
|
||||||
resultCallback: resultCallback,
|
resultCallback: resultCallback,
|
||||||
description: 'with a spec',
|
description: 'with a spec',
|
||||||
|
parentSuiteId: 'suite1',
|
||||||
|
filename: 'someSpecFile.js',
|
||||||
getSpecName: function() {
|
getSpecName: function() {
|
||||||
return 'a suite with a spec';
|
return 'a suite with a spec';
|
||||||
},
|
},
|
||||||
@@ -219,6 +221,8 @@ describe('Spec', function() {
|
|||||||
status: 'pending',
|
status: 'pending',
|
||||||
description: 'with a spec',
|
description: 'with a spec',
|
||||||
fullName: 'a suite with a spec',
|
fullName: 'a suite with a spec',
|
||||||
|
parentSuiteId: 'suite1',
|
||||||
|
filename: 'someSpecFile.js',
|
||||||
failedExpectations: [],
|
failedExpectations: [],
|
||||||
passedExpectations: [],
|
passedExpectations: [],
|
||||||
deprecationWarnings: [],
|
deprecationWarnings: [],
|
||||||
|
|||||||
@@ -175,4 +175,91 @@ describe('SuiteBuilder', function() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
describe('#parallelReset', function() {
|
||||||
|
it('resets the top suite result', function() {
|
||||||
|
jasmineUnderTest.Suite.prototype.handleException.and.callThrough();
|
||||||
|
|
||||||
|
const env = { configuration: () => ({}) };
|
||||||
|
const suiteBuilder = new jasmineUnderTest.SuiteBuilder({ env });
|
||||||
|
|
||||||
|
suiteBuilder.topSuite.handleException(new Error('nope'));
|
||||||
|
suiteBuilder.parallelReset();
|
||||||
|
|
||||||
|
expect(suiteBuilder.topSuite.result).toEqual({
|
||||||
|
id: suiteBuilder.topSuite.id,
|
||||||
|
description: 'Jasmine__TopLevel__Suite',
|
||||||
|
fullName: '',
|
||||||
|
failedExpectations: [],
|
||||||
|
deprecationWarnings: [],
|
||||||
|
duration: null,
|
||||||
|
properties: null,
|
||||||
|
parentSuiteId: null,
|
||||||
|
filename: undefined
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('removes children of the top suite', function() {
|
||||||
|
const env = { configuration: () => ({}) };
|
||||||
|
const suiteBuilder = new jasmineUnderTest.SuiteBuilder({ env });
|
||||||
|
suiteBuilder.describe('a suite', function() {
|
||||||
|
suiteBuilder.it('a nested spec');
|
||||||
|
});
|
||||||
|
suiteBuilder.it('a spec');
|
||||||
|
|
||||||
|
suiteBuilder.parallelReset();
|
||||||
|
|
||||||
|
expect(suiteBuilder.topSuite.children).toEqual([]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('preserves top suite befores and afters', function() {
|
||||||
|
const env = { configuration: () => ({}) };
|
||||||
|
const suiteBuilder = new jasmineUnderTest.SuiteBuilder({ env });
|
||||||
|
|
||||||
|
function beforeAll() {}
|
||||||
|
function beforeEach() {}
|
||||||
|
function afterEach() {}
|
||||||
|
function afterAll() {}
|
||||||
|
|
||||||
|
suiteBuilder.beforeAll(beforeAll);
|
||||||
|
suiteBuilder.beforeEach(beforeEach);
|
||||||
|
suiteBuilder.afterEach(afterEach);
|
||||||
|
suiteBuilder.afterAll(afterAll);
|
||||||
|
|
||||||
|
suiteBuilder.parallelReset();
|
||||||
|
|
||||||
|
expect(suiteBuilder.topSuite.beforeAllFns).toEqual([
|
||||||
|
jasmine.objectContaining({ fn: beforeAll })
|
||||||
|
]);
|
||||||
|
expect(suiteBuilder.topSuite.beforeFns).toEqual([
|
||||||
|
jasmine.objectContaining({ fn: beforeEach })
|
||||||
|
]);
|
||||||
|
expect(suiteBuilder.topSuite.afterFns).toEqual([
|
||||||
|
jasmine.objectContaining({ fn: afterEach })
|
||||||
|
]);
|
||||||
|
expect(suiteBuilder.topSuite.afterAllFns).toEqual([
|
||||||
|
jasmine.objectContaining({ fn: afterAll })
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('resets totalSpecsDefined', function() {
|
||||||
|
const env = { configuration: () => ({}) };
|
||||||
|
const suiteBuilder = new jasmineUnderTest.SuiteBuilder({ env });
|
||||||
|
suiteBuilder.it('a spec');
|
||||||
|
|
||||||
|
suiteBuilder.parallelReset();
|
||||||
|
|
||||||
|
expect(suiteBuilder.totalSpecsDefined).toEqual(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('resets focusedRunables', function() {
|
||||||
|
const env = { configuration: () => ({}) };
|
||||||
|
const suiteBuilder = new jasmineUnderTest.SuiteBuilder({ env });
|
||||||
|
suiteBuilder.fit('a spec', function() {});
|
||||||
|
|
||||||
|
suiteBuilder.parallelReset();
|
||||||
|
|
||||||
|
expect(suiteBuilder.focusedRunables).toEqual([]);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -274,7 +274,7 @@ describe('TreeProcessor', function() {
|
|||||||
expect(result.valid).toBe(true);
|
expect(result.valid).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('runs a single leaf', function() {
|
it('runs a single leaf', async function() {
|
||||||
const leaf = new Leaf(),
|
const leaf = new Leaf(),
|
||||||
node = new Node({ children: [leaf], userContext: { root: 'context' } }),
|
node = new Node({ children: [leaf], userContext: { root: 'context' } }),
|
||||||
queueRunner = jasmine.createSpy('queueRunner'),
|
queueRunner = jasmine.createSpy('queueRunner'),
|
||||||
@@ -282,25 +282,27 @@ describe('TreeProcessor', function() {
|
|||||||
tree: node,
|
tree: node,
|
||||||
runnableIds: [leaf.id],
|
runnableIds: [leaf.id],
|
||||||
queueRunnerFactory: queueRunner
|
queueRunnerFactory: queueRunner
|
||||||
}),
|
});
|
||||||
treeComplete = jasmine.createSpy('treeComplete');
|
|
||||||
|
|
||||||
processor.execute(treeComplete);
|
const promise = processor.execute();
|
||||||
|
|
||||||
expect(queueRunner).toHaveBeenCalledWith({
|
expect(queueRunner).toHaveBeenCalledWith({
|
||||||
onComplete: treeComplete,
|
onComplete: jasmine.any(Function),
|
||||||
onException: jasmine.any(Function),
|
onException: jasmine.any(Function),
|
||||||
userContext: { root: 'context' },
|
userContext: { root: 'context' },
|
||||||
queueableFns: [{ fn: jasmine.any(Function) }],
|
queueableFns: [{ fn: jasmine.any(Function) }],
|
||||||
onMultipleDone: null
|
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);
|
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' } }),
|
const node = new Node({ userContext: { node: 'context' } }),
|
||||||
root = new Node({ children: [node], userContext: { root: 'context' } }),
|
root = new Node({ children: [node], userContext: { root: 'context' } }),
|
||||||
nodeStart = jasmine.createSpy('nodeStart'),
|
nodeStart = jasmine.createSpy('nodeStart'),
|
||||||
@@ -313,21 +315,20 @@ describe('TreeProcessor', function() {
|
|||||||
nodeComplete: nodeComplete,
|
nodeComplete: nodeComplete,
|
||||||
queueRunnerFactory: queueRunner
|
queueRunnerFactory: queueRunner
|
||||||
}),
|
}),
|
||||||
treeComplete = jasmine.createSpy('treeComplete'),
|
|
||||||
nodeDone = jasmine.createSpy('nodeDone');
|
nodeDone = jasmine.createSpy('nodeDone');
|
||||||
|
|
||||||
processor.execute(treeComplete);
|
const promise = processor.execute();
|
||||||
|
|
||||||
expect(queueRunner).toHaveBeenCalledWith({
|
expect(queueRunner).toHaveBeenCalledWith({
|
||||||
onComplete: treeComplete,
|
onComplete: jasmine.any(Function),
|
||||||
onException: jasmine.any(Function),
|
onException: jasmine.any(Function),
|
||||||
userContext: { root: 'context' },
|
userContext: { root: 'context' },
|
||||||
queueableFns: [{ fn: jasmine.any(Function) }],
|
queueableFns: [{ fn: jasmine.any(Function) }],
|
||||||
onMultipleDone: null
|
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({
|
expect(queueRunner).toHaveBeenCalledWith({
|
||||||
onComplete: jasmine.any(Function),
|
onComplete: jasmine.any(Function),
|
||||||
onMultipleDone: null,
|
onMultipleDone: null,
|
||||||
@@ -348,6 +349,9 @@ describe('TreeProcessor', function() {
|
|||||||
{ my: 'result' },
|
{ my: 'result' },
|
||||||
jasmine.any(Function)
|
jasmine.any(Function)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
queueRunnerArgs.onComplete();
|
||||||
|
await expectAsync(promise).toBeResolvedTo(undefined);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('runs a node with children', function() {
|
it('runs a node with children', function() {
|
||||||
|
|||||||
@@ -145,7 +145,7 @@ describe('base helpers', function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('returns a promise that resolves to false when the promise is rejected', function() {
|
it('returns a promise that resolves to false when the promise is rejected', function() {
|
||||||
const promise = Promise.reject();
|
const promise = Promise.reject(new Error('nope'));
|
||||||
return expectAsync(jasmineUnderTest.isPending_(promise)).toBeResolvedTo(
|
return expectAsync(jasmineUnderTest.isPending_(promise)).toBeResolvedTo(
|
||||||
false
|
false
|
||||||
);
|
);
|
||||||
@@ -179,7 +179,10 @@ describe('base helpers', function() {
|
|||||||
f2 = jasmine.createSpy('setTimeout callback for ' + (max + 1));
|
f2 = jasmine.createSpy('setTimeout callback for ' + (max + 1));
|
||||||
|
|
||||||
// Suppress printing of TimeoutOverflowWarning in node
|
// Suppress printing of TimeoutOverflowWarning in node
|
||||||
spyOn(console, 'error');
|
if (typeof process !== 'undefined' && process.emitWarning) {
|
||||||
|
spyOn(process, 'emitWarning'); // Node 22
|
||||||
|
}
|
||||||
|
spyOn(console, 'error'); // Node <22
|
||||||
|
|
||||||
let id = setTimeout(f1, max);
|
let id = setTimeout(f1, max);
|
||||||
setTimeout(function() {
|
setTimeout(function() {
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -349,7 +349,7 @@ describe('Matchers (Integration)', function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
verifyFailsAsync(function(env) {
|
verifyFailsAsync(function(env) {
|
||||||
return env.expectAsync(Promise.reject()).toBeResolved();
|
return env.expectAsync(Promise.reject(new Error('nope'))).toBeResolved();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -610,19 +610,13 @@ describe('Matchers (Integration)', function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('toHaveClass', function() {
|
describe('toHaveClass', function() {
|
||||||
beforeEach(function() {
|
|
||||||
this.domHelpers = jasmine.getEnv().domHelpers();
|
|
||||||
});
|
|
||||||
|
|
||||||
verifyPasses(function(env) {
|
verifyPasses(function(env) {
|
||||||
const domHelpers = jasmine.getEnv().domHelpers();
|
const el = specHelpers.domHelpers.createElementWithClassName('foo');
|
||||||
const el = domHelpers.createElementWithClassName('foo');
|
|
||||||
env.expect(el).toHaveClass('foo');
|
env.expect(el).toHaveClass('foo');
|
||||||
});
|
});
|
||||||
|
|
||||||
verifyFails(function(env) {
|
verifyFails(function(env) {
|
||||||
const domHelpers = jasmine.getEnv().domHelpers();
|
const el = specHelpers.domHelpers.createElementWithClassName('foo');
|
||||||
const el = domHelpers.createElementWithClassName('foo');
|
|
||||||
env.expect(el).toHaveClass('bar');
|
env.expect(el).toHaveClass('bar');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
162
spec/core/integration/ParallelSpec.js
Normal file
162
spec/core/integration/ParallelSpec.js
Normal file
@@ -0,0 +1,162 @@
|
|||||||
|
describe('Support for parallel execution', function() {
|
||||||
|
let env;
|
||||||
|
|
||||||
|
beforeEach(function() {
|
||||||
|
env = new jasmineUnderTest.Env();
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(function() {
|
||||||
|
env.cleanup_();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('removes specs and suites from previous batches', async function() {
|
||||||
|
env.describe('a suite', function() {
|
||||||
|
env.it('a spec', function() {});
|
||||||
|
});
|
||||||
|
env.it('a spec', function() {});
|
||||||
|
|
||||||
|
await env.execute();
|
||||||
|
env.parallelReset();
|
||||||
|
|
||||||
|
env.describe('a suite in a new batch', function() {
|
||||||
|
env.it('a spec in a new batch', function() {});
|
||||||
|
});
|
||||||
|
const reporter = jasmine.createSpyObj('reporter', [
|
||||||
|
'suiteDone',
|
||||||
|
'specDone'
|
||||||
|
]);
|
||||||
|
env.addReporter(reporter);
|
||||||
|
|
||||||
|
await env.execute();
|
||||||
|
|
||||||
|
expect(reporter.suiteDone).toHaveBeenCalledOnceWith(
|
||||||
|
jasmine.objectContaining({
|
||||||
|
fullName: 'a suite in a new batch'
|
||||||
|
})
|
||||||
|
);
|
||||||
|
expect(reporter.specDone).toHaveBeenCalledOnceWith(
|
||||||
|
jasmine.objectContaining({
|
||||||
|
fullName: 'a suite in a new batch a spec in a new batch'
|
||||||
|
})
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('preserves top-level before and after fns from previous batches', async function() {
|
||||||
|
const beforeAll = jasmine.createSpy('beforeAll');
|
||||||
|
const beforeEach = jasmine.createSpy('beforeEach');
|
||||||
|
const afterEach = jasmine.createSpy('afterEach');
|
||||||
|
const afterAll = jasmine.createSpy('afterAll');
|
||||||
|
env.beforeAll(beforeAll);
|
||||||
|
env.beforeEach(beforeEach);
|
||||||
|
env.afterEach(afterEach);
|
||||||
|
env.afterAll(afterAll);
|
||||||
|
|
||||||
|
env.parallelReset();
|
||||||
|
env.it('a spec', function() {});
|
||||||
|
await env.execute();
|
||||||
|
|
||||||
|
expect(beforeAll).toHaveBeenCalled();
|
||||||
|
expect(beforeEach).toHaveBeenCalled();
|
||||||
|
expect(afterEach).toHaveBeenCalled();
|
||||||
|
expect(afterAll).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does not remember focused runables from previous batches', async function() {
|
||||||
|
env.fit('a focused spec', function() {});
|
||||||
|
env.parallelReset();
|
||||||
|
env.it('a spec', function() {});
|
||||||
|
const reporter = jasmine.createSpyObj('reporter', [
|
||||||
|
'specDone',
|
||||||
|
'jasmineDone'
|
||||||
|
]);
|
||||||
|
env.addReporter(reporter);
|
||||||
|
await env.execute();
|
||||||
|
|
||||||
|
expect(reporter.specDone).toHaveBeenCalledOnceWith(
|
||||||
|
jasmine.objectContaining({
|
||||||
|
fullName: 'a spec',
|
||||||
|
status: 'passed'
|
||||||
|
})
|
||||||
|
);
|
||||||
|
expect(reporter.jasmineDone).toHaveBeenCalledWith(
|
||||||
|
jasmine.objectContaining({ overallStatus: 'passed' })
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does not remember failures from previous batches', async function() {
|
||||||
|
env.it('a failing spec', function() {
|
||||||
|
env.expect(true).toBe(false);
|
||||||
|
});
|
||||||
|
await env.execute();
|
||||||
|
env.parallelReset();
|
||||||
|
env.it('a spec', function() {});
|
||||||
|
const reporter = jasmine.createSpyObj('reporter', [
|
||||||
|
'specDone',
|
||||||
|
'jasmineDone'
|
||||||
|
]);
|
||||||
|
env.addReporter(reporter);
|
||||||
|
await env.execute();
|
||||||
|
|
||||||
|
expect(reporter.jasmineDone).toHaveBeenCalledWith(
|
||||||
|
jasmine.objectContaining({ overallStatus: 'passed' })
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('reports errors thrown from describe', async function() {
|
||||||
|
const reporter = jasmine.createSpyObj('reporter', ['suiteDone']);
|
||||||
|
env.addReporter(reporter);
|
||||||
|
|
||||||
|
env.describe('borken', function() {
|
||||||
|
throw new Error('nope');
|
||||||
|
});
|
||||||
|
await env.execute();
|
||||||
|
|
||||||
|
expect(reporter.suiteDone).toHaveBeenCalledWith(
|
||||||
|
jasmine.objectContaining({
|
||||||
|
description: 'borken',
|
||||||
|
status: 'failed',
|
||||||
|
failedExpectations: [
|
||||||
|
jasmine.objectContaining({
|
||||||
|
message: jasmine.stringContaining('Error: nope')
|
||||||
|
})
|
||||||
|
]
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
// Errors in subsequent suites should also be reported
|
||||||
|
reporter.suiteDone.calls.reset();
|
||||||
|
env.parallelReset();
|
||||||
|
env.describe('zarro boogs', function() {
|
||||||
|
throw new Error('nor that either');
|
||||||
|
});
|
||||||
|
await env.execute();
|
||||||
|
|
||||||
|
expect(reporter.suiteDone).toHaveBeenCalledOnceWith(
|
||||||
|
jasmine.objectContaining({
|
||||||
|
description: 'zarro boogs',
|
||||||
|
status: 'failed',
|
||||||
|
failedExpectations: [
|
||||||
|
jasmine.objectContaining({
|
||||||
|
message: jasmine.stringContaining('Error: nor that either')
|
||||||
|
})
|
||||||
|
]
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
// Failure state should not persist across resets
|
||||||
|
reporter.suiteDone.calls.reset();
|
||||||
|
env.parallelReset();
|
||||||
|
env.describe('actually works', function() {
|
||||||
|
env.it('a spec', function() {});
|
||||||
|
});
|
||||||
|
await env.execute();
|
||||||
|
|
||||||
|
expect(reporter.suiteDone).toHaveBeenCalledOnceWith(
|
||||||
|
jasmine.objectContaining({
|
||||||
|
description: 'actually works',
|
||||||
|
status: 'passed',
|
||||||
|
failedExpectations: []
|
||||||
|
})
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -2,7 +2,7 @@ describe('spec running', function() {
|
|||||||
let env;
|
let env;
|
||||||
|
|
||||||
beforeEach(function() {
|
beforeEach(function() {
|
||||||
jasmine.getEnv().registerIntegrationMatchers();
|
specHelpers.registerIntegrationMatchers();
|
||||||
env = new jasmineUnderTest.Env();
|
env = new jasmineUnderTest.Env();
|
||||||
env.configure({ random: false });
|
env.configure({ random: false });
|
||||||
});
|
});
|
||||||
@@ -626,7 +626,7 @@ describe('spec running', function() {
|
|||||||
expect(actions).toEqual(['spec2', 'spec3', 'spec1']);
|
expect(actions).toEqual(['spec2', 'spec3', 'spec1']);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('refuses to re-enter suites with a beforeAll', function() {
|
it('refuses to re-enter suites with a beforeAll', async function() {
|
||||||
const actions = [];
|
const actions = [];
|
||||||
let spec1;
|
let spec1;
|
||||||
let spec2;
|
let spec2;
|
||||||
@@ -648,13 +648,12 @@ describe('spec running', function() {
|
|||||||
actions.push('spec3');
|
actions.push('spec3');
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(function() {
|
const promise = env.execute([spec2.id, spec3.id, spec1.id]);
|
||||||
env.execute([spec2.id, spec3.id, spec1.id]);
|
await expectAsync(promise).toBeRejectedWithError(/beforeAll/);
|
||||||
}).toThrowError(/beforeAll/);
|
|
||||||
expect(actions).toEqual([]);
|
expect(actions).toEqual([]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('refuses to re-enter suites with a afterAll', function() {
|
it('refuses to re-enter suites with a afterAll', async function() {
|
||||||
const actions = [];
|
const actions = [];
|
||||||
let spec1;
|
let spec1;
|
||||||
let spec2;
|
let spec2;
|
||||||
@@ -676,9 +675,8 @@ describe('spec running', function() {
|
|||||||
actions.push('spec3');
|
actions.push('spec3');
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(function() {
|
const promise = env.execute([spec2.id, spec3.id, spec1.id]);
|
||||||
env.execute([spec2.id, spec3.id, spec1.id]);
|
await expectAsync(promise).toBeRejectedWithError(/afterAll/);
|
||||||
}).toThrowError(/afterAll/);
|
|
||||||
expect(actions).toEqual([]);
|
expect(actions).toEqual([]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,8 @@
|
|||||||
describe('toHaveClass', function() {
|
describe('toHaveClass', function() {
|
||||||
beforeEach(function() {
|
|
||||||
this.domHelpers = jasmine.getEnv().domHelpers();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('fails for a DOM element that lacks the expected class', function() {
|
it('fails for a DOM element that lacks the expected class', function() {
|
||||||
const matcher = jasmineUnderTest.matchers.toHaveClass(),
|
const matcher = jasmineUnderTest.matchers.toHaveClass(),
|
||||||
result = matcher.compare(
|
result = matcher.compare(
|
||||||
this.domHelpers.createElementWithClassName(''),
|
specHelpers.domHelpers.createElementWithClassName(''),
|
||||||
'foo'
|
'foo'
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -15,7 +11,7 @@ describe('toHaveClass', function() {
|
|||||||
|
|
||||||
it('passes for a DOM element that has the expected class', function() {
|
it('passes for a DOM element that has the expected class', function() {
|
||||||
const matcher = jasmineUnderTest.matchers.toHaveClass(),
|
const matcher = jasmineUnderTest.matchers.toHaveClass(),
|
||||||
el = this.domHelpers.createElementWithClassName('foo bar baz');
|
el = specHelpers.domHelpers.createElementWithClassName('foo bar baz');
|
||||||
|
|
||||||
expect(matcher.compare(el, 'foo').pass).toBe(true);
|
expect(matcher.compare(el, 'foo').pass).toBe(true);
|
||||||
expect(matcher.compare(el, 'bar').pass).toBe(true);
|
expect(matcher.compare(el, 'bar').pass).toBe(true);
|
||||||
@@ -24,7 +20,7 @@ describe('toHaveClass', function() {
|
|||||||
|
|
||||||
it('fails for a DOM element that only has other classes', function() {
|
it('fails for a DOM element that only has other classes', function() {
|
||||||
const matcher = jasmineUnderTest.matchers.toHaveClass(),
|
const matcher = jasmineUnderTest.matchers.toHaveClass(),
|
||||||
el = this.domHelpers.createElementWithClassName('foo bar');
|
el = specHelpers.domHelpers.createElementWithClassName('foo bar');
|
||||||
|
|
||||||
expect(matcher.compare(el, 'fo').pass).toBe(false);
|
expect(matcher.compare(el, 'fo').pass).toBe(false);
|
||||||
});
|
});
|
||||||
@@ -42,7 +38,7 @@ describe('toHaveClass', function() {
|
|||||||
matcher.compare(undefined, 'foo');
|
matcher.compare(undefined, 'foo');
|
||||||
}).toThrowError('undefined is not a DOM element');
|
}).toThrowError('undefined is not a DOM element');
|
||||||
|
|
||||||
const textNode = this.domHelpers.document.createTextNode('');
|
const textNode = specHelpers.domHelpers.document.createTextNode('');
|
||||||
expect(function() {
|
expect(function() {
|
||||||
matcher.compare(textNode, 'foo');
|
matcher.compare(textNode, 'foo');
|
||||||
}).toThrowError('HTMLNode is not a DOM element');
|
}).toThrowError('HTMLNode is not a DOM element');
|
||||||
|
|||||||
@@ -15,6 +15,17 @@ describe('toHaveSize', function() {
|
|||||||
expect(result.pass).toBe(false);
|
expect(result.pass).toBe(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('informs about the size of an array whose length does not match', function() {
|
||||||
|
const matcher = jasmineUnderTest.matchers.toHaveSize({
|
||||||
|
pp: jasmineUnderTest.makePrettyPrinter()
|
||||||
|
}),
|
||||||
|
result = matcher.compare([1, 2, 3], 2);
|
||||||
|
|
||||||
|
expect(result.message()).toEqual(
|
||||||
|
'Expected [ 1, 2, 3 ] with size 3 to have size 2.'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
it('passes for an object with the proper number of keys', function() {
|
it('passes for an object with the proper number of keys', function() {
|
||||||
const matcher = jasmineUnderTest.matchers.toHaveSize(),
|
const matcher = jasmineUnderTest.matchers.toHaveSize(),
|
||||||
result = matcher.compare({ a: 1, b: 2 }, 2);
|
result = matcher.compare({ a: 1, b: 2 }, 2);
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
(function(env) {
|
(function() {
|
||||||
function browserVersion(matchFn) {
|
function browserVersion(matchFn) {
|
||||||
const userAgent = jasmine.getGlobal().navigator.userAgent;
|
const userAgent = jasmine.getGlobal().navigator.userAgent;
|
||||||
if (!userAgent) {
|
if (!userAgent) {
|
||||||
@@ -10,7 +10,7 @@
|
|||||||
return match ? parseFloat(match[1]) : void 0;
|
return match ? parseFloat(match[1]) : void 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
env.firefoxVersion = browserVersion(function(userAgent) {
|
specHelpers.firefoxVersion = browserVersion(function(userAgent) {
|
||||||
return /Firefox\/([0-9]{0,})/.exec(userAgent);
|
return /Firefox\/([0-9]{0,})/.exec(userAgent);
|
||||||
});
|
});
|
||||||
})(jasmine.getEnv());
|
})();
|
||||||
|
|||||||
@@ -1,24 +1,20 @@
|
|||||||
(function(env) {
|
(function() {
|
||||||
function domHelpers() {
|
let doc;
|
||||||
let doc;
|
|
||||||
|
|
||||||
if (typeof document !== 'undefined') {
|
if (typeof document !== 'undefined') {
|
||||||
doc = document;
|
doc = document;
|
||||||
} else {
|
} else {
|
||||||
const JSDOM = require('jsdom').JSDOM;
|
const JSDOM = require('jsdom').JSDOM;
|
||||||
const dom = new JSDOM();
|
const dom = new JSDOM();
|
||||||
doc = dom.window.document;
|
doc = dom.window.document;
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
document: doc,
|
|
||||||
createElementWithClassName: function(className) {
|
|
||||||
const el = this.document.createElement('div');
|
|
||||||
el.className = className;
|
|
||||||
return el;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
env.domHelpers = domHelpers;
|
specHelpers.domHelpers = {
|
||||||
})(jasmine.getEnv());
|
document: doc,
|
||||||
|
createElementWithClassName(className) {
|
||||||
|
const el = this.document.createElement('div');
|
||||||
|
el.className = className;
|
||||||
|
return el;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
})();
|
||||||
|
|||||||
1
spec/helpers/init.js
Normal file
1
spec/helpers/init.js
Normal file
@@ -0,0 +1 @@
|
|||||||
|
globalThis.specHelpers = {};
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
(function(env) {
|
(function() {
|
||||||
env.registerIntegrationMatchers = function() {
|
specHelpers.registerIntegrationMatchers = function() {
|
||||||
jasmine.addMatchers({
|
jasmine.addMatchers({
|
||||||
toHaveFailedExpectationsForRunnable: function() {
|
toHaveFailedExpectationsForRunnable: function() {
|
||||||
return {
|
return {
|
||||||
@@ -51,4 +51,4 @@
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
})(jasmine.getEnv());
|
})();
|
||||||
|
|||||||
@@ -1411,6 +1411,23 @@ describe('HtmlReporter', function() {
|
|||||||
describe('and there are pending specs', function() {
|
describe('and there are pending specs', function() {
|
||||||
let container, reporter;
|
let container, reporter;
|
||||||
|
|
||||||
|
function pendingSpecStatus() {
|
||||||
|
return {
|
||||||
|
id: 123,
|
||||||
|
description: 'with a spec',
|
||||||
|
fullName: 'A Suite with a spec',
|
||||||
|
status: 'pending',
|
||||||
|
passedExpectations: [],
|
||||||
|
failedExpectations: []
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function reportWithSpecStatus(specStatus) {
|
||||||
|
reporter.specStarted(specStatus);
|
||||||
|
reporter.specDone(specStatus);
|
||||||
|
reporter.jasmineDone({});
|
||||||
|
}
|
||||||
|
|
||||||
beforeEach(function() {
|
beforeEach(function() {
|
||||||
container = document.createElement('div');
|
container = document.createElement('div');
|
||||||
const getContainer = function() {
|
const getContainer = function() {
|
||||||
@@ -1429,21 +1446,10 @@ describe('HtmlReporter', function() {
|
|||||||
reporter.initialize();
|
reporter.initialize();
|
||||||
|
|
||||||
reporter.jasmineStarted({ totalSpecsDefined: 1 });
|
reporter.jasmineStarted({ totalSpecsDefined: 1 });
|
||||||
const specStatus = {
|
|
||||||
id: 123,
|
|
||||||
description: 'with a spec',
|
|
||||||
fullName: 'A Suite with a spec',
|
|
||||||
status: 'pending',
|
|
||||||
passedExpectations: [],
|
|
||||||
failedExpectations: [],
|
|
||||||
pendingReason: 'my custom pending reason'
|
|
||||||
};
|
|
||||||
reporter.specStarted(specStatus);
|
|
||||||
reporter.specDone(specStatus);
|
|
||||||
reporter.jasmineDone({});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('reports the pending specs count', function() {
|
it('reports the pending specs count', function() {
|
||||||
|
reportWithSpecStatus(pendingSpecStatus());
|
||||||
const alertBar = container.querySelector('.jasmine-alert .jasmine-bar');
|
const alertBar = container.querySelector('.jasmine-alert .jasmine-bar');
|
||||||
|
|
||||||
expect(alertBar.innerHTML).toMatch(
|
expect(alertBar.innerHTML).toMatch(
|
||||||
@@ -1452,17 +1458,36 @@ describe('HtmlReporter', function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('reports no failure details', function() {
|
it('reports no failure details', function() {
|
||||||
|
reportWithSpecStatus(pendingSpecStatus());
|
||||||
const specFailure = container.querySelector('.jasmine-failures');
|
const specFailure = container.querySelector('.jasmine-failures');
|
||||||
|
|
||||||
expect(specFailure.childNodes.length).toEqual(0);
|
expect(specFailure.childNodes.length).toEqual(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('displays the custom pending reason', function() {
|
it('displays the custom pending reason', function() {
|
||||||
|
reportWithSpecStatus({
|
||||||
|
...pendingSpecStatus(),
|
||||||
|
pendingReason: 'my custom pending reason'
|
||||||
|
});
|
||||||
const pendingDetails = container.querySelector(
|
const pendingDetails = container.querySelector(
|
||||||
'.jasmine-summary .jasmine-pending'
|
'.jasmine-summary .jasmine-pending'
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(pendingDetails.innerHTML).toContain('my custom pending reason');
|
expect(pendingDetails.innerHTML).toContain(
|
||||||
|
'PENDING WITH MESSAGE: my custom pending reason'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('indicates that the spec is pending even if there is no reason', function() {
|
||||||
|
reportWithSpecStatus({
|
||||||
|
...pendingSpecStatus(),
|
||||||
|
pendingReason: ''
|
||||||
|
});
|
||||||
|
const pendingDetails = container.querySelector(
|
||||||
|
'.jasmine-summary .jasmine-pending'
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(pendingDetails.innerHTML).toContain('PENDING');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ describe('PrettyPrinter (HTML Dependent)', function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("should print Firefox's wrapped native objects correctly", function() {
|
it("should print Firefox's wrapped native objects correctly", function() {
|
||||||
if (jasmine.getEnv().firefoxVersion) {
|
if (specHelpers.firefoxVersion) {
|
||||||
const pp = jasmineUnderTest.makePrettyPrinter();
|
const pp = jasmineUnderTest.makePrettyPrinter();
|
||||||
let err;
|
let err;
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -78,15 +78,10 @@ describe('npm package', function() {
|
|||||||
|
|
||||||
it('has bootFiles', function() {
|
it('has bootFiles', function() {
|
||||||
expect(this.packagedCore.files.bootFiles).toEqual(['boot0.js', 'boot1.js']);
|
expect(this.packagedCore.files.bootFiles).toEqual(['boot0.js', 'boot1.js']);
|
||||||
expect(this.packagedCore.files.nodeBootFiles).toEqual(['node_boot.js']);
|
|
||||||
|
|
||||||
for (const fileName of this.packagedCore.files.bootFiles) {
|
for (const fileName of this.packagedCore.files.bootFiles) {
|
||||||
expect(fileName).toExistInPath(this.packagedCore.files.bootDir);
|
expect(fileName).toExistInPath(this.packagedCore.files.bootDir);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const fileName of this.packagedCore.files.nodeBootFiles) {
|
|
||||||
expect(fileName).toExistInPath(this.packagedCore.files.bootDir);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('has an imagesDir', function() {
|
it('has an imagesDir', function() {
|
||||||
@@ -118,7 +113,7 @@ describe('npm package', function() {
|
|||||||
const files = fs.readdirSync(path.resolve(this.tmpDir, 'package'));
|
const files = fs.readdirSync(path.resolve(this.tmpDir, 'package'));
|
||||||
files.sort();
|
files.sort();
|
||||||
expect(files).toEqual([
|
expect(files).toEqual([
|
||||||
'MIT.LICENSE',
|
'LICENSE',
|
||||||
'README.md',
|
'README.md',
|
||||||
'images',
|
'images',
|
||||||
'lib',
|
'lib',
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ module.exports = {
|
|||||||
specDir: 'spec',
|
specDir: 'spec',
|
||||||
specFiles: ['**/*[Ss]pec.js', '!npmPackage/**/*'],
|
specFiles: ['**/*[Ss]pec.js', '!npmPackage/**/*'],
|
||||||
helpers: [
|
helpers: [
|
||||||
|
'helpers/init.js',
|
||||||
'helpers/generator.js',
|
'helpers/generator.js',
|
||||||
'helpers/BrowserFlags.js',
|
'helpers/BrowserFlags.js',
|
||||||
'helpers/domHelpers.js',
|
'helpers/domHelpers.js',
|
||||||
@@ -28,16 +29,19 @@ module.exports = {
|
|||||||
random: true,
|
random: true,
|
||||||
browser: {
|
browser: {
|
||||||
name: process.env.JASMINE_BROWSER || 'firefox',
|
name: process.env.JASMINE_BROWSER || 'firefox',
|
||||||
useSauce: process.env.USE_SAUCE === 'true',
|
useRemoteSeleniumGrid: process.env.USE_SAUCE === 'true',
|
||||||
sauce: {
|
remoteSeleniumGrid: {
|
||||||
name: `jasmine-core ${new Date().toISOString()}`,
|
url: 'https://ondemand.saucelabs.com/wd/hub',
|
||||||
os: process.env.SAUCE_OS,
|
|
||||||
browserVersion: process.env.SAUCE_BROWSER_VERSION,
|
browserVersion: process.env.SAUCE_BROWSER_VERSION,
|
||||||
build: `Core ${process.env.TRAVIS_BUILD_NUMBER || 'Ran locally'}`,
|
platformName: process.env.SAUCE_OS,
|
||||||
tags: ['Jasmine-Core'],
|
'sauce:options': {
|
||||||
tunnelIdentifier: process.env.SAUCE_TUNNEL_IDENTIFIER,
|
name: `jasmine-core ${new Date().toISOString()}`,
|
||||||
username: process.env.SAUCE_USERNAME,
|
build: `Core ${process.env.CIRCLE_BUILD_NUM || 'Ran locally'}`,
|
||||||
accessKey: process.env.SAUCE_ACCESS_KEY
|
tags: ['Jasmine-Core'],
|
||||||
|
tunnelIdentifier: process.env.SAUCE_TUNNEL_IDENTIFIER,
|
||||||
|
username: process.env.SAUCE_USERNAME,
|
||||||
|
accessKey: process.env.SAUCE_ACCESS_KEY
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
"npmPackage/**/*[Ss]pec.js"
|
"npmPackage/**/*[Ss]pec.js"
|
||||||
],
|
],
|
||||||
"helpers": [
|
"helpers": [
|
||||||
|
"helpers/init.js",
|
||||||
"helpers/domHelpers.js",
|
"helpers/domHelpers.js",
|
||||||
"helpers/integrationMatchers.js",
|
"helpers/integrationMatchers.js",
|
||||||
"helpers/overrideConsoleLogForCircleCi.js",
|
"helpers/overrideConsoleLogForCircleCi.js",
|
||||||
|
|||||||
@@ -1,16 +0,0 @@
|
|||||||
module.exports = function(jasmineRequire) {
|
|
||||||
const jasmine = jasmineRequire.core(jasmineRequire);
|
|
||||||
|
|
||||||
const env = jasmine.getEnv({ suppressLoadErrors: true });
|
|
||||||
|
|
||||||
const jasmineInterface = jasmineRequire.interface(jasmine, env);
|
|
||||||
|
|
||||||
extend(global, jasmineInterface);
|
|
||||||
|
|
||||||
function extend(destination, source) {
|
|
||||||
for (const property in source) destination[property] = source[property];
|
|
||||||
return destination;
|
|
||||||
}
|
|
||||||
|
|
||||||
return jasmine;
|
|
||||||
};
|
|
||||||
@@ -1,8 +1,32 @@
|
|||||||
getJasmineRequireObj().clearStack = function(j$) {
|
getJasmineRequireObj().clearStack = function(j$) {
|
||||||
const maxInlineCallCount = 10;
|
const maxInlineCallCount = 10;
|
||||||
|
|
||||||
function messageChannelImpl(global, setTimeout) {
|
function browserQueueMicrotaskImpl(global) {
|
||||||
const channel = new global.MessageChannel();
|
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 head = {};
|
||||||
let tail = head;
|
let tail = head;
|
||||||
|
|
||||||
@@ -13,7 +37,7 @@ getJasmineRequireObj().clearStack = function(j$) {
|
|||||||
delete head.task;
|
delete head.task;
|
||||||
|
|
||||||
if (taskRunning) {
|
if (taskRunning) {
|
||||||
global.setTimeout(task, 0);
|
setTimeout(task, 0);
|
||||||
} else {
|
} else {
|
||||||
try {
|
try {
|
||||||
taskRunning = true;
|
taskRunning = true;
|
||||||
@@ -39,29 +63,32 @@ getJasmineRequireObj().clearStack = function(j$) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function getClearStack(global) {
|
function getClearStack(global) {
|
||||||
let currentCallCount = 0;
|
const NODE_JS =
|
||||||
const realSetTimeout = global.setTimeout;
|
global.process &&
|
||||||
const setTimeoutImpl = function clearStack(fn) {
|
global.process.versions &&
|
||||||
Function.prototype.apply.apply(realSetTimeout, [global, [fn, 0]]);
|
typeof global.process.versions.node === 'string';
|
||||||
};
|
|
||||||
|
|
||||||
if (j$.isFunction_(global.setImmediate)) {
|
const SAFARI =
|
||||||
const realSetImmediate = global.setImmediate;
|
global.navigator &&
|
||||||
return function(fn) {
|
/^((?!chrome|android).)*safari/i.test(global.navigator.userAgent);
|
||||||
currentCallCount++;
|
|
||||||
|
|
||||||
if (currentCallCount < maxInlineCallCount) {
|
if (NODE_JS) {
|
||||||
realSetImmediate(fn);
|
// Unlike browsers, Node doesn't require us to do a periodic setTimeout
|
||||||
} else {
|
// so we avoid the overhead.
|
||||||
currentCallCount = 0;
|
return nodeQueueMicrotaskImpl(global);
|
||||||
|
} else if (
|
||||||
setTimeoutImpl(fn);
|
SAFARI ||
|
||||||
}
|
j$.util.isUndefined(global.MessageChannel) /* tests */
|
||||||
};
|
) {
|
||||||
} else if (!j$.util.isUndefined(global.MessageChannel)) {
|
// queueMicrotask is dramatically faster than MessageChannel in Safari,
|
||||||
return messageChannelImpl(global, setTimeoutImpl);
|
// 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 {
|
} else {
|
||||||
return setTimeoutImpl;
|
// MessageChannel is faster than queueMicrotask in supported browsers
|
||||||
|
// other than Safari.
|
||||||
|
return messageChannelImpl(global);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
// Warning: don't add "use strict" to this file. Doing so potentially changes
|
||||||
|
// the behavior of user code that does things like setTimeout("var x = 1;")
|
||||||
|
// while the mock clock is installed.
|
||||||
getJasmineRequireObj().DelayedFunctionScheduler = function(j$) {
|
getJasmineRequireObj().DelayedFunctionScheduler = function(j$) {
|
||||||
function DelayedFunctionScheduler() {
|
function DelayedFunctionScheduler() {
|
||||||
this.scheduledLookup_ = [];
|
this.scheduledLookup_ = [];
|
||||||
@@ -23,6 +26,9 @@ getJasmineRequireObj().DelayedFunctionScheduler = function(j$) {
|
|||||||
) {
|
) {
|
||||||
let f;
|
let f;
|
||||||
if (typeof funcToCall === 'string') {
|
if (typeof funcToCall === 'string') {
|
||||||
|
// setTimeout("some code") and setInterval("some code") are legal, if
|
||||||
|
// not recommended. We don't do that ourselves, but user code might.
|
||||||
|
// This allows such code to work when the mock clock is installed.
|
||||||
f = function() {
|
f = function() {
|
||||||
// eslint-disable-next-line no-eval
|
// eslint-disable-next-line no-eval
|
||||||
return eval(funcToCall);
|
return eval(funcToCall);
|
||||||
|
|||||||
289
src/core/Env.js
289
src/core/Env.js
@@ -46,6 +46,7 @@ getJasmineRequireObj().Env = function(j$) {
|
|||||||
let reporter;
|
let reporter;
|
||||||
let topSuite;
|
let topSuite;
|
||||||
let runner;
|
let runner;
|
||||||
|
let parallelLoadingState = null; // 'specs', 'helpers', or null for non-parallel
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This represents the available options to configure Jasmine.
|
* This represents the available options to configure Jasmine.
|
||||||
@@ -74,6 +75,10 @@ getJasmineRequireObj().Env = function(j$) {
|
|||||||
seed: null,
|
seed: null,
|
||||||
/**
|
/**
|
||||||
* Whether to stop execution of the suite after the first spec failure
|
* Whether to stop execution of the suite after the first spec failure
|
||||||
|
*
|
||||||
|
* <p>In parallel mode, `stopOnSpecFailure` works on a "best effort"
|
||||||
|
* basis. Jasmine will stop execution as soon as practical after a failure
|
||||||
|
* but it might not be immediate.</p>
|
||||||
* @name Configuration#stopOnSpecFailure
|
* @name Configuration#stopOnSpecFailure
|
||||||
* @since 3.9.0
|
* @since 3.9.0
|
||||||
* @type Boolean
|
* @type Boolean
|
||||||
@@ -149,20 +154,14 @@ getJasmineRequireObj().Env = function(j$) {
|
|||||||
|
|
||||||
if (!options.suppressLoadErrors) {
|
if (!options.suppressLoadErrors) {
|
||||||
installGlobalErrors();
|
installGlobalErrors();
|
||||||
globalErrors.pushListener(function loadtimeErrorHandler(
|
globalErrors.pushListener(function loadtimeErrorHandler(error, event) {
|
||||||
message,
|
|
||||||
filename,
|
|
||||||
lineno,
|
|
||||||
colNo,
|
|
||||||
err
|
|
||||||
) {
|
|
||||||
topSuite.result.failedExpectations.push({
|
topSuite.result.failedExpectations.push({
|
||||||
passed: false,
|
passed: false,
|
||||||
globalErrorType: 'load',
|
globalErrorType: 'load',
|
||||||
message: message,
|
message: error ? error.message : event.message,
|
||||||
stack: err && err.stack,
|
stack: error && error.stack,
|
||||||
filename: filename,
|
filename: event && event.filename,
|
||||||
lineno: lineno
|
lineno: event && event.lineno
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -175,6 +174,12 @@ getJasmineRequireObj().Env = function(j$) {
|
|||||||
* @function
|
* @function
|
||||||
*/
|
*/
|
||||||
this.configure = function(configuration) {
|
this.configure = function(configuration) {
|
||||||
|
if (parallelLoadingState) {
|
||||||
|
throw new Error(
|
||||||
|
'Jasmine cannot be configured via Env in parallel mode'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
const booleanProps = [
|
const booleanProps = [
|
||||||
'random',
|
'random',
|
||||||
'failSpecWithNoExpectations',
|
'failSpecWithNoExpectations',
|
||||||
@@ -259,6 +264,49 @@ getJasmineRequireObj().Env = function(j$) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleThrowUnlessFailure = function(passed, result) {
|
||||||
|
if (!passed) {
|
||||||
|
/**
|
||||||
|
* @interface
|
||||||
|
* @name ThrowUnlessFailure
|
||||||
|
* @extends Error
|
||||||
|
* @description Represents a failure of an expectation evaluated with
|
||||||
|
* {@link throwUnless}. Properties of this error are a subset of the
|
||||||
|
* properties of {@link Expectation} and have the same values.
|
||||||
|
* @property {String} matcherName - The name of the matcher that was executed for this expectation.
|
||||||
|
* @property {String} message - The failure message for the expectation.
|
||||||
|
* @property {Boolean} passed - Whether the expectation passed or failed.
|
||||||
|
* @property {Object} expected - If the expectation failed, what was the expected value.
|
||||||
|
* @property {Object} actual - If the expectation failed, what actual value was produced.
|
||||||
|
*/
|
||||||
|
const error = new Error(result.message);
|
||||||
|
error.passed = result.passed;
|
||||||
|
error.message = result.message;
|
||||||
|
error.expected = result.expected;
|
||||||
|
error.actual = result.actual;
|
||||||
|
error.matcherName = result.matcherName;
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const throwUnlessFactory = function(actual, spec) {
|
||||||
|
return j$.Expectation.factory({
|
||||||
|
matchersUtil: runableResources.makeMatchersUtil(),
|
||||||
|
customMatchers: runableResources.customMatchers(),
|
||||||
|
actual: actual,
|
||||||
|
addExpectationResult: handleThrowUnlessFailure
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const throwUnlessAsyncFactory = function(actual, spec) {
|
||||||
|
return j$.Expectation.asyncFactory({
|
||||||
|
matchersUtil: runableResources.makeMatchersUtil(),
|
||||||
|
customAsyncMatchers: runableResources.customAsyncMatchers(),
|
||||||
|
actual: actual,
|
||||||
|
addExpectationResult: handleThrowUnlessFailure
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
// TODO: Unify recordLateError with recordLateExpectation? The extra
|
// TODO: Unify recordLateError with recordLateExpectation? The extra
|
||||||
// diagnostic info added by the latter is probably useful in most cases.
|
// diagnostic info added by the latter is probably useful in most cases.
|
||||||
function recordLateError(error) {
|
function recordLateError(error) {
|
||||||
@@ -380,7 +428,6 @@ getJasmineRequireObj().Env = function(j$) {
|
|||||||
function(e) {
|
function(e) {
|
||||||
(runner.currentRunable() || topSuite).handleException(e);
|
(runner.currentRunable() || topSuite).handleException(e);
|
||||||
};
|
};
|
||||||
options.deprecated = self.deprecated;
|
|
||||||
|
|
||||||
new j$.QueueRunner(options).execute();
|
new j$.QueueRunner(options).execute();
|
||||||
}
|
}
|
||||||
@@ -406,6 +453,7 @@ getJasmineRequireObj().Env = function(j$) {
|
|||||||
* @since 2.0.0
|
* @since 2.0.0
|
||||||
*/
|
*/
|
||||||
this.topSuite = function() {
|
this.topSuite = function() {
|
||||||
|
ensureNonParallel('topSuite');
|
||||||
return topSuite.metadata;
|
return topSuite.metadata;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -415,72 +463,7 @@ getJasmineRequireObj().Env = function(j$) {
|
|||||||
* @see custom_reporter
|
* @see custom_reporter
|
||||||
*/
|
*/
|
||||||
reporter = new j$.ReportDispatcher(
|
reporter = new j$.ReportDispatcher(
|
||||||
[
|
j$.reporterEvents,
|
||||||
/**
|
|
||||||
* `jasmineStarted` is called after all of the specs have been loaded, but just before execution starts.
|
|
||||||
* @function
|
|
||||||
* @name Reporter#jasmineStarted
|
|
||||||
* @param {JasmineStartedInfo} suiteInfo Information about the full Jasmine suite that is being run
|
|
||||||
* @param {Function} [done] Used to specify to Jasmine that this callback is asynchronous and Jasmine should wait until it has been called before moving on.
|
|
||||||
* @returns {} Optionally return a Promise instead of using `done` to cause Jasmine to wait for completion.
|
|
||||||
* @see async
|
|
||||||
*/
|
|
||||||
'jasmineStarted',
|
|
||||||
/**
|
|
||||||
* When the entire suite has finished execution `jasmineDone` is called
|
|
||||||
* @function
|
|
||||||
* @name Reporter#jasmineDone
|
|
||||||
* @param {JasmineDoneInfo} suiteInfo Information about the full Jasmine suite that just finished running.
|
|
||||||
* @param {Function} [done] Used to specify to Jasmine that this callback is asynchronous and Jasmine should wait until it has been called before moving on.
|
|
||||||
* @returns {} Optionally return a Promise instead of using `done` to cause Jasmine to wait for completion.
|
|
||||||
* @see async
|
|
||||||
*/
|
|
||||||
'jasmineDone',
|
|
||||||
/**
|
|
||||||
* `suiteStarted` is invoked when a `describe` starts to run
|
|
||||||
* @function
|
|
||||||
* @name Reporter#suiteStarted
|
|
||||||
* @param {SuiteResult} result Information about the individual {@link describe} being run
|
|
||||||
* @param {Function} [done] Used to specify to Jasmine that this callback is asynchronous and Jasmine should wait until it has been called before moving on.
|
|
||||||
* @returns {} Optionally return a Promise instead of using `done` to cause Jasmine to wait for completion.
|
|
||||||
* @see async
|
|
||||||
*/
|
|
||||||
'suiteStarted',
|
|
||||||
/**
|
|
||||||
* `suiteDone` is invoked when all of the child specs and suites for a given suite have been run
|
|
||||||
*
|
|
||||||
* While jasmine doesn't require any specific functions, not defining a `suiteDone` will make it impossible for a reporter to know when a suite has failures in an `afterAll`.
|
|
||||||
* @function
|
|
||||||
* @name Reporter#suiteDone
|
|
||||||
* @param {SuiteResult} result
|
|
||||||
* @param {Function} [done] Used to specify to Jasmine that this callback is asynchronous and Jasmine should wait until it has been called before moving on.
|
|
||||||
* @returns {} Optionally return a Promise instead of using `done` to cause Jasmine to wait for completion.
|
|
||||||
* @see async
|
|
||||||
*/
|
|
||||||
'suiteDone',
|
|
||||||
/**
|
|
||||||
* `specStarted` is invoked when an `it` starts to run (including associated `beforeEach` functions)
|
|
||||||
* @function
|
|
||||||
* @name Reporter#specStarted
|
|
||||||
* @param {SpecResult} result Information about the individual {@link it} being run
|
|
||||||
* @param {Function} [done] Used to specify to Jasmine that this callback is asynchronous and Jasmine should wait until it has been called before moving on.
|
|
||||||
* @returns {} Optionally return a Promise instead of using `done` to cause Jasmine to wait for completion.
|
|
||||||
* @see async
|
|
||||||
*/
|
|
||||||
'specStarted',
|
|
||||||
/**
|
|
||||||
* `specDone` is invoked when an `it` and its associated `beforeEach` and `afterEach` functions have been run.
|
|
||||||
*
|
|
||||||
* While jasmine doesn't require any specific functions, not defining a `specDone` will make it impossible for a reporter to know when a spec has failed.
|
|
||||||
* @function
|
|
||||||
* @name Reporter#specDone
|
|
||||||
* @param {SpecResult} result
|
|
||||||
* @param {Function} [done] Used to specify to Jasmine that this callback is asynchronous and Jasmine should wait until it has been called before moving on.
|
|
||||||
* @returns {} Optionally return a Promise instead of using `done` to cause Jasmine to wait for completion.
|
|
||||||
* @see async
|
|
||||||
*/
|
|
||||||
'specDone'
|
|
||||||
],
|
|
||||||
function(options) {
|
function(options) {
|
||||||
options.SkipPolicy = j$.NeverSkipPolicy;
|
options.SkipPolicy = j$.NeverSkipPolicy;
|
||||||
return queueRunnerFactory(options);
|
return queueRunnerFactory(options);
|
||||||
@@ -499,21 +482,24 @@ getJasmineRequireObj().Env = function(j$) {
|
|||||||
reportSpecDone
|
reportSpecDone
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.setParallelLoadingState = function(state) {
|
||||||
|
parallelLoadingState = state;
|
||||||
|
};
|
||||||
|
|
||||||
|
this.parallelReset = function() {
|
||||||
|
suiteBuilder.parallelReset();
|
||||||
|
runner.parallelReset();
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Executes the specs.
|
* Executes the specs.
|
||||||
*
|
*
|
||||||
* If called with no parameters or with a falsy value as the first parameter,
|
* If called with no parameter or with a falsy parameter,
|
||||||
* all specs will be executed except those that are excluded by a
|
* all specs will be executed except those that are excluded by a
|
||||||
* [spec filter]{@link Configuration#specFilter} or other mechanism. If the
|
* [spec filter]{@link Configuration#specFilter} or other mechanism. If the
|
||||||
* first parameter is a list of spec/suite IDs, only those specs/suites will
|
* parameter is a list of spec/suite IDs, only those specs/suites will
|
||||||
* be run.
|
* be run.
|
||||||
*
|
*
|
||||||
* Both parameters are optional, but a completion callback is only valid as
|
|
||||||
* the second parameter. To specify a completion callback but not a list of
|
|
||||||
* specs/suites to run, pass null or undefined as the first parameter. The
|
|
||||||
* completion callback is supported for backward compatibility. In most
|
|
||||||
* cases it will be more convenient to use the returned promise instead.
|
|
||||||
*
|
|
||||||
* execute should not be called more than once unless the env has been
|
* execute should not be called more than once unless the env has been
|
||||||
* configured with `{autoCleanClosures: false}`.
|
* configured with `{autoCleanClosures: false}`.
|
||||||
*
|
*
|
||||||
@@ -521,25 +507,26 @@ getJasmineRequireObj().Env = function(j$) {
|
|||||||
* {@link JasmineDoneInfo|overall result} that's passed to a reporter's
|
* {@link JasmineDoneInfo|overall result} that's passed to a reporter's
|
||||||
* `jasmineDone` method, even if the suite did not pass. To determine
|
* `jasmineDone` method, even if the suite did not pass. To determine
|
||||||
* whether the suite passed, check the value that the promise resolves to
|
* whether the suite passed, check the value that the promise resolves to
|
||||||
* or use a {@link Reporter}.
|
* or use a {@link Reporter}. The promise will be rejected in the case of
|
||||||
|
* certain serious errors that prevent execution from starting.
|
||||||
*
|
*
|
||||||
* @name Env#execute
|
* @name Env#execute
|
||||||
* @since 2.0.0
|
* @since 2.0.0
|
||||||
* @function
|
* @function
|
||||||
|
* @async
|
||||||
* @param {(string[])=} runablesToRun IDs of suites and/or specs to run
|
* @param {(string[])=} runablesToRun IDs of suites and/or specs to run
|
||||||
* @param {Function=} onComplete Function that will be called after all specs have run
|
|
||||||
* @return {Promise<JasmineDoneInfo>}
|
* @return {Promise<JasmineDoneInfo>}
|
||||||
*/
|
*/
|
||||||
this.execute = function(runablesToRun, onComplete) {
|
this.execute = async function(runablesToRun) {
|
||||||
installGlobalErrors();
|
installGlobalErrors();
|
||||||
|
|
||||||
return runner.execute(runablesToRun).then(function(jasmineDoneInfo) {
|
if (parallelLoadingState) {
|
||||||
if (onComplete) {
|
validateConfigForParallel();
|
||||||
onComplete();
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return jasmineDoneInfo;
|
const result = await runner.execute(runablesToRun);
|
||||||
});
|
this.cleanup_();
|
||||||
|
return result;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -551,6 +538,10 @@ getJasmineRequireObj().Env = function(j$) {
|
|||||||
* @see custom_reporter
|
* @see custom_reporter
|
||||||
*/
|
*/
|
||||||
this.addReporter = function(reporterToAdd) {
|
this.addReporter = function(reporterToAdd) {
|
||||||
|
if (parallelLoadingState) {
|
||||||
|
throw new Error('Reporters cannot be added via Env in parallel mode');
|
||||||
|
}
|
||||||
|
|
||||||
reporter.addReporter(reporterToAdd);
|
reporter.addReporter(reporterToAdd);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -573,6 +564,10 @@ getJasmineRequireObj().Env = function(j$) {
|
|||||||
* @function
|
* @function
|
||||||
*/
|
*/
|
||||||
this.clearReporters = function() {
|
this.clearReporters = function() {
|
||||||
|
if (parallelLoadingState) {
|
||||||
|
throw new Error('Reporters cannot be removed via Env in parallel mode');
|
||||||
|
}
|
||||||
|
|
||||||
reporter.clearReporters();
|
reporter.clearReporters();
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -672,19 +667,58 @@ getJasmineRequireObj().Env = function(j$) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function ensureNonParallel(method) {
|
||||||
|
if (parallelLoadingState) {
|
||||||
|
throw new Error(`'${method}' is not available in parallel mode`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function ensureNonParallelOrInDescribe(msg) {
|
||||||
|
if (parallelLoadingState && !suiteBuilder.inDescribe()) {
|
||||||
|
throw new Error(msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function ensureNonParallelOrInHelperOrInDescribe(method) {
|
||||||
|
if (parallelLoadingState === 'specs' && !suiteBuilder.inDescribe()) {
|
||||||
|
throw new Error(
|
||||||
|
'In parallel mode, ' +
|
||||||
|
method +
|
||||||
|
' must be in a describe block or in a helper file'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function validateConfigForParallel() {
|
||||||
|
if (!config.random) {
|
||||||
|
throw new Error('Randomization cannot be disabled in parallel mode');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.seed !== null && config.seed !== undefined) {
|
||||||
|
throw new Error('Random seed cannot be set in parallel mode');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
this.describe = function(description, definitionFn) {
|
this.describe = function(description, definitionFn) {
|
||||||
ensureIsNotNested('describe');
|
ensureIsNotNested('describe');
|
||||||
return suiteBuilder.describe(description, definitionFn).metadata;
|
const filename = callerCallerFilename();
|
||||||
|
return suiteBuilder.describe(description, definitionFn, filename)
|
||||||
|
.metadata;
|
||||||
};
|
};
|
||||||
|
|
||||||
this.xdescribe = function(description, definitionFn) {
|
this.xdescribe = function(description, definitionFn) {
|
||||||
ensureIsNotNested('xdescribe');
|
ensureIsNotNested('xdescribe');
|
||||||
return suiteBuilder.xdescribe(description, definitionFn).metadata;
|
const filename = callerCallerFilename();
|
||||||
|
return suiteBuilder.xdescribe(description, definitionFn, filename)
|
||||||
|
.metadata;
|
||||||
};
|
};
|
||||||
|
|
||||||
this.fdescribe = function(description, definitionFn) {
|
this.fdescribe = function(description, definitionFn) {
|
||||||
ensureIsNotNested('fdescribe');
|
ensureIsNotNested('fdescribe');
|
||||||
return suiteBuilder.fdescribe(description, definitionFn).metadata;
|
ensureNonParallel('fdescribe');
|
||||||
|
const filename = callerCallerFilename();
|
||||||
|
return suiteBuilder.fdescribe(description, definitionFn, filename)
|
||||||
|
.metadata;
|
||||||
};
|
};
|
||||||
|
|
||||||
function specResultCallback(spec, result, next) {
|
function specResultCallback(spec, result, next) {
|
||||||
@@ -701,27 +735,31 @@ getJasmineRequireObj().Env = function(j$) {
|
|||||||
function specStarted(spec, suite, next) {
|
function specStarted(spec, suite, next) {
|
||||||
runner.currentSpec = spec;
|
runner.currentSpec = spec;
|
||||||
runableResources.initForRunable(spec.id, suite.id);
|
runableResources.initForRunable(spec.id, suite.id);
|
||||||
reporter.specStarted(spec.result, next);
|
reporter.specStarted(spec.result).then(next);
|
||||||
}
|
}
|
||||||
|
|
||||||
function reportSpecDone(spec, result, next) {
|
function reportSpecDone(spec, result, next) {
|
||||||
spec.reportedDone = true;
|
spec.reportedDone = true;
|
||||||
reporter.specDone(result, next);
|
reporter.specDone(result).then(next);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.it = function(description, fn, timeout) {
|
this.it = function(description, fn, timeout) {
|
||||||
ensureIsNotNested('it');
|
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) {
|
this.xit = function(description, fn, timeout) {
|
||||||
ensureIsNotNested('xit');
|
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) {
|
this.fit = function(description, fn, timeout) {
|
||||||
ensureIsNotNested('fit');
|
ensureIsNotNested('fit');
|
||||||
return suiteBuilder.fit(description, fn, timeout).metadata;
|
ensureNonParallel('fit');
|
||||||
|
const filename = callerCallerFilename();
|
||||||
|
return suiteBuilder.fit(description, fn, timeout, filename).metadata;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -772,42 +810,72 @@ getJasmineRequireObj().Env = function(j$) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
this.expect = function(actual) {
|
this.expect = function(actual) {
|
||||||
if (!runner.currentRunable()) {
|
const runable = runner.currentRunable();
|
||||||
|
|
||||||
|
if (!runable) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
"'expect' was used when there was no current spec, this could be because an asynchronous test timed out"
|
"'expect' was used when there was no current spec, this could be because an asynchronous test timed out"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return runner.currentRunable().expect(actual);
|
return runable.expectationFactory(actual, runable);
|
||||||
};
|
};
|
||||||
|
|
||||||
this.expectAsync = function(actual) {
|
this.expectAsync = function(actual) {
|
||||||
if (!runner.currentRunable()) {
|
const runable = runner.currentRunable();
|
||||||
|
|
||||||
|
if (!runable) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
"'expectAsync' was used when there was no current spec, this could be because an asynchronous test timed out"
|
"'expectAsync' was used when there was no current spec, this could be because an asynchronous test timed out"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return runner.currentRunable().expectAsync(actual);
|
return runable.asyncExpectationFactory(actual, runable);
|
||||||
|
};
|
||||||
|
|
||||||
|
this.throwUnless = function(actual) {
|
||||||
|
const runable = runner.currentRunable();
|
||||||
|
return throwUnlessFactory(actual, runable);
|
||||||
|
};
|
||||||
|
|
||||||
|
this.throwUnlessAsync = function(actual) {
|
||||||
|
const runable = runner.currentRunable();
|
||||||
|
return throwUnlessAsyncFactory(actual, runable);
|
||||||
};
|
};
|
||||||
|
|
||||||
this.beforeEach = function(beforeEachFunction, timeout) {
|
this.beforeEach = function(beforeEachFunction, timeout) {
|
||||||
ensureIsNotNested('beforeEach');
|
ensureIsNotNested('beforeEach');
|
||||||
|
ensureNonParallelOrInHelperOrInDescribe('beforeEach');
|
||||||
suiteBuilder.beforeEach(beforeEachFunction, timeout);
|
suiteBuilder.beforeEach(beforeEachFunction, timeout);
|
||||||
};
|
};
|
||||||
|
|
||||||
this.beforeAll = function(beforeAllFunction, timeout) {
|
this.beforeAll = function(beforeAllFunction, timeout) {
|
||||||
ensureIsNotNested('beforeAll');
|
ensureIsNotNested('beforeAll');
|
||||||
|
// This message is -npm-specific, but currently parallel operation is
|
||||||
|
// only supported via -npm.
|
||||||
|
ensureNonParallelOrInDescribe(
|
||||||
|
"In parallel mode, 'beforeAll' " +
|
||||||
|
'must be in a describe block. Use the globalSetup config ' +
|
||||||
|
'property for exactly-once setup in parallel mode.'
|
||||||
|
);
|
||||||
suiteBuilder.beforeAll(beforeAllFunction, timeout);
|
suiteBuilder.beforeAll(beforeAllFunction, timeout);
|
||||||
};
|
};
|
||||||
|
|
||||||
this.afterEach = function(afterEachFunction, timeout) {
|
this.afterEach = function(afterEachFunction, timeout) {
|
||||||
ensureIsNotNested('afterEach');
|
ensureIsNotNested('afterEach');
|
||||||
|
ensureNonParallelOrInHelperOrInDescribe('afterEach');
|
||||||
suiteBuilder.afterEach(afterEachFunction, timeout);
|
suiteBuilder.afterEach(afterEachFunction, timeout);
|
||||||
};
|
};
|
||||||
|
|
||||||
this.afterAll = function(afterAllFunction, timeout) {
|
this.afterAll = function(afterAllFunction, timeout) {
|
||||||
ensureIsNotNested('afterAll');
|
ensureIsNotNested('afterAll');
|
||||||
|
// This message is -npm-specific, but currently parallel operation is
|
||||||
|
// only supported via -npm.
|
||||||
|
ensureNonParallelOrInDescribe(
|
||||||
|
"In parallel mode, 'afterAll' " +
|
||||||
|
'must be in a describe block. Use the globalTeardown config ' +
|
||||||
|
'property for exactly-once teardown in parallel mode.'
|
||||||
|
);
|
||||||
suiteBuilder.afterAll(afterAllFunction, timeout);
|
suiteBuilder.afterAll(afterAllFunction, timeout);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -861,5 +929,12 @@ getJasmineRequireObj().Env = function(j$) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function callerCallerFilename() {
|
||||||
|
const frames = new j$.StackTrace(new Error()).frames;
|
||||||
|
// frames[3] should always exist except in Jasmine's own tests, which bypass
|
||||||
|
// the global it/describe layer, but don't crash if it doesn't.
|
||||||
|
return frames[3] && frames[3].file;
|
||||||
|
}
|
||||||
|
|
||||||
return Env;
|
return Env;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -44,18 +44,38 @@ getJasmineRequireObj().ExceptionFormatter = function(j$) {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const stackTrace = new j$.StackTrace(error);
|
const lines = this.stack_(error, {
|
||||||
const lines = filterJasmine(stackTrace);
|
messageHandling: omitMessage ? 'omit' : undefined
|
||||||
let result = '';
|
});
|
||||||
|
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);
|
lines.unshift(stackTrace.message);
|
||||||
}
|
}
|
||||||
|
|
||||||
result += formatProperties(error);
|
if (error.cause && error.cause instanceof Error) {
|
||||||
result += lines.join('\n');
|
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) {
|
function filterJasmine(stackTrace) {
|
||||||
@@ -82,7 +102,7 @@ getJasmineRequireObj().ExceptionFormatter = function(j$) {
|
|||||||
const result = {};
|
const result = {};
|
||||||
let empty = true;
|
let empty = true;
|
||||||
|
|
||||||
for (const prop in error) {
|
for (const prop of Object.keys(error)) {
|
||||||
if (ignoredProperties.includes(prop)) {
|
if (ignoredProperties.includes(prop)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,19 +16,24 @@ getJasmineRequireObj().Expectation = function(j$) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add some context for an {@link expect}
|
* Add some context to be included in matcher failures for an
|
||||||
|
* {@link expect|expectation}, so that it can be more easily distinguished
|
||||||
|
* from similar expectations.
|
||||||
* @function
|
* @function
|
||||||
* @name matchers#withContext
|
* @name matchers#withContext
|
||||||
* @since 3.3.0
|
* @since 3.3.0
|
||||||
* @param {String} message - Additional context to show when the matcher fails
|
* @param {String} message - Additional context to show when the matcher fails
|
||||||
* @return {matchers}
|
* @return {matchers}
|
||||||
|
* @example
|
||||||
|
* expect(things[0]).withContext('thing 0').toEqual('a');
|
||||||
|
* expect(things[1]).withContext('thing 1').toEqual('b');
|
||||||
*/
|
*/
|
||||||
Expectation.prototype.withContext = function withContext(message) {
|
Expectation.prototype.withContext = function withContext(message) {
|
||||||
return addFilter(this, new ContextAddingFilter(message));
|
return addFilter(this, new ContextAddingFilter(message));
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Invert the matcher following this {@link expect}
|
* Invert the matcher following this {@link expect|expectation}
|
||||||
* @member
|
* @member
|
||||||
* @name matchers#not
|
* @name matchers#not
|
||||||
* @since 1.3.0
|
* @since 1.3.0
|
||||||
|
|||||||
@@ -6,18 +6,23 @@ getJasmineRequireObj().GlobalErrors = function(j$) {
|
|||||||
let overrideHandler = null,
|
let overrideHandler = null,
|
||||||
onRemoveOverrideHandler = null;
|
onRemoveOverrideHandler = null;
|
||||||
|
|
||||||
function onerror(message, source, lineno, colno, error) {
|
function onBrowserError(event) {
|
||||||
|
dispatchBrowserError(event.error, event);
|
||||||
|
}
|
||||||
|
|
||||||
|
function dispatchBrowserError(error, event) {
|
||||||
if (overrideHandler) {
|
if (overrideHandler) {
|
||||||
overrideHandler(error || message);
|
// See discussion of spyOnGlobalErrorsAsync in base.js
|
||||||
|
overrideHandler(error);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const handler = handlers[handlers.length - 1];
|
const handler = handlers[handlers.length - 1];
|
||||||
|
|
||||||
if (handler) {
|
if (handler) {
|
||||||
handler.apply(null, Array.prototype.slice.call(arguments, 0));
|
handler(error, event);
|
||||||
} else {
|
} else {
|
||||||
throw arguments[0];
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -51,6 +56,7 @@ getJasmineRequireObj().GlobalErrors = function(j$) {
|
|||||||
const handler = handlers[handlers.length - 1];
|
const handler = handlers[handlers.length - 1];
|
||||||
|
|
||||||
if (overrideHandler) {
|
if (overrideHandler) {
|
||||||
|
// See discussion of spyOnGlobalErrorsAsync in base.js
|
||||||
overrideHandler(error);
|
overrideHandler(error);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -94,8 +100,7 @@ getJasmineRequireObj().GlobalErrors = function(j$) {
|
|||||||
this.installOne_('uncaughtException', 'Uncaught exception');
|
this.installOne_('uncaughtException', 'Uncaught exception');
|
||||||
this.installOne_('unhandledRejection', 'Unhandled promise rejection');
|
this.installOne_('unhandledRejection', 'Unhandled promise rejection');
|
||||||
} else {
|
} else {
|
||||||
const originalHandler = global.onerror;
|
global.addEventListener('error', onBrowserError);
|
||||||
global.onerror = onerror;
|
|
||||||
|
|
||||||
const browserRejectionHandler = function browserRejectionHandler(
|
const browserRejectionHandler = function browserRejectionHandler(
|
||||||
event
|
event
|
||||||
@@ -103,31 +108,34 @@ getJasmineRequireObj().GlobalErrors = function(j$) {
|
|||||||
if (j$.isError_(event.reason)) {
|
if (j$.isError_(event.reason)) {
|
||||||
event.reason.jasmineMessage =
|
event.reason.jasmineMessage =
|
||||||
'Unhandled promise rejection: ' + event.reason;
|
'Unhandled promise rejection: ' + event.reason;
|
||||||
global.onerror(event.reason);
|
dispatchBrowserError(event.reason, event);
|
||||||
} else {
|
} else {
|
||||||
global.onerror('Unhandled promise rejection: ' + event.reason);
|
dispatchBrowserError(
|
||||||
|
'Unhandled promise rejection: ' + event.reason,
|
||||||
|
event
|
||||||
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if (global.addEventListener) {
|
global.addEventListener('unhandledrejection', browserRejectionHandler);
|
||||||
global.addEventListener(
|
|
||||||
|
this.uninstall = function uninstall() {
|
||||||
|
global.removeEventListener('error', onBrowserError);
|
||||||
|
global.removeEventListener(
|
||||||
'unhandledrejection',
|
'unhandledrejection',
|
||||||
browserRejectionHandler
|
browserRejectionHandler
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
this.uninstall = function uninstall() {
|
|
||||||
global.onerror = originalHandler;
|
|
||||||
if (global.removeEventListener) {
|
|
||||||
global.removeEventListener(
|
|
||||||
'unhandledrejection',
|
|
||||||
browserRejectionHandler
|
|
||||||
);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// The listener at the top of the stack will be called with two arguments:
|
||||||
|
// the error and the event. Either of them may be falsy.
|
||||||
|
// The error will normally be provided, but will be falsy in the case of
|
||||||
|
// some browser load-time errors. The event will normally be provided in
|
||||||
|
// browsers but will be falsy in Node.
|
||||||
|
// Listeners that are pushed after spec files have been loaded should be
|
||||||
|
// able to just use the error parameter.
|
||||||
this.pushListener = function pushListener(listener) {
|
this.pushListener = function pushListener(listener) {
|
||||||
handlers.push(listener);
|
handlers.push(listener);
|
||||||
};
|
};
|
||||||
|
|||||||
95
src/core/ParallelReportDispatcher.js
Normal file
95
src/core/ParallelReportDispatcher.js
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
getJasmineRequireObj().ParallelReportDispatcher = function(j$) {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @class ParallelReportDispatcher
|
||||||
|
* @implements Reporter
|
||||||
|
* @classdesc A report dispatcher packaged for convenient use from outside jasmine-core.
|
||||||
|
*
|
||||||
|
* This is intended to help packages like `jasmine` (the Jasmine runner for
|
||||||
|
* Node.js) do their own report dispatching in order to support parallel
|
||||||
|
* execution. If you aren't implementing a runner package that supports
|
||||||
|
* parallel execution, this class probably isn't what you're looking for.
|
||||||
|
*
|
||||||
|
* Warning: Do not use ParallelReportDispatcher in the same process that
|
||||||
|
* Jasmine specs run in. Doing so will break Jasmine's error handling.
|
||||||
|
* @param onError {function} Function called when an unhandled exception, unhandled promise rejection, or explicit reporter failure occurs
|
||||||
|
*/
|
||||||
|
function ParallelReportDispatcher(onError, deps = {}) {
|
||||||
|
const ReportDispatcher = deps.ReportDispatcher || j$.ReportDispatcher;
|
||||||
|
const QueueRunner = deps.QueueRunner || j$.QueueRunner;
|
||||||
|
const globalErrors = deps.globalErrors || new j$.GlobalErrors();
|
||||||
|
const dispatcher = new ReportDispatcher(
|
||||||
|
j$.reporterEvents,
|
||||||
|
function(queueRunnerOptions) {
|
||||||
|
queueRunnerOptions = {
|
||||||
|
...queueRunnerOptions,
|
||||||
|
globalErrors,
|
||||||
|
timeout: { setTimeout, clearTimeout },
|
||||||
|
fail: function(error) {
|
||||||
|
// A callback-style async reporter called either done.fail()
|
||||||
|
// or done(anError).
|
||||||
|
if (!error) {
|
||||||
|
error = new Error('A reporter called done.fail()');
|
||||||
|
}
|
||||||
|
|
||||||
|
onError(error);
|
||||||
|
},
|
||||||
|
onException: function(error) {
|
||||||
|
// A reporter method threw an exception or returned a rejected
|
||||||
|
// promise, or there was an unhandled exception or unhandled promise
|
||||||
|
// rejection while an asynchronous reporter method was running.
|
||||||
|
onError(error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
new QueueRunner(queueRunnerOptions).execute();
|
||||||
|
},
|
||||||
|
function(error) {
|
||||||
|
// A reporter called done() more than once.
|
||||||
|
onError(error);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const self = {
|
||||||
|
/**
|
||||||
|
* Adds a reporter to the list of reporters that events will be dispatched to.
|
||||||
|
* @function
|
||||||
|
* @name ParallelReportDispatcher#addReporter
|
||||||
|
* @param {Reporter} reporterToAdd The reporter to be added.
|
||||||
|
* @see custom_reporter
|
||||||
|
*/
|
||||||
|
addReporter: dispatcher.addReporter.bind(dispatcher),
|
||||||
|
/**
|
||||||
|
* Clears all registered reporters.
|
||||||
|
* @function
|
||||||
|
* @name ParallelReportDispatcher#clearReporters
|
||||||
|
*/
|
||||||
|
clearReporters: dispatcher.clearReporters.bind(dispatcher),
|
||||||
|
/**
|
||||||
|
* Installs a global error handler. After this method is called, any
|
||||||
|
* unhandled exceptions or unhandled promise rejections will be passed to
|
||||||
|
* the onError callback that was passed to the constructor.
|
||||||
|
* @function
|
||||||
|
* @name ParallelReportDispatcher#installGlobalErrors
|
||||||
|
*/
|
||||||
|
installGlobalErrors: globalErrors.install.bind(globalErrors),
|
||||||
|
/**
|
||||||
|
* Uninstalls the global error handler.
|
||||||
|
* @function
|
||||||
|
* @name ParallelReportDispatcher#uninstallGlobalErrors
|
||||||
|
*/
|
||||||
|
uninstallGlobalErrors: function() {
|
||||||
|
// late-bind uninstall because it doesn't exist until install is called
|
||||||
|
globalErrors.uninstall(globalErrors);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
for (const eventName of j$.reporterEvents) {
|
||||||
|
self[eventName] = dispatcher[eventName].bind(dispatcher);
|
||||||
|
}
|
||||||
|
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ParallelReportDispatcher;
|
||||||
|
};
|
||||||
@@ -62,15 +62,11 @@ getJasmineRequireObj().QueueRunner = function(j$) {
|
|||||||
if (typeof this.onComplete !== 'function') {
|
if (typeof this.onComplete !== 'function') {
|
||||||
throw new Error('invalid onComplete ' + JSON.stringify(this.onComplete));
|
throw new Error('invalid onComplete ' + JSON.stringify(this.onComplete));
|
||||||
}
|
}
|
||||||
this.deprecated = attrs.deprecated;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QueueRunner.prototype.execute = function() {
|
QueueRunner.prototype.execute = function() {
|
||||||
this.handleFinalError = (message, source, lineno, colno, error) => {
|
this.handleFinalError = (error, event) => {
|
||||||
// Older browsers would send the error as the first parameter. HTML5
|
this.onException(errorOrMsgForGlobalError(error, event));
|
||||||
// specifies the the five parameters above. The error instance should
|
|
||||||
// be preffered, otherwise the call stack would get lost.
|
|
||||||
this.onException(error || message);
|
|
||||||
};
|
};
|
||||||
this.globalErrors.pushListener(this.handleFinalError);
|
this.globalErrors.pushListener(this.handleFinalError);
|
||||||
this.run(0);
|
this.run(0);
|
||||||
@@ -100,10 +96,8 @@ getJasmineRequireObj().QueueRunner = function(j$) {
|
|||||||
this.recordError_(iterativeIndex);
|
this.recordError_(iterativeIndex);
|
||||||
};
|
};
|
||||||
|
|
||||||
function handleError(error) {
|
function handleError(error, event) {
|
||||||
// TODO probably shouldn't next() right away here.
|
onException(errorOrMsgForGlobalError(error, event));
|
||||||
// That makes debugging async failures much more confusing.
|
|
||||||
onException(error);
|
|
||||||
}
|
}
|
||||||
const cleanup = once(() => {
|
const cleanup = once(() => {
|
||||||
if (timeoutId !== void 0) {
|
if (timeoutId !== void 0) {
|
||||||
@@ -259,17 +253,21 @@ getJasmineRequireObj().QueueRunner = function(j$) {
|
|||||||
// on the stack at this point.
|
// on the stack at this point.
|
||||||
if (j$.isAsyncFunction_(fn)) {
|
if (j$.isAsyncFunction_(fn)) {
|
||||||
this.onException(
|
this.onException(
|
||||||
'An asynchronous before/it/after ' +
|
new Error(
|
||||||
'function was defined with the async keyword but also took a ' +
|
'An asynchronous before/it/after ' +
|
||||||
'done callback. Either remove the done callback (recommended) or ' +
|
'function was defined with the async keyword but also took a ' +
|
||||||
'remove the async keyword.'
|
'done callback. Either remove the done callback (recommended) or ' +
|
||||||
|
'remove the async keyword.'
|
||||||
|
)
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
this.onException(
|
this.onException(
|
||||||
'An asynchronous before/it/after ' +
|
new Error(
|
||||||
'function took a done callback but also returned a promise. ' +
|
'An asynchronous before/it/after ' +
|
||||||
'Either remove the done callback (recommended) or change the ' +
|
'function took a done callback but also returned a promise. ' +
|
||||||
'function to not return a promise.'
|
'Either remove the done callback (recommended) or change the ' +
|
||||||
|
'function to not return a promise.'
|
||||||
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -285,5 +283,16 @@ getJasmineRequireObj().QueueRunner = function(j$) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function errorOrMsgForGlobalError(error, event) {
|
||||||
|
// TODO: In cases where error is a string or undefined, the error message
|
||||||
|
// that gets sent to reporters will be `${message} thrown`, which could
|
||||||
|
// be improved to not say "thrown" when the cause wasn't necessarily
|
||||||
|
// an exception or to provide hints about throwing Errors rather than
|
||||||
|
// strings.
|
||||||
|
return (
|
||||||
|
error || (event && event.message) || 'Global error event with no message'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return QueueRunner;
|
return QueueRunner;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,11 +1,13 @@
|
|||||||
getJasmineRequireObj().ReportDispatcher = function(j$) {
|
getJasmineRequireObj().ReportDispatcher = function(j$) {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
function ReportDispatcher(methods, queueRunnerFactory, onLateError) {
|
function ReportDispatcher(methods, queueRunnerFactory, onLateError) {
|
||||||
const dispatchedMethods = methods || [];
|
const dispatchedMethods = methods || [];
|
||||||
|
|
||||||
for (const method of dispatchedMethods) {
|
for (const method of dispatchedMethods) {
|
||||||
this[method] = (function(m) {
|
this[method] = (function(m) {
|
||||||
return function() {
|
return function() {
|
||||||
dispatch(m, arguments);
|
return dispatch(m, arguments);
|
||||||
};
|
};
|
||||||
})(method);
|
})(method);
|
||||||
}
|
}
|
||||||
@@ -31,25 +33,25 @@ getJasmineRequireObj().ReportDispatcher = function(j$) {
|
|||||||
if (reporters.length === 0 && fallbackReporter !== null) {
|
if (reporters.length === 0 && fallbackReporter !== null) {
|
||||||
reporters.push(fallbackReporter);
|
reporters.push(fallbackReporter);
|
||||||
}
|
}
|
||||||
const onComplete = args[args.length - 1];
|
|
||||||
args = Array.from(args).splice(0, args.length - 1);
|
|
||||||
const fns = [];
|
const fns = [];
|
||||||
for (const reporter of reporters) {
|
for (const reporter of reporters) {
|
||||||
addFn(fns, reporter, method, args);
|
addFn(fns, reporter, method, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
queueRunnerFactory({
|
return new Promise(function(resolve) {
|
||||||
queueableFns: fns,
|
queueRunnerFactory({
|
||||||
onComplete: onComplete,
|
queueableFns: fns,
|
||||||
isReporter: true,
|
onComplete: resolve,
|
||||||
onMultipleDone: function() {
|
isReporter: true,
|
||||||
onLateError(
|
onMultipleDone: function() {
|
||||||
new Error(
|
onLateError(
|
||||||
"An asynchronous reporter callback called its 'done' callback " +
|
new Error(
|
||||||
'more than once.'
|
"An asynchronous reporter callback called its 'done' callback " +
|
||||||
)
|
'more than once.'
|
||||||
);
|
)
|
||||||
}
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ getJasmineRequireObj().Runner = function(j$) {
|
|||||||
class Runner {
|
class Runner {
|
||||||
constructor(options) {
|
constructor(options) {
|
||||||
this.topSuite_ = options.topSuite;
|
this.topSuite_ = options.topSuite;
|
||||||
|
// TODO use names that read like getters
|
||||||
this.totalSpecsDefined_ = options.totalSpecsDefined;
|
this.totalSpecsDefined_ = options.totalSpecsDefined;
|
||||||
this.focusedRunables_ = options.focusedRunables;
|
this.focusedRunables_ = options.focusedRunables;
|
||||||
this.runableResources_ = options.runableResources;
|
this.runableResources_ = options.runableResources;
|
||||||
@@ -26,18 +27,17 @@ getJasmineRequireObj().Runner = function(j$) {
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Although execute returns a promise, it isn't async for backwards
|
parallelReset() {
|
||||||
// compatibility: The "Invalid order" exception needs to be propagated
|
this.executedBefore_ = false;
|
||||||
// synchronously from Env#execute.
|
}
|
||||||
// TODO: make this and Env#execute async in the next major release
|
|
||||||
execute(runablesToRun) {
|
async execute(runablesToRun) {
|
||||||
if (this.executedBefore_) {
|
if (this.executedBefore_) {
|
||||||
this.topSuite_.reset();
|
this.topSuite_.reset();
|
||||||
}
|
}
|
||||||
this.executedBefore_ = true;
|
this.executedBefore_ = true;
|
||||||
|
|
||||||
this.hasFailures = false;
|
this.hasFailures = false;
|
||||||
const totalSpecsDefined = this.totalSpecsDefined_();
|
|
||||||
const focusedRunables = this.focusedRunables_();
|
const focusedRunables = this.focusedRunables_();
|
||||||
const config = this.getConfig_();
|
const config = this.getConfig_();
|
||||||
|
|
||||||
@@ -51,7 +51,7 @@ getJasmineRequireObj().Runner = function(j$) {
|
|||||||
|
|
||||||
const order = new j$.Order({
|
const order = new j$.Order({
|
||||||
random: config.random,
|
random: config.random,
|
||||||
seed: config.seed
|
seed: j$.isNumber_(config.seed) ? config.seed + '' : config.seed
|
||||||
});
|
});
|
||||||
|
|
||||||
const processor = new j$.TreeProcessor({
|
const processor = new j$.TreeProcessor({
|
||||||
@@ -76,7 +76,7 @@ getJasmineRequireObj().Runner = function(j$) {
|
|||||||
nodeStart: (suite, next) => {
|
nodeStart: (suite, next) => {
|
||||||
this.currentlyExecutingSuites_.push(suite);
|
this.currentlyExecutingSuites_.push(suite);
|
||||||
this.runableResources_.initForRunable(suite.id, suite.parentSuite.id);
|
this.runableResources_.initForRunable(suite.id, suite.parentSuite.id);
|
||||||
this.reporter_.suiteStarted(suite.result, next);
|
this.reporter_.suiteStarted(suite.result).then(next);
|
||||||
suite.startTimer();
|
suite.startTimer();
|
||||||
},
|
},
|
||||||
nodeComplete: (suite, result, next) => {
|
nodeComplete: (suite, result, next) => {
|
||||||
@@ -114,106 +114,106 @@ 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);
|
this.runableResources_.initForRunable(this.topSuite_.id);
|
||||||
const jasmineTimer = new j$.Timer();
|
const jasmineTimer = new j$.Timer();
|
||||||
jasmineTimer.start();
|
jasmineTimer.start();
|
||||||
|
|
||||||
return new Promise(resolve => {
|
/**
|
||||||
/**
|
* Information passed to the {@link Reporter#jasmineStarted} event.
|
||||||
* Information passed to the {@link Reporter#jasmineStarted} event.
|
* @typedef JasmineStartedInfo
|
||||||
* @typedef JasmineStartedInfo
|
* @property {Int} totalSpecsDefined - The total number of specs defined in this suite. Note that this property is not present when Jasmine is run in parallel mode.
|
||||||
* @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. Note that this property is not present when Jasmine is run in parallel mode.
|
||||||
* @property {Order} order - Information about the ordering (random or not) of this execution of the suite.
|
* @property {Boolean} parallel - Whether Jasmine is being run in parallel mode.
|
||||||
* @since 2.0.0
|
* @since 2.0.0
|
||||||
*/
|
*/
|
||||||
this.reporter_.jasmineStarted(
|
await this.reporter_.jasmineStarted({
|
||||||
{
|
// In parallel mode, the jasmineStarted event is separately dispatched
|
||||||
totalSpecsDefined,
|
// by jasmine-npm. This event only reaches reporters in non-parallel.
|
||||||
order: order
|
totalSpecsDefined,
|
||||||
},
|
order: order,
|
||||||
() => {
|
parallel: false
|
||||||
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);
|
|
||||||
});
|
|
||||||
})();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
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, incompleteCode;
|
||||||
|
|
||||||
|
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';
|
||||||
|
incompleteCode = 'focused';
|
||||||
|
} else if (totalSpecsDefined === 0) {
|
||||||
|
overallStatus = 'incomplete';
|
||||||
|
incompleteReason = 'No specs found';
|
||||||
|
incompleteCode = 'noSpecsFound';
|
||||||
|
} 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 {String} incompleteReason - Human-readable explanation of why the suite was incomplete.
|
||||||
|
* @property {String} incompleteCode - Machine-readable explanation of why the suite was incomplete: 'focused', 'noSpecsFound', or undefined.
|
||||||
|
* @property {Order} order - Information about the ordering (random or not) of this execution of the suite. Note that this property is not present when Jasmine is run in parallel mode.
|
||||||
|
* @property {Int} numWorkers - Number of parallel workers. Note that this property is only present when Jasmine is run in parallel mode.
|
||||||
|
* @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,
|
||||||
|
incompleteCode: incompleteCode,
|
||||||
|
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) {
|
reportSuiteDone_(suite, result, next) {
|
||||||
suite.reportedDone = true;
|
suite.reportedDone = true;
|
||||||
this.reporter_.suiteDone(result, next);
|
this.reporter_.suiteDone(result).then(next);
|
||||||
}
|
}
|
||||||
|
|
||||||
async reportChildrenOfBeforeAllFailure_(suite) {
|
async reportChildrenOfBeforeAllFailure_(suite) {
|
||||||
for (const child of suite.children) {
|
for (const child of suite.children) {
|
||||||
if (child instanceof j$.Suite) {
|
if (child instanceof j$.Suite) {
|
||||||
await new Promise(resolve => {
|
await this.reporter_.suiteStarted(child.result);
|
||||||
this.reporter_.suiteStarted(child.result, resolve);
|
|
||||||
});
|
|
||||||
await this.reportChildrenOfBeforeAllFailure_(child);
|
await this.reportChildrenOfBeforeAllFailure_(child);
|
||||||
|
|
||||||
// Marking the suite passed is consistent with how suites that
|
// Marking the suite passed is consistent with how suites that
|
||||||
// contain failed specs but no suite-level failures are reported.
|
// contain failed specs but no suite-level failures are reported.
|
||||||
child.result.status = 'passed';
|
child.result.status = 'passed';
|
||||||
|
|
||||||
await new Promise(resolve => {
|
await this.reporter_.suiteDone(child.result);
|
||||||
this.reporter_.suiteDone(child.result, resolve);
|
|
||||||
});
|
|
||||||
} else {
|
} else {
|
||||||
/* a spec */
|
/* a spec */
|
||||||
await new Promise(resolve => {
|
await this.reporter_.specStarted(child.result);
|
||||||
this.reporter_.specStarted(child.result, resolve);
|
|
||||||
});
|
|
||||||
|
|
||||||
child.addExpectationResult(
|
child.addExpectationResult(
|
||||||
false,
|
false,
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ getJasmineRequireObj().Spec = function(j$) {
|
|||||||
this.asyncExpectationFactory = attrs.asyncExpectationFactory;
|
this.asyncExpectationFactory = attrs.asyncExpectationFactory;
|
||||||
this.resultCallback = attrs.resultCallback || function() {};
|
this.resultCallback = attrs.resultCallback || function() {};
|
||||||
this.id = attrs.id;
|
this.id = attrs.id;
|
||||||
|
this.filename = attrs.filename;
|
||||||
|
this.parentSuiteId = attrs.parentSuiteId;
|
||||||
this.description = attrs.description || '';
|
this.description = attrs.description || '';
|
||||||
this.queueableFn = attrs.queueableFn;
|
this.queueableFn = attrs.queueableFn;
|
||||||
this.beforeAndAfterFns =
|
this.beforeAndAfterFns =
|
||||||
@@ -37,35 +39,7 @@ getJasmineRequireObj().Spec = function(j$) {
|
|||||||
this.exclude();
|
this.exclude();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
this.reset();
|
||||||
* @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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Spec.prototype.addExpectationResult = function(passed, data, isError) {
|
Spec.prototype.addExpectationResult = function(passed, data, isError) {
|
||||||
@@ -96,14 +70,6 @@ getJasmineRequireObj().Spec = function(j$) {
|
|||||||
this.result.properties[key] = value;
|
this.result.properties[key] = value;
|
||||||
};
|
};
|
||||||
|
|
||||||
Spec.prototype.expect = function(actual) {
|
|
||||||
return this.expectationFactory(actual, this);
|
|
||||||
};
|
|
||||||
|
|
||||||
Spec.prototype.expectAsync = function(actual) {
|
|
||||||
return this.asyncExpectationFactory(actual, this);
|
|
||||||
};
|
|
||||||
|
|
||||||
Spec.prototype.execute = function(
|
Spec.prototype.execute = function(
|
||||||
queueRunnerFactory,
|
queueRunnerFactory,
|
||||||
onComplete,
|
onComplete,
|
||||||
@@ -175,14 +141,33 @@ getJasmineRequireObj().Spec = function(j$) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
Spec.prototype.reset = function() {
|
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 = {
|
this.result = {
|
||||||
id: this.id,
|
id: this.id,
|
||||||
description: this.description,
|
description: this.description,
|
||||||
fullName: this.getFullName(),
|
fullName: this.getFullName(),
|
||||||
|
parentSuiteId: this.parentSuiteId,
|
||||||
|
filename: this.filename,
|
||||||
failedExpectations: [],
|
failedExpectations: [],
|
||||||
passedExpectations: [],
|
passedExpectations: [],
|
||||||
deprecationWarnings: [],
|
deprecationWarnings: [],
|
||||||
pendingReason: this.excludeMessage,
|
pendingReason: this.excludeMessage || '',
|
||||||
duration: null,
|
duration: null,
|
||||||
properties: null,
|
properties: null,
|
||||||
debugLogs: null
|
debugLogs: null
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ getJasmineRequireObj().StackTrace = function(j$) {
|
|||||||
// e.g. " at /some/path:4320:20
|
// e.g. " at /some/path:4320:20
|
||||||
{ re: /\s*at (.+)$/, fileLineColIx: 1, style: 'v8' },
|
{ re: /\s*at (.+)$/, fileLineColIx: 1, style: 'v8' },
|
||||||
|
|
||||||
// PhantomJS on OS X, Safari, Firefox
|
// Safari, most Firefox stack frames
|
||||||
// e.g. "run@http://localhost:8888/__jasmine__/jasmine.js:4320:27"
|
// e.g. "run@http://localhost:8888/__jasmine__/jasmine.js:4320:27"
|
||||||
// or "http://localhost:8888/__jasmine__/jasmine.js:4320:27"
|
// or "http://localhost:8888/__jasmine__/jasmine.js:4320:27"
|
||||||
{
|
{
|
||||||
@@ -40,6 +40,15 @@ getJasmineRequireObj().StackTrace = function(j$) {
|
|||||||
fnIx: 2,
|
fnIx: 2,
|
||||||
fileLineColIx: 3,
|
fileLineColIx: 3,
|
||||||
style: 'webkit'
|
style: 'webkit'
|
||||||
|
},
|
||||||
|
|
||||||
|
// Some Firefox stack frames when the developer tools are open
|
||||||
|
// e.g. "promise callback*specStarted@http://localhost:8888/__jasmine__/jasmine.js:1880:41"
|
||||||
|
{
|
||||||
|
re: /^^(?:((?:promise callback|[^\s]+ handler)\*([^@\s]+)@)|@)?([^\s]+)$/,
|
||||||
|
fnIx: 2,
|
||||||
|
fileLineColIx: 3,
|
||||||
|
style: 'webkit'
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ getJasmineRequireObj().Suite = function(j$) {
|
|||||||
this.id = attrs.id;
|
this.id = attrs.id;
|
||||||
this.parentSuite = attrs.parentSuite;
|
this.parentSuite = attrs.parentSuite;
|
||||||
this.description = attrs.description;
|
this.description = attrs.description;
|
||||||
|
this.reportedParentSuiteId = attrs.reportedParentSuiteId;
|
||||||
|
this.filename = attrs.filename;
|
||||||
this.expectationFactory = attrs.expectationFactory;
|
this.expectationFactory = attrs.expectationFactory;
|
||||||
this.asyncExpectationFactory = attrs.asyncExpectationFactory;
|
this.asyncExpectationFactory = attrs.asyncExpectationFactory;
|
||||||
this.throwOnExpectationFailure = !!attrs.throwOnExpectationFailure;
|
this.throwOnExpectationFailure = !!attrs.throwOnExpectationFailure;
|
||||||
@@ -26,14 +28,6 @@ getJasmineRequireObj().Suite = function(j$) {
|
|||||||
this.result.properties[key] = value;
|
this.result.properties[key] = value;
|
||||||
};
|
};
|
||||||
|
|
||||||
Suite.prototype.expect = function(actual) {
|
|
||||||
return this.expectationFactory(actual, this);
|
|
||||||
};
|
|
||||||
|
|
||||||
Suite.prototype.expectAsync = function(actual) {
|
|
||||||
return this.asyncExpectationFactory(actual, this);
|
|
||||||
};
|
|
||||||
|
|
||||||
Suite.prototype.getFullName = function() {
|
Suite.prototype.getFullName = function() {
|
||||||
const fullName = [];
|
const fullName = [];
|
||||||
for (
|
for (
|
||||||
@@ -106,9 +100,11 @@ getJasmineRequireObj().Suite = function(j$) {
|
|||||||
Suite.prototype.reset = function() {
|
Suite.prototype.reset = function() {
|
||||||
/**
|
/**
|
||||||
* @typedef SuiteResult
|
* @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} 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} 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[]} 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 {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.
|
* @property {String} status - Once the suite has completed, this string represents the pass/fail status of this suite.
|
||||||
@@ -120,6 +116,8 @@ getJasmineRequireObj().Suite = function(j$) {
|
|||||||
id: this.id,
|
id: this.id,
|
||||||
description: this.description,
|
description: this.description,
|
||||||
fullName: this.getFullName(),
|
fullName: this.getFullName(),
|
||||||
|
parentSuiteId: this.reportedParentSuiteId,
|
||||||
|
filename: this.filename,
|
||||||
failedExpectations: [],
|
failedExpectations: [],
|
||||||
deprecationWarnings: [],
|
deprecationWarnings: [],
|
||||||
duration: null,
|
duration: null,
|
||||||
@@ -132,6 +130,10 @@ getJasmineRequireObj().Suite = function(j$) {
|
|||||||
this.reportedDone = false;
|
this.reportedDone = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Suite.prototype.removeChildren = function() {
|
||||||
|
this.children = [];
|
||||||
|
};
|
||||||
|
|
||||||
Suite.prototype.addChild = function(child) {
|
Suite.prototype.addChild = function(child) {
|
||||||
this.children.push(child);
|
this.children.push(child);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -22,9 +22,20 @@ getJasmineRequireObj().SuiteBuilder = function(j$) {
|
|||||||
this.focusedRunables = [];
|
this.focusedRunables = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
describe(description, definitionFn) {
|
inDescribe() {
|
||||||
|
return this.currentDeclarationSuite_ !== this.topSuite;
|
||||||
|
}
|
||||||
|
|
||||||
|
parallelReset() {
|
||||||
|
this.topSuite.removeChildren();
|
||||||
|
this.topSuite.reset();
|
||||||
|
this.totalSpecsDefined = 0;
|
||||||
|
this.focusedRunables = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
describe(description, definitionFn, filename) {
|
||||||
ensureIsFunction(definitionFn, 'describe');
|
ensureIsFunction(definitionFn, 'describe');
|
||||||
const suite = this.suiteFactory_(description);
|
const suite = this.suiteFactory_(description, filename);
|
||||||
if (definitionFn.length > 0) {
|
if (definitionFn.length > 0) {
|
||||||
throw new Error('describe does not expect any arguments');
|
throw new Error('describe does not expect any arguments');
|
||||||
}
|
}
|
||||||
@@ -32,17 +43,12 @@ getJasmineRequireObj().SuiteBuilder = function(j$) {
|
|||||||
suite.exclude();
|
suite.exclude();
|
||||||
}
|
}
|
||||||
this.addSpecsToSuite_(suite, definitionFn);
|
this.addSpecsToSuite_(suite, definitionFn);
|
||||||
if (suite.parentSuite && !suite.children.length) {
|
|
||||||
throw new Error(
|
|
||||||
`describe with no children (describe() or it()): ${suite.getFullName()}`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return suite;
|
return suite;
|
||||||
}
|
}
|
||||||
|
|
||||||
fdescribe(description, definitionFn) {
|
fdescribe(description, definitionFn, filename) {
|
||||||
ensureIsFunction(definitionFn, 'fdescribe');
|
ensureIsFunction(definitionFn, 'fdescribe');
|
||||||
const suite = this.suiteFactory_(description);
|
const suite = this.suiteFactory_(description, filename);
|
||||||
suite.isFocused = true;
|
suite.isFocused = true;
|
||||||
|
|
||||||
this.focusedRunables.push(suite.id);
|
this.focusedRunables.push(suite.id);
|
||||||
@@ -52,37 +58,37 @@ getJasmineRequireObj().SuiteBuilder = function(j$) {
|
|||||||
return suite;
|
return suite;
|
||||||
}
|
}
|
||||||
|
|
||||||
xdescribe(description, definitionFn) {
|
xdescribe(description, definitionFn, filename) {
|
||||||
ensureIsFunction(definitionFn, 'xdescribe');
|
ensureIsFunction(definitionFn, 'xdescribe');
|
||||||
const suite = this.suiteFactory_(description);
|
const suite = this.suiteFactory_(description, filename);
|
||||||
suite.exclude();
|
suite.exclude();
|
||||||
this.addSpecsToSuite_(suite, definitionFn);
|
this.addSpecsToSuite_(suite, definitionFn);
|
||||||
|
|
||||||
return suite;
|
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() sometimes doesn't have a fn argument, so only check the type if
|
||||||
// it's given.
|
// it's given.
|
||||||
if (arguments.length > 1 && typeof fn !== 'undefined') {
|
if (arguments.length > 1 && typeof fn !== 'undefined') {
|
||||||
ensureIsFunctionOrAsync(fn, 'it');
|
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
|
// xit(), like it(), doesn't always have a fn argument, so only check the
|
||||||
// type when needed.
|
// type when needed.
|
||||||
if (arguments.length > 1 && typeof fn !== 'undefined') {
|
if (arguments.length > 1 && typeof fn !== 'undefined') {
|
||||||
ensureIsFunctionOrAsync(fn, 'xit');
|
ensureIsFunctionOrAsync(fn, 'xit');
|
||||||
}
|
}
|
||||||
const spec = this.it_(description, fn, timeout);
|
const spec = this.it_(description, fn, timeout, filename);
|
||||||
spec.exclude('Temporarily disabled with xit');
|
spec.exclude('Temporarily disabled with xit');
|
||||||
return spec;
|
return spec;
|
||||||
}
|
}
|
||||||
|
|
||||||
fit(description, fn, timeout) {
|
fit(description, fn, timeout, filename) {
|
||||||
// Unlike it and xit, the function is required because it doesn't make
|
// Unlike it and xit, the function is required because it doesn't make
|
||||||
// sense to focus on nothing.
|
// sense to focus on nothing.
|
||||||
ensureIsFunctionOrAsync(fn, 'fit');
|
ensureIsFunctionOrAsync(fn, 'fit');
|
||||||
@@ -90,7 +96,7 @@ getJasmineRequireObj().SuiteBuilder = function(j$) {
|
|||||||
if (timeout) {
|
if (timeout) {
|
||||||
j$.util.validateTimeout(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.currentDeclarationSuite_.addChild(spec);
|
||||||
this.focusedRunables.push(spec.id);
|
this.focusedRunables.push(spec.id);
|
||||||
this.unfocusAncestor_();
|
this.unfocusAncestor_();
|
||||||
@@ -150,12 +156,12 @@ getJasmineRequireObj().SuiteBuilder = function(j$) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
it_(description, fn, timeout) {
|
it_(description, fn, timeout, filename) {
|
||||||
if (timeout) {
|
if (timeout) {
|
||||||
j$.util.validateTimeout(timeout);
|
j$.util.validateTimeout(timeout);
|
||||||
}
|
}
|
||||||
|
|
||||||
const spec = this.specFactory_(description, fn, timeout);
|
const spec = this.specFactory_(description, fn, timeout, filename);
|
||||||
if (this.currentDeclarationSuite_.markedExcluding) {
|
if (this.currentDeclarationSuite_.markedExcluding) {
|
||||||
spec.exclude();
|
spec.exclude();
|
||||||
}
|
}
|
||||||
@@ -164,12 +170,17 @@ getJasmineRequireObj().SuiteBuilder = function(j$) {
|
|||||||
return spec;
|
return spec;
|
||||||
}
|
}
|
||||||
|
|
||||||
suiteFactory_(description) {
|
suiteFactory_(description, filename) {
|
||||||
const config = this.env_.configuration();
|
const config = this.env_.configuration();
|
||||||
|
const parentSuite = this.currentDeclarationSuite_;
|
||||||
|
const reportedParentSuiteId =
|
||||||
|
parentSuite === this.topSuite ? null : parentSuite.id;
|
||||||
return new j$.Suite({
|
return new j$.Suite({
|
||||||
id: 'suite' + this.nextSuiteId_++,
|
id: 'suite' + this.nextSuiteId_++,
|
||||||
description,
|
description,
|
||||||
parentSuite: this.currentDeclarationSuite_,
|
filename,
|
||||||
|
parentSuite,
|
||||||
|
reportedParentSuiteId,
|
||||||
timer: new j$.Timer(),
|
timer: new j$.Timer(),
|
||||||
expectationFactory: this.expectationFactory_,
|
expectationFactory: this.expectationFactory_,
|
||||||
asyncExpectationFactory: this.suiteAsyncExpectationFactory_,
|
asyncExpectationFactory: this.suiteAsyncExpectationFactory_,
|
||||||
@@ -183,22 +194,33 @@ getJasmineRequireObj().SuiteBuilder = function(j$) {
|
|||||||
const parentSuite = this.currentDeclarationSuite_;
|
const parentSuite = this.currentDeclarationSuite_;
|
||||||
parentSuite.addChild(suite);
|
parentSuite.addChild(suite);
|
||||||
this.currentDeclarationSuite_ = suite;
|
this.currentDeclarationSuite_ = suite;
|
||||||
|
let threw = false;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
definitionFn();
|
definitionFn();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
suite.handleException(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;
|
this.currentDeclarationSuite_ = parentSuite;
|
||||||
}
|
}
|
||||||
|
|
||||||
specFactory_(description, fn, timeout) {
|
specFactory_(description, fn, timeout, filename) {
|
||||||
this.totalSpecsDefined++;
|
this.totalSpecsDefined++;
|
||||||
const config = this.env_.configuration();
|
const config = this.env_.configuration();
|
||||||
const suite = this.currentDeclarationSuite_;
|
const suite = this.currentDeclarationSuite_;
|
||||||
|
const parentSuiteId = suite === this.topSuite ? null : suite.id;
|
||||||
const spec = new j$.Spec({
|
const spec = new j$.Spec({
|
||||||
id: 'spec' + this.nextSpecId_++,
|
id: 'spec' + this.nextSpecId_++,
|
||||||
|
filename,
|
||||||
|
parentSuiteId,
|
||||||
beforeAndAfterFns: beforeAndAfterFns(suite),
|
beforeAndAfterFns: beforeAndAfterFns(suite),
|
||||||
expectationFactory: this.expectationFactory_,
|
expectationFactory: this.expectationFactory_,
|
||||||
asyncExpectationFactory: this.specAsyncExpectationFactory_,
|
asyncExpectationFactory: this.specAsyncExpectationFactory_,
|
||||||
|
|||||||
@@ -5,16 +5,35 @@ getJasmineRequireObj().Timer = function() {
|
|||||||
};
|
};
|
||||||
})(Date);
|
})(Date);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @class Timer
|
||||||
|
* @classdesc Tracks elapsed time
|
||||||
|
* @example
|
||||||
|
* const timer = new jasmine.Timer();
|
||||||
|
* timer.start();
|
||||||
|
* const elapsed = timer.elapsed()
|
||||||
|
*/
|
||||||
function Timer(options) {
|
function Timer(options) {
|
||||||
options = options || {};
|
options = options || {};
|
||||||
|
|
||||||
const now = options.now || defaultNow;
|
const now = options.now || defaultNow;
|
||||||
let startTime;
|
let startTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Starts the timer.
|
||||||
|
* @function
|
||||||
|
* @name Timer#start
|
||||||
|
*/
|
||||||
this.start = function() {
|
this.start = function() {
|
||||||
startTime = now();
|
startTime = now();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines the time since the timer was started.
|
||||||
|
* @function
|
||||||
|
* @name Timer#elapsed
|
||||||
|
* @returns {number} Elapsed time in milliseconds, or NaN if the timer has not been started
|
||||||
|
*/
|
||||||
this.elapsed = function() {
|
this.elapsed = function() {
|
||||||
return now() - startTime;
|
return now() - startTime;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ getJasmineRequireObj().TreeProcessor = function() {
|
|||||||
return stats;
|
return stats;
|
||||||
};
|
};
|
||||||
|
|
||||||
this.execute = function(done) {
|
this.execute = async function() {
|
||||||
if (!processed) {
|
if (!processed) {
|
||||||
this.processTree();
|
this.processTree();
|
||||||
}
|
}
|
||||||
@@ -38,16 +38,18 @@ getJasmineRequireObj().TreeProcessor = function() {
|
|||||||
|
|
||||||
const childFns = wrapChildren(tree, 0);
|
const childFns = wrapChildren(tree, 0);
|
||||||
|
|
||||||
queueRunnerFactory({
|
await new Promise(function(resolve) {
|
||||||
queueableFns: childFns,
|
queueRunnerFactory({
|
||||||
userContext: tree.sharedUserContext(),
|
queueableFns: childFns,
|
||||||
onException: function() {
|
userContext: tree.sharedUserContext(),
|
||||||
tree.handleException.apply(tree, arguments);
|
onException: function() {
|
||||||
},
|
tree.handleException.apply(tree, arguments);
|
||||||
onComplete: done,
|
},
|
||||||
onMultipleDone: tree.onMultipleDone
|
onComplete: resolve,
|
||||||
? tree.onMultipleDone.bind(tree)
|
onMultipleDone: tree.onMultipleDone
|
||||||
: null
|
? tree.onMultipleDone.bind(tree)
|
||||||
|
: null
|
||||||
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
119
src/core/base.js
119
src/core/base.js
@@ -224,9 +224,10 @@ getJasmineRequireObj().base = function(j$, jasmineGlobal) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get an {@link AsymmetricEqualityTester}, usable in any {@link matchers|matcher} that uses Jasmine's equality (e.g. {@link matchers#toEqual|toEqual}, {@link matchers#toContain|toContain}, or {@link matchers#toHaveBeenCalledWith|toHaveBeenCalledWith}),
|
* Get an {@link AsymmetricEqualityTester} that will succeed if the actual
|
||||||
* that will succeed if the actual value being compared is an instance of the specified class/constructor.
|
* value being compared is an instance of the specified class/constructor.
|
||||||
* @name jasmine.any
|
* @name asymmetricEqualityTesters.any
|
||||||
|
* @emittedName jasmine.any
|
||||||
* @since 1.3.0
|
* @since 1.3.0
|
||||||
* @function
|
* @function
|
||||||
* @param {Constructor} clazz - The constructor to check against.
|
* @param {Constructor} clazz - The constructor to check against.
|
||||||
@@ -236,9 +237,10 @@ getJasmineRequireObj().base = function(j$, jasmineGlobal) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get an {@link AsymmetricEqualityTester}, usable in any {@link matchers|matcher} that uses Jasmine's equality (e.g. {@link matchers#toEqual|toEqual}, {@link matchers#toContain|toContain}, or {@link matchers#toHaveBeenCalledWith|toHaveBeenCalledWith}),
|
* Get an {@link AsymmetricEqualityTester} that will succeed if the actual
|
||||||
* that will succeed if the actual value being compared is not `null` and not `undefined`.
|
* value being compared is not `null` and not `undefined`.
|
||||||
* @name jasmine.anything
|
* @name asymmetricEqualityTesters.anything
|
||||||
|
* @emittedName jasmine.anything
|
||||||
* @since 2.2.0
|
* @since 2.2.0
|
||||||
* @function
|
* @function
|
||||||
*/
|
*/
|
||||||
@@ -247,9 +249,10 @@ getJasmineRequireObj().base = function(j$, jasmineGlobal) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get an {@link AsymmetricEqualityTester}, usable in any {@link matchers|matcher} that uses Jasmine's equality (e.g. {@link matchers#toEqual|toEqual}, {@link matchers#toContain|toContain}, or {@link matchers#toHaveBeenCalledWith|toHaveBeenCalledWith}),
|
* Get an {@link AsymmetricEqualityTester} that will succeed if the actual
|
||||||
* that will succeed if the actual value being compared is `true` or anything truthy.
|
* value being compared is `true` or anything truthy.
|
||||||
* @name jasmine.truthy
|
* @name asymmetricEqualityTesters.truthy
|
||||||
|
* @emittedName jasmine.truthy
|
||||||
* @since 3.1.0
|
* @since 3.1.0
|
||||||
* @function
|
* @function
|
||||||
*/
|
*/
|
||||||
@@ -258,9 +261,11 @@ getJasmineRequireObj().base = function(j$, jasmineGlobal) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get an {@link AsymmetricEqualityTester}, usable in any {@link matchers|matcher} that uses Jasmine's equality (e.g. {@link matchers#toEqual|toEqual}, {@link matchers#toContain|toContain}, or {@link matchers#toHaveBeenCalledWith|toHaveBeenCalledWith}),
|
* Get an {@link AsymmetricEqualityTester} that will succeed if the actual
|
||||||
* that will succeed if the actual value being compared is `null`, `undefined`, `0`, `false` or anything falsey.
|
* value being compared is `null`, `undefined`, `0`, `false` or anything
|
||||||
* @name jasmine.falsy
|
* falsy.
|
||||||
|
* @name asymmetricEqualityTesters.falsy
|
||||||
|
* @emittedName jasmine.falsy
|
||||||
* @since 3.1.0
|
* @since 3.1.0
|
||||||
* @function
|
* @function
|
||||||
*/
|
*/
|
||||||
@@ -269,9 +274,10 @@ getJasmineRequireObj().base = function(j$, jasmineGlobal) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get an {@link AsymmetricEqualityTester}, usable in any {@link matchers|matcher} that uses Jasmine's equality (e.g. {@link matchers#toEqual|toEqual}, {@link matchers#toContain|toContain}, or {@link matchers#toHaveBeenCalledWith|toHaveBeenCalledWith}),
|
* Get an {@link AsymmetricEqualityTester} that will succeed if the actual
|
||||||
* that will succeed if the actual value being compared is empty.
|
* value being compared is empty.
|
||||||
* @name jasmine.empty
|
* @name asymmetricEqualityTesters.empty
|
||||||
|
* @emittedName jasmine.empty
|
||||||
* @since 3.1.0
|
* @since 3.1.0
|
||||||
* @function
|
* @function
|
||||||
*/
|
*/
|
||||||
@@ -280,10 +286,10 @@ getJasmineRequireObj().base = function(j$, jasmineGlobal) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get an {@link AsymmetricEqualityTester}, usable in any {@link matchers|matcher}
|
* Get an {@link AsymmetricEqualityTester} that passes if the actual value is
|
||||||
* that passes if the actual value is the same as the sample as determined
|
* the same as the sample as determined by the `===` operator.
|
||||||
* by the `===` operator.
|
* @name asymmetricEqualityTesters.is
|
||||||
* @name jasmine.is
|
* @emittedName jasmine.is
|
||||||
* @function
|
* @function
|
||||||
* @param {Object} sample - The value to compare the actual to.
|
* @param {Object} sample - The value to compare the actual to.
|
||||||
*/
|
*/
|
||||||
@@ -292,9 +298,10 @@ getJasmineRequireObj().base = function(j$, jasmineGlobal) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get an {@link AsymmetricEqualityTester}, usable in any {@link matchers|matcher} that uses Jasmine's equality (e.g. {@link matchers#toEqual|toEqual}, {@link matchers#toContain|toContain}, or {@link matchers#toHaveBeenCalledWith|toHaveBeenCalledWith}),
|
* Get an {@link AsymmetricEqualityTester} that will succeed if the actual
|
||||||
* that will succeed if the actual value being compared is not empty.
|
* value being compared is not empty.
|
||||||
* @name jasmine.notEmpty
|
* @name asymmetricEqualityTesters.notEmpty
|
||||||
|
* @emittedName jasmine.notEmpty
|
||||||
* @since 3.1.0
|
* @since 3.1.0
|
||||||
* @function
|
* @function
|
||||||
*/
|
*/
|
||||||
@@ -303,9 +310,10 @@ getJasmineRequireObj().base = function(j$, jasmineGlobal) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get an {@link AsymmetricEqualityTester}, usable in any {@link matchers|matcher} that uses Jasmine's equality (e.g. {@link matchers#toEqual|toEqual}, {@link matchers#toContain|toContain}, or {@link matchers#toHaveBeenCalledWith|toHaveBeenCalledWith}),
|
* Get an {@link AsymmetricEqualityTester} that will succeed if the actual
|
||||||
* that will succeed if the actual value being compared contains at least the keys and values.
|
* value being compared contains at least the specified keys and values.
|
||||||
* @name jasmine.objectContaining
|
* @name asymmetricEqualityTesters.objectContaining
|
||||||
|
* @emittedName jasmine.objectContaining
|
||||||
* @since 1.3.0
|
* @since 1.3.0
|
||||||
* @function
|
* @function
|
||||||
* @param {Object} sample - The subset of properties that _must_ be in the actual.
|
* @param {Object} sample - The subset of properties that _must_ be in the actual.
|
||||||
@@ -315,9 +323,10 @@ getJasmineRequireObj().base = function(j$, jasmineGlobal) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get an {@link AsymmetricEqualityTester}, usable in any {@link matchers|matcher} that uses Jasmine's equality (e.g. {@link matchers#toEqual|toEqual}, {@link matchers#toContain|toContain}, or {@link matchers#toHaveBeenCalledWith|toHaveBeenCalledWith}),
|
* Get an {@link AsymmetricEqualityTester} that will succeed if the actual
|
||||||
* that will succeed if the actual value is a `String` that matches the `RegExp` or `String`.
|
* value is a `String` that matches the `RegExp` or `String`.
|
||||||
* @name jasmine.stringMatching
|
* @name asymmetricEqualityTesters.stringMatching
|
||||||
|
* @emittedName jasmine.stringMatching
|
||||||
* @since 2.2.0
|
* @since 2.2.0
|
||||||
* @function
|
* @function
|
||||||
* @param {RegExp|String} expected
|
* @param {RegExp|String} expected
|
||||||
@@ -327,9 +336,10 @@ getJasmineRequireObj().base = function(j$, jasmineGlobal) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get an {@link AsymmetricEqualityTester}, usable in any {@link matchers|matcher} that uses Jasmine's equality (e.g. {@link matchers#toEqual|toEqual}, {@link matchers#toContain|toContain}, or {@link matchers#toHaveBeenCalledWith|toHaveBeenCalledWith}),
|
* Get an {@link AsymmetricEqualityTester} that will succeed if the actual
|
||||||
* that will succeed if the actual value is a `String` that contains the specified `String`.
|
* value is a `String` that contains the specified `String`.
|
||||||
* @name jasmine.stringContaining
|
* @name asymmetricEqualityTesters.stringContaining
|
||||||
|
* @emittedName jasmine.stringContaining
|
||||||
* @since 3.10.0
|
* @since 3.10.0
|
||||||
* @function
|
* @function
|
||||||
* @param {String} expected
|
* @param {String} expected
|
||||||
@@ -339,9 +349,10 @@ getJasmineRequireObj().base = function(j$, jasmineGlobal) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get an {@link AsymmetricEqualityTester}, usable in any {@link matchers|matcher} that uses Jasmine's equality (e.g. {@link matchers#toEqual|toEqual}, {@link matchers#toContain|toContain}, or {@link matchers#toHaveBeenCalledWith|toHaveBeenCalledWith}),
|
* Get an {@link AsymmetricEqualityTester} that will succeed if the actual
|
||||||
* that will succeed if the actual value is an `Array` that contains at least the elements in the sample.
|
* value is an `Array` that contains at least the elements in the sample.
|
||||||
* @name jasmine.arrayContaining
|
* @name asymmetricEqualityTesters.arrayContaining
|
||||||
|
* @emittedName jasmine.arrayContaining
|
||||||
* @since 2.2.0
|
* @since 2.2.0
|
||||||
* @function
|
* @function
|
||||||
* @param {Array} sample
|
* @param {Array} sample
|
||||||
@@ -351,9 +362,11 @@ getJasmineRequireObj().base = function(j$, jasmineGlobal) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get an {@link AsymmetricEqualityTester}, usable in any {@link matchers|matcher} that uses Jasmine's equality (e.g. {@link matchers#toEqual|toEqual}, {@link matchers#toContain|toContain}, or {@link matchers#toHaveBeenCalledWith|toHaveBeenCalledWith}),
|
* Get an {@link AsymmetricEqualityTester} that will succeed if the actual
|
||||||
* that will succeed if the actual value is an `Array` that contains all of the elements in the sample in any order.
|
* value is an `Array` that contains all of the elements in the sample in
|
||||||
* @name jasmine.arrayWithExactContents
|
* any order.
|
||||||
|
* @name asymmetricEqualityTesters.arrayWithExactContents
|
||||||
|
* @emittedName jasmine.arrayWithExactContents
|
||||||
* @since 2.8.0
|
* @since 2.8.0
|
||||||
* @function
|
* @function
|
||||||
* @param {Array} sample
|
* @param {Array} sample
|
||||||
@@ -363,10 +376,11 @@ getJasmineRequireObj().base = function(j$, jasmineGlobal) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get an {@link AsymmetricEqualityTester}, usable in any {@link matchers|matcher} that uses Jasmine's equality (e.g. {@link matchers#toEqual|toEqual}, {@link matchers#toContain|toContain}, or {@link matchers#toHaveBeenCalledWith|toHaveBeenCalledWith}),
|
* Get an {@link AsymmetricEqualityTester} that will succeed if every
|
||||||
* that will succeed if every key/value pair in the sample passes the deep equality comparison
|
* key/value pair in the sample passes the deep equality comparison
|
||||||
* with at least one key/value pair in the actual value being compared
|
* with at least one key/value pair in the actual value being compared
|
||||||
* @name jasmine.mapContaining
|
* @name asymmetricEqualityTesters.mapContaining
|
||||||
|
* @emittedName jasmine.mapContaining
|
||||||
* @since 3.5.0
|
* @since 3.5.0
|
||||||
* @function
|
* @function
|
||||||
* @param {Map} sample - The subset of items that _must_ be in the actual.
|
* @param {Map} sample - The subset of items that _must_ be in the actual.
|
||||||
@@ -376,10 +390,11 @@ getJasmineRequireObj().base = function(j$, jasmineGlobal) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get an {@link AsymmetricEqualityTester}, usable in any {@link matchers|matcher} that uses Jasmine's equality (e.g. {@link matchers#toEqual|toEqual}, {@link matchers#toContain|toContain}, or {@link matchers#toHaveBeenCalledWith|toHaveBeenCalledWith}),
|
* Get an {@link AsymmetricEqualityTester} that will succeed if every item
|
||||||
* that will succeed if every item in the sample passes the deep equality comparison
|
* in the sample passes the deep equality comparison
|
||||||
* with at least one item in the actual value being compared
|
* with at least one item in the actual value being compared
|
||||||
* @name jasmine.setContaining
|
* @name asymmetricEqualityTesters.setContaining
|
||||||
|
* @emittedName jasmine.setContaining
|
||||||
* @since 3.5.0
|
* @since 3.5.0
|
||||||
* @function
|
* @function
|
||||||
* @param {Set} sample - The subset of items that _must_ be in the actual.
|
* @param {Set} sample - The subset of items that _must_ be in the actual.
|
||||||
@@ -435,14 +450,26 @@ getJasmineRequireObj().base = function(j$, jasmineGlobal) {
|
|||||||
* handling will be restored when the promise returned from the callback is
|
* handling will be restored when the promise returned from the callback is
|
||||||
* settled.
|
* settled.
|
||||||
*
|
*
|
||||||
|
* When the JavaScript runtime reports an uncaught error or unhandled rejection,
|
||||||
|
* the spy will be called with a single parameter representing Jasmine's best
|
||||||
|
* effort at describing the error. This parameter may be of any type, because
|
||||||
|
* JavaScript allows anything to be thrown or used as the reason for a
|
||||||
|
* rejected promise, but Error instances and strings are most common.
|
||||||
|
*
|
||||||
* Note: The JavaScript runtime may deliver uncaught error events and unhandled
|
* Note: The JavaScript runtime may deliver uncaught error events and unhandled
|
||||||
* rejection events asynchronously, especially in browsers. If the event
|
* rejection events asynchronously, especially in browsers. If the event
|
||||||
* occurs after the promise returned from the callback is settled, it won't
|
* occurs after the promise returned from the callback is settled, it won't
|
||||||
* be routed to the spy even if the underlying error occurred previously.
|
* be routed to the spy even if the underlying error occurred previously.
|
||||||
* It's up to you to ensure that the returned promise isn't resolved until
|
* It's up to you to ensure that all of the error/rejection events that you
|
||||||
* all of the error/rejection events that you want to handle have occurred.
|
* want to handle have occurred before you resolve the promise returned from
|
||||||
|
* the callback.
|
||||||
*
|
*
|
||||||
* You must await the return value of spyOnGlobalErrorsAsync.
|
* You must ensure that the `it`/`beforeEach`/etc fn that called
|
||||||
|
* `spyOnGlobalErrorsAsync` does not signal completion until after the
|
||||||
|
* promise returned by `spyOnGlobalErrorsAsync` is resolved. Normally this is
|
||||||
|
* done by `await`ing the returned promise. Leaving the global error spy
|
||||||
|
* installed after the `it`/`beforeEach`/etc fn that installed it signals
|
||||||
|
* completion is likely to cause problems and is not supported.
|
||||||
* @name jasmine.spyOnGlobalErrorsAsync
|
* @name jasmine.spyOnGlobalErrorsAsync
|
||||||
* @function
|
* @function
|
||||||
* @async
|
* @async
|
||||||
|
|||||||
@@ -69,11 +69,7 @@ getJasmineRequireObj().buildExpectationResult = function(j$) {
|
|||||||
} else if (options.stack) {
|
} else if (options.stack) {
|
||||||
error = options;
|
error = options;
|
||||||
} else {
|
} else {
|
||||||
try {
|
error = new Error(message());
|
||||||
throw new Error(message());
|
|
||||||
} catch (e) {
|
|
||||||
error = e;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Omit the message from the stack trace because it will be
|
// Omit the message from the stack trace because it will be
|
||||||
|
|||||||
@@ -652,7 +652,7 @@ getJasmineRequireObj().MatchersUtil = function(j$) {
|
|||||||
* };
|
* };
|
||||||
* }
|
* }
|
||||||
*
|
*
|
||||||
* var actual = {
|
* const actual = {
|
||||||
* n: 2,
|
* n: 2,
|
||||||
* otherFields: "don't care"
|
* otherFields: "don't care"
|
||||||
* };
|
* };
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ getJasmineRequireObj().toHaveClass = function(j$) {
|
|||||||
* @since 3.0.0
|
* @since 3.0.0
|
||||||
* @param {Object} expected - The class name to test for
|
* @param {Object} expected - The class name to test for
|
||||||
* @example
|
* @example
|
||||||
* var el = document.createElement('div');
|
* const el = document.createElement('div');
|
||||||
* el.className = 'foo bar baz';
|
* el.className = 'foo bar baz';
|
||||||
* expect(el).toHaveClass('bar');
|
* expect(el).toHaveClass('bar');
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ getJasmineRequireObj().toHaveSize = function(j$) {
|
|||||||
* array = [1,2];
|
* array = [1,2];
|
||||||
* expect(array).toHaveSize(2);
|
* expect(array).toHaveSize(2);
|
||||||
*/
|
*/
|
||||||
function toHaveSize() {
|
function toHaveSize(matchersUtil) {
|
||||||
return {
|
return {
|
||||||
compare: function(actual, expected) {
|
compare: function(actual, expected) {
|
||||||
const result = {
|
const result = {
|
||||||
@@ -24,12 +24,29 @@ getJasmineRequireObj().toHaveSize = function(j$) {
|
|||||||
throw new Error('Cannot get size of ' + actual + '.');
|
throw new Error('Cannot get size of ' + actual + '.');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let actualSize;
|
||||||
if (j$.isSet(actual) || j$.isMap(actual)) {
|
if (j$.isSet(actual) || j$.isMap(actual)) {
|
||||||
result.pass = actual.size === expected;
|
actualSize = actual.size;
|
||||||
} else if (isLength(actual.length)) {
|
} else if (isLength(actual.length)) {
|
||||||
result.pass = actual.length === expected;
|
actualSize = actual.length;
|
||||||
} else {
|
} else {
|
||||||
result.pass = Object.keys(actual).length === expected;
|
actualSize = Object.keys(actual).length;
|
||||||
|
}
|
||||||
|
|
||||||
|
result.pass = actualSize === expected;
|
||||||
|
|
||||||
|
if (!result.pass) {
|
||||||
|
result.message = function() {
|
||||||
|
return (
|
||||||
|
'Expected ' +
|
||||||
|
matchersUtil.pp(actual) +
|
||||||
|
' with size ' +
|
||||||
|
actualSize +
|
||||||
|
' to have size ' +
|
||||||
|
expected +
|
||||||
|
'.'
|
||||||
|
);
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
|||||||
70
src/core/reporterEvents.js
Normal file
70
src/core/reporterEvents.js
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
getJasmineRequireObj().reporterEvents = function() {
|
||||||
|
const events = [
|
||||||
|
/**
|
||||||
|
* `jasmineStarted` is called after all of the specs have been loaded, but just before execution starts.
|
||||||
|
* @function
|
||||||
|
* @name Reporter#jasmineStarted
|
||||||
|
* @param {JasmineStartedInfo} suiteInfo Information about the full Jasmine suite that is being run
|
||||||
|
* @param {Function} [done] Used to specify to Jasmine that this callback is asynchronous and Jasmine should wait until it has been called before moving on.
|
||||||
|
* @returns {} Optionally return a Promise instead of using `done` to cause Jasmine to wait for completion.
|
||||||
|
* @see async
|
||||||
|
*/
|
||||||
|
'jasmineStarted',
|
||||||
|
/**
|
||||||
|
* When the entire suite has finished execution `jasmineDone` is called
|
||||||
|
* @function
|
||||||
|
* @name Reporter#jasmineDone
|
||||||
|
* @param {JasmineDoneInfo} suiteInfo Information about the full Jasmine suite that just finished running.
|
||||||
|
* @param {Function} [done] Used to specify to Jasmine that this callback is asynchronous and Jasmine should wait until it has been called before moving on.
|
||||||
|
* @returns {} Optionally return a Promise instead of using `done` to cause Jasmine to wait for completion.
|
||||||
|
* @see async
|
||||||
|
*/
|
||||||
|
'jasmineDone',
|
||||||
|
/**
|
||||||
|
* `suiteStarted` is invoked when a `describe` starts to run
|
||||||
|
* @function
|
||||||
|
* @name Reporter#suiteStarted
|
||||||
|
* @param {SuiteResult} result Information about the individual {@link describe} being run
|
||||||
|
* @param {Function} [done] Used to specify to Jasmine that this callback is asynchronous and Jasmine should wait until it has been called before moving on.
|
||||||
|
* @returns {} Optionally return a Promise instead of using `done` to cause Jasmine to wait for completion.
|
||||||
|
* @see async
|
||||||
|
*/
|
||||||
|
'suiteStarted',
|
||||||
|
/**
|
||||||
|
* `suiteDone` is invoked when all of the child specs and suites for a given suite have been run
|
||||||
|
*
|
||||||
|
* While jasmine doesn't require any specific functions, not defining a `suiteDone` will make it impossible for a reporter to know when a suite has failures in an `afterAll`.
|
||||||
|
* @function
|
||||||
|
* @name Reporter#suiteDone
|
||||||
|
* @param {SuiteResult} result
|
||||||
|
* @param {Function} [done] Used to specify to Jasmine that this callback is asynchronous and Jasmine should wait until it has been called before moving on.
|
||||||
|
* @returns {} Optionally return a Promise instead of using `done` to cause Jasmine to wait for completion.
|
||||||
|
* @see async
|
||||||
|
*/
|
||||||
|
'suiteDone',
|
||||||
|
/**
|
||||||
|
* `specStarted` is invoked when an `it` starts to run (including associated `beforeEach` functions)
|
||||||
|
* @function
|
||||||
|
* @name Reporter#specStarted
|
||||||
|
* @param {SpecResult} result Information about the individual {@link it} being run
|
||||||
|
* @param {Function} [done] Used to specify to Jasmine that this callback is asynchronous and Jasmine should wait until it has been called before moving on.
|
||||||
|
* @returns {} Optionally return a Promise instead of using `done` to cause Jasmine to wait for completion.
|
||||||
|
* @see async
|
||||||
|
*/
|
||||||
|
'specStarted',
|
||||||
|
/**
|
||||||
|
* `specDone` is invoked when an `it` and its associated `beforeEach` and `afterEach` functions have been run.
|
||||||
|
*
|
||||||
|
* While jasmine doesn't require any specific functions, not defining a `specDone` will make it impossible for a reporter to know when a spec has failed.
|
||||||
|
* @function
|
||||||
|
* @name Reporter#specDone
|
||||||
|
* @param {SpecResult} result
|
||||||
|
* @param {Function} [done] Used to specify to Jasmine that this callback is asynchronous and Jasmine should wait until it has been called before moving on.
|
||||||
|
* @returns {} Optionally return a Promise instead of using `done` to cause Jasmine to wait for completion.
|
||||||
|
* @see async
|
||||||
|
*/
|
||||||
|
'specDone'
|
||||||
|
];
|
||||||
|
Object.freeze(events);
|
||||||
|
return events;
|
||||||
|
};
|
||||||
@@ -67,7 +67,9 @@ var getJasmineRequireObj = (function(jasmineGlobal) {
|
|||||||
j$.CompleteOnFirstErrorSkipPolicy = jRequire.CompleteOnFirstErrorSkipPolicy(
|
j$.CompleteOnFirstErrorSkipPolicy = jRequire.CompleteOnFirstErrorSkipPolicy(
|
||||||
j$
|
j$
|
||||||
);
|
);
|
||||||
|
j$.reporterEvents = jRequire.reporterEvents(j$);
|
||||||
j$.ReportDispatcher = jRequire.ReportDispatcher(j$);
|
j$.ReportDispatcher = jRequire.ReportDispatcher(j$);
|
||||||
|
j$.ParallelReportDispatcher = jRequire.ParallelReportDispatcher(j$);
|
||||||
j$.RunableResources = jRequire.RunableResources(j$);
|
j$.RunableResources = jRequire.RunableResources(j$);
|
||||||
j$.Runner = jRequire.Runner(j$);
|
j$.Runner = jRequire.Runner(j$);
|
||||||
j$.Spec = jRequire.Spec(j$);
|
j$.Spec = jRequire.Spec(j$);
|
||||||
|
|||||||
@@ -225,6 +225,54 @@ getJasmineRequireObj().interface = function(jasmine, env) {
|
|||||||
return env.expectAsync(actual);
|
return env.expectAsync(actual);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create an asynchronous expectation for a spec and throw an error if it fails.
|
||||||
|
*
|
||||||
|
* This is intended to allow Jasmine matchers to be used with tools like
|
||||||
|
* testing-library's `waitFor`, which expect matcher failures to throw
|
||||||
|
* exceptions and not trigger a spec failure if the exception is caught.
|
||||||
|
* It can also be used to integration-test custom matchers.
|
||||||
|
*
|
||||||
|
* If the resulting expectation fails, a {@link ThrowUnlessFailure} will be
|
||||||
|
* thrown. A failed expectation will not result in a spec failure unless the
|
||||||
|
* exception propagates back to Jasmine, either via the call stack or via
|
||||||
|
* the global unhandled exception/unhandled promise rejection events.
|
||||||
|
* @name throwUnlessAsync
|
||||||
|
* @since 5.1.0
|
||||||
|
* @function
|
||||||
|
* @param actual
|
||||||
|
* @global
|
||||||
|
* @param {Object} actual - Actual computed value to test expectations against.
|
||||||
|
* @return {matchers}
|
||||||
|
*/
|
||||||
|
throwUnlessAsync: function(actual) {
|
||||||
|
return env.throwUnlessAsync(actual);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create an expectation for a spec and throw an error if it fails.
|
||||||
|
*
|
||||||
|
* This is intended to allow Jasmine matchers to be used with tools like
|
||||||
|
* testing-library's `waitFor`, which expect matcher failures to throw
|
||||||
|
* exceptions and not trigger a spec failure if the exception is caught.
|
||||||
|
* It can also be used to integration-test custom matchers.
|
||||||
|
*
|
||||||
|
* If the resulting expectation fails, a {@link ThrowUnlessFailure} will be
|
||||||
|
* thrown. A failed expectation will not result in a spec failure unless the
|
||||||
|
* exception propagates back to Jasmine, either via the call stack or via
|
||||||
|
* the global unhandled exception/unhandled promise rejection events.
|
||||||
|
* @name throwUnless
|
||||||
|
* @since 5.1.0
|
||||||
|
* @function
|
||||||
|
* @param actual
|
||||||
|
* @global
|
||||||
|
* @param {Object} actual - Actual computed value to test expectations against.
|
||||||
|
* @return {matchers}
|
||||||
|
*/
|
||||||
|
throwUnless: function(actual) {
|
||||||
|
return env.throwUnless(actual);
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Mark a spec as pending, expectation results will be ignored.
|
* Mark a spec as pending, expectation results will be ignored.
|
||||||
* @name pending
|
* @name pending
|
||||||
@@ -297,6 +345,12 @@ getJasmineRequireObj().interface = function(jasmine, env) {
|
|||||||
}),
|
}),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* <p>Members of the jasmine global.</p>
|
||||||
|
* <p>Note: The members of the
|
||||||
|
* {@link asymmetricEqualityTesters|asymmetricEqualityTesters namespace}
|
||||||
|
* are also accessed via the jasmine global, but due to jsdoc limitations
|
||||||
|
* they are not listed here.</p>
|
||||||
|
*
|
||||||
* @namespace jasmine
|
* @namespace jasmine
|
||||||
*/
|
*/
|
||||||
jasmine: jasmine
|
jasmine: jasmine
|
||||||
@@ -375,7 +429,11 @@ getJasmineRequireObj().interface = function(jasmine, env) {
|
|||||||
* @since 1.3.0
|
* @since 1.3.0
|
||||||
* @function
|
* @function
|
||||||
* @param {String} [name] - Name to give the spy. This will be displayed in failure messages.
|
* @param {String} [name] - Name to give the spy. This will be displayed in failure messages.
|
||||||
* @param {Function} [originalFn] - Function to act as the real implementation.
|
* @param {Function} [originalFn] - The "real" function. This will
|
||||||
|
* be used for subsequent calls to the spy after you call
|
||||||
|
* `mySpy.and.callThrough()`. In most cases you should omit this parameter.
|
||||||
|
* The usual way to supply an original function is to call {@link spyOn}
|
||||||
|
* instead of createSpy.
|
||||||
* @return {Spy}
|
* @return {Spy}
|
||||||
*/
|
*/
|
||||||
jasmine.createSpy = function(name, originalFn) {
|
jasmine.createSpy = function(name, originalFn) {
|
||||||
@@ -426,5 +484,27 @@ getJasmineRequireObj().interface = function(jasmine, env) {
|
|||||||
return env.setDefaultSpyStrategy(defaultStrategyFn);
|
return env.setDefaultSpyStrategy(defaultStrategyFn);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link AsymmetricEqualityTester|Asymmetric equality testers} allow for
|
||||||
|
* non-exact matching in matchers that use Jasmine's deep value equality
|
||||||
|
* semantics, such as {@link matchers#toEqual|toEqual},
|
||||||
|
* {@link matchers#toContain|toContain}, and
|
||||||
|
* {@link matchers#toHaveBeenCalledWith|toHaveBeenCalledWith}.
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* const someComplexObject = {
|
||||||
|
* foo: 'bar',
|
||||||
|
* baz: 'a string that contains "something"',
|
||||||
|
* qux: 'whatever'
|
||||||
|
* };
|
||||||
|
* // Passes.
|
||||||
|
* expect(someComplexObject).toEqual(jasmine.objectContaining({
|
||||||
|
* foo: 'bar',
|
||||||
|
* baz: jasmine.stringContaining('something')
|
||||||
|
* });
|
||||||
|
*
|
||||||
|
* @namespace asymmetricEqualityTesters
|
||||||
|
*/
|
||||||
|
|
||||||
return jasmineInterface;
|
return jasmineInterface;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -430,7 +430,11 @@ jasmineRequire.HtmlReporter = function(j$) {
|
|||||||
'tr',
|
'tr',
|
||||||
{},
|
{},
|
||||||
createDom('td', {}, entry.timestamp.toString()),
|
createDom('td', {}, entry.timestamp.toString()),
|
||||||
createDom('td', {}, entry.message)
|
createDom(
|
||||||
|
'td',
|
||||||
|
{ className: 'jasmine-debug-log-msg' },
|
||||||
|
entry.message
|
||||||
|
)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@@ -498,14 +502,13 @@ jasmineRequire.HtmlReporter = function(j$) {
|
|||||||
if (noExpectations(resultNode.result)) {
|
if (noExpectations(resultNode.result)) {
|
||||||
specDescription = 'SPEC HAS NO EXPECTATIONS ' + specDescription;
|
specDescription = 'SPEC HAS NO EXPECTATIONS ' + specDescription;
|
||||||
}
|
}
|
||||||
if (
|
if (resultNode.result.status === 'pending') {
|
||||||
resultNode.result.status === 'pending' &&
|
if (resultNode.result.pendingReason !== '') {
|
||||||
resultNode.result.pendingReason !== ''
|
specDescription +=
|
||||||
) {
|
' PENDING WITH MESSAGE: ' + resultNode.result.pendingReason;
|
||||||
specDescription =
|
} else {
|
||||||
specDescription +
|
specDescription += ' PENDING';
|
||||||
' PENDING WITH MESSAGE: ' +
|
}
|
||||||
resultNode.result.pendingReason;
|
|
||||||
}
|
}
|
||||||
specListNode.appendChild(
|
specListNode.appendChild(
|
||||||
createDom(
|
createDom(
|
||||||
|
|||||||
@@ -3,11 +3,13 @@
|
|||||||
$line-height: 14px;
|
$line-height: 14px;
|
||||||
$margin-unit: 14px;
|
$margin-unit: 14px;
|
||||||
|
|
||||||
$faint-text-color: #aaa;
|
|
||||||
$light-text-color: #666;
|
$light-text-color: #666;
|
||||||
$text-color: #333;
|
$text-color: #333;
|
||||||
|
$inactive-tab-text-color: blue;
|
||||||
|
$active-tab-text-color: #000;
|
||||||
|
|
||||||
$page-background-color: #eee;
|
$page-background-color: #eee;
|
||||||
|
$menu-background-color: #aaa;
|
||||||
|
|
||||||
$passing-color: #007069;
|
$passing-color: #007069;
|
||||||
$failing-color: #ca3a11;
|
$failing-color: #ca3a11;
|
||||||
@@ -91,10 +93,6 @@ body {
|
|||||||
right: 100%;
|
right: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.jasmine-version {
|
|
||||||
color: $faint-text-color;
|
|
||||||
}
|
|
||||||
|
|
||||||
//--- Banner ---//
|
//--- Banner ---//
|
||||||
|
|
||||||
.jasmine-banner {
|
.jasmine-banner {
|
||||||
@@ -238,10 +236,11 @@ body {
|
|||||||
|
|
||||||
&.jasmine-menu {
|
&.jasmine-menu {
|
||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
color: $faint-text-color;
|
color: $active-tab-text-color;
|
||||||
|
|
||||||
a {
|
a {
|
||||||
color: $text-color;
|
color: $inactive-tab-text-color;
|
||||||
|
text-decoration: underline;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -425,5 +424,9 @@ body {
|
|||||||
table, th, td {
|
table, th, td {
|
||||||
border: 1px solid #ddd;
|
border: 1px solid #ddd;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.jasmine-debug-log-msg {
|
||||||
|
white-space: pre;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user