Compare commits
191 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d5872bba66 | ||
|
|
c4f4edda1b | ||
|
|
cf057b6631 | ||
|
|
2a7a157713 | ||
|
|
7463fe511b | ||
|
|
1b724daa10 | ||
|
|
888d3b6250 | ||
|
|
289afbf0a6 | ||
|
|
9b89bee4f5 | ||
|
|
3f8f488a58 | ||
|
|
592d47e971 | ||
|
|
4732012f1c | ||
|
|
7683325d68 | ||
|
|
03d665e243 | ||
|
|
a1591da25d | ||
|
|
1f1e1209d2 | ||
|
|
a389905a38 | ||
|
|
36dd6b07d1 | ||
|
|
2f3689713b | ||
|
|
819fab7b58 | ||
|
|
e5ca1f37f1 | ||
|
|
c3650ea7c7 | ||
|
|
1805337424 | ||
|
|
27bb6ebac1 | ||
|
|
580323c221 | ||
|
|
d9286c549f | ||
|
|
26dfa6d257 | ||
|
|
483d4ab3c3 | ||
|
|
663dfe5932 | ||
|
|
ce9c752899 | ||
|
|
d5e7bc9fd6 | ||
|
|
744e765d6f | ||
|
|
29551ba4f3 | ||
|
|
bd9a3b2305 | ||
|
|
c8c3325b56 | ||
|
|
84c7e2b21b | ||
|
|
dda25bb29e | ||
|
|
9ccf2ef96b | ||
|
|
c6fa55bfc8 | ||
|
|
06bcf1c2e1 | ||
|
|
40f402d117 | ||
|
|
71f6a95ce5 | ||
|
|
5cd7d47f72 | ||
|
|
d0fe5c4712 | ||
|
|
f602c4911c | ||
|
|
7aaf7eaf30 | ||
|
|
35f16e8125 | ||
|
|
acc777c267 | ||
|
|
1c74356691 | ||
|
|
bbebea0fa5 | ||
|
|
66eb27b0af | ||
|
|
7a63c06a65 | ||
|
|
554dfd4923 | ||
|
|
36a6e2aa1d | ||
|
|
3c4b73f136 | ||
|
|
bc3ed74336 | ||
|
|
97b6f33cc2 | ||
|
|
a9889ddb31 | ||
|
|
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
|
||||
|
||||
executors:
|
||||
node22:
|
||||
docker:
|
||||
- image: cimg/node:22.0.0
|
||||
working_directory: ~/workspace
|
||||
node20:
|
||||
docker:
|
||||
- image: cimg/node:20.0.0
|
||||
working_directory: ~/workspace
|
||||
node18:
|
||||
docker:
|
||||
- image: cimg/node:18.0.0 # Latest 18.x
|
||||
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
|
||||
- image: cimg/node:18.0.0
|
||||
working_directory: ~/workspace
|
||||
|
||||
jobs:
|
||||
@@ -59,8 +51,20 @@ jobs:
|
||||
name: Run tests
|
||||
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
|
||||
executor: node14
|
||||
executor: node18
|
||||
steps:
|
||||
- attach_workspace:
|
||||
at: .
|
||||
@@ -80,7 +84,7 @@ jobs:
|
||||
# cleanly if we kill it from a different step than it started in.
|
||||
|
||||
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
|
||||
set +o errexit
|
||||
scripts/run-all-browsers
|
||||
@@ -94,49 +98,48 @@ workflows:
|
||||
|
||||
push:
|
||||
jobs:
|
||||
- build:
|
||||
executor: node22
|
||||
name: build_node_22
|
||||
- build:
|
||||
executor: node20
|
||||
name: build_node_20
|
||||
- build:
|
||||
executor: node18
|
||||
name: build_node_18
|
||||
- build:
|
||||
executor: node16
|
||||
name: build_node_16
|
||||
- build:
|
||||
executor: node14
|
||||
name: build_node_14
|
||||
- build:
|
||||
executor: node12_latest
|
||||
name: build_node_12_latest
|
||||
- build:
|
||||
executor: node12_17
|
||||
name: build_node_12_17
|
||||
- test_node:
|
||||
executor: node22
|
||||
name: test_node_22
|
||||
requires:
|
||||
- build_node_22
|
||||
- test_node:
|
||||
executor: node20
|
||||
name: test_node_20
|
||||
requires:
|
||||
- build_node_20
|
||||
- test_node:
|
||||
executor: node18
|
||||
name: test_node_18
|
||||
requires:
|
||||
- build_node_18
|
||||
- test_node:
|
||||
executor: node16
|
||||
name: test_node_16
|
||||
- build_node_18
|
||||
- test_parallel:
|
||||
executor: node18
|
||||
name: test_parallel_node_18
|
||||
requires:
|
||||
- build_node_16
|
||||
- test_node:
|
||||
executor: node14
|
||||
name: test_node_14
|
||||
- build_node_18
|
||||
- test_parallel:
|
||||
executor: node22
|
||||
name: test_parallel_node_22
|
||||
requires:
|
||||
- build_node_14
|
||||
- test_node:
|
||||
executor: node12_latest
|
||||
name: test_node_12_latest
|
||||
- build_node_22
|
||||
- test_parallel:
|
||||
executor: node20
|
||||
name: test_parallel_node_20
|
||||
requires:
|
||||
- build_node_12_latest
|
||||
- test_node:
|
||||
executor: node12_17
|
||||
name: test_node_12_17
|
||||
requires:
|
||||
- build_node_12_17
|
||||
- build_node_20
|
||||
- test_browsers:
|
||||
requires:
|
||||
- build_node_14
|
||||
- build_node_18
|
||||
filters:
|
||||
branches:
|
||||
ignore: /pull\/.*/ # Don't run on pull requests.
|
||||
|
||||
47
.eslintrc
Normal file
47
.eslintrc
Normal file
@@ -0,0 +1,47 @@
|
||||
{
|
||||
"extends": [
|
||||
"plugin:compat/recommended"
|
||||
],
|
||||
"env": {
|
||||
"browser": true,
|
||||
"node": true,
|
||||
"es2017": true
|
||||
},
|
||||
"parserOptions": {
|
||||
"ecmaVersion": 2018
|
||||
},
|
||||
"rules": {
|
||||
"curly": "error",
|
||||
"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"
|
||||
}
|
||||
}
|
||||
31
.github/CONTRIBUTING.md
vendored
31
.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
|
||||
for everyone. Please review the backlog and discussion lists before starting
|
||||
work. What you're looking for may already have been done. If it hasn't, the
|
||||
community can help make your contribution better. If you want to contribute but
|
||||
don't know what to work on,
|
||||
[issues tagged help needed](https://github.com/jasmine/jasmine/labels/help%20needed)
|
||||
for everyone. If you want to contribute but don't know what to work on,
|
||||
[issues tagged help needed](https://github.com/issues?q=is%3Aopen+is%3Aissue+org%3Ajasmine+label%3A%22help+needed%22+)
|
||||
should have enough detail to get started.
|
||||
|
||||
## Links
|
||||
|
||||
- [Jasmine Google Group](http://groups.google.com/group/jasmine-js)
|
||||
- [Jasmine-dev Google Group](http://groups.google.com/group/jasmine-js-dev)
|
||||
- [Jasmine backlog](https://www.pivotaltracker.com/n/projects/10606)
|
||||
|
||||
## Before Submitting a Pull Request
|
||||
|
||||
1. Ensure all specs are green in browsers *and* node.
|
||||
@@ -94,14 +85,7 @@ Or, How to make a successful pull request
|
||||
* _Write specs_ - Jasmine's a testing framework. Don't add functionality
|
||||
without test-driving it.
|
||||
* _Write code in the style of the rest of the repo_ - Jasmine should look like
|
||||
a cohesive whole.
|
||||
|
||||
Key exceptions:
|
||||
* Use `const` or `let` for new variable declarations, even if nearby code
|
||||
uses `var`.
|
||||
* New async specs should usually be async/await or promise-returning, not
|
||||
callback based.
|
||||
|
||||
a cohesive whole.
|
||||
* _Ensure the *entire* test suite is green_ in all the big browsers, Node, and
|
||||
ESLint/Prettier. Your contribution shouldn't break Jasmine for other users.
|
||||
|
||||
@@ -119,3 +103,10 @@ chromedriver), you can also use Jasmine's CI tooling:
|
||||
|
||||
$ JASMINE_BROWSER=<name of browser> npm run ci
|
||||
|
||||
### Submitting a Pull Requeset
|
||||
|
||||
Once you've done the steps listed under "Before Submitting a Pull Request"
|
||||
above, you can submit a pull request via the
|
||||
[standard GitHub process](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/creating-a-pull-request).
|
||||
TL;DR: Fork the repository, push your work up to your fork, and create a PR from
|
||||
there.
|
||||
|
||||
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
|
||||
17
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
17
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: Questions, requests for help, etc
|
||||
url: https://github.com/jasmine/jasmine/discussions/new/choose
|
||||
about: Please start a discussion.
|
||||
- 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.
|
||||
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() {
|
||||
verifyNoGlobals(() => require('./lib/jasmine-core.js').noGlobals());
|
||||
const done = this.async(),
|
||||
Jasmine = require('jasmine'),
|
||||
jasmineCore = require('./lib/jasmine-core.js'),
|
||||
jasmine = new Jasmine({jasmineCore: jasmineCore});
|
||||
Jasmine = require('jasmine'),
|
||||
jasmineCore = require('./lib/jasmine-core.js'),
|
||||
jasmine = new Jasmine({jasmineCore: jasmineCore});
|
||||
|
||||
jasmine.loadConfigFile('./spec/support/jasmine.json');
|
||||
jasmine.exitOnCompletion = false;
|
||||
@@ -40,12 +40,50 @@ module.exports = function(grunt) {
|
||||
result => done(result.overallStatus === 'passed'),
|
||||
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",
|
||||
"Run Jasmine performance specs in Node.js",
|
||||
function() {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
Copyright (c) 2008-2019 Pivotal Labs
|
||||
Copyright (c) 2008-2025 The Jasmine developers
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
27
README.md
27
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>
|
||||
|
||||
[](https://circleci.com/gh/jasmine/jasmine)
|
||||
[](https://www.codetriage.com/jasmine/jasmine)
|
||||
|
||||
# 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.
|
||||
|
||||
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
|
||||
|
||||
@@ -30,18 +27,22 @@ for information on writing specs, and [the FAQ](https://jasmine.github.io/pages/
|
||||
Jasmine tests itself across popular browsers (Safari, Chrome, Firefox, and
|
||||
Microsoft Edge) as well as Node.
|
||||
|
||||
| Environment | Supported versions |
|
||||
|-------------------|--------------------|
|
||||
| Node | 12.17+, 14, 16, 18 |
|
||||
| Safari | 14-15 |
|
||||
| Chrome | Evergreen |
|
||||
| Firefox | Evergreen, 91 |
|
||||
| Edge | Evergreen |
|
||||
| Environment | Supported versions |
|
||||
|-------------------|----------------------------|
|
||||
| Node | 18, 20, 22 |
|
||||
| Safari | 15*, 16*, 17* |
|
||||
| Chrome | Evergreen |
|
||||
| Firefox | Evergreen, 102*, 115*, 128 |
|
||||
| Edge | Evergreen |
|
||||
|
||||
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.
|
||||
However, Jasmine isn't tested against them and they aren't actively supported.
|
||||
|
||||
\* Supported on a best-effort basis. Support for these versions may be dropped
|
||||
if it becomes impractical, and bugs affecting only these versions may not be
|
||||
treated as release blockers.
|
||||
|
||||
To find out what environments work with a particular Jasmine release, see the [release notes](https://github.com/jasmine/jasmine/tree/main/release_notes).
|
||||
|
||||
## Maintainers
|
||||
@@ -58,4 +59,6 @@ To find out what environments work with a particular Jasmine release, see the [r
|
||||
* [Christian Williams](mailto:antixian666@gmail.com)
|
||||
* 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-2025 The Jasmine developers<br>
|
||||
This software is licensed under the [MIT License](https://github.com/jasmine/jasmine/blob/main/LICENSE).
|
||||
|
||||
37
RELEASE.md
37
RELEASE.md
@@ -18,12 +18,11 @@ copied to `jasmine.js` when the distribution is built. When releasing a new
|
||||
version, update `package.json` with the new version and `npm run build` to
|
||||
update the gem version number.
|
||||
|
||||
Note that Jasmine should only use the "patch" version number in the following cases:
|
||||
Note that Jasmine should only use the "patch" version number if the new release
|
||||
contains only bug fixes.
|
||||
|
||||
* Changes related to packaging for a specific binding library (npm or browser-runner)
|
||||
* Fixes for regressions.
|
||||
|
||||
When jasmine-core revs its major or minor version, the binding libraries should also rev to that version.
|
||||
When `jasmine-core` revs its major or minor version, the `jasmine` NPM package
|
||||
should also rev to that version.
|
||||
|
||||
## Release
|
||||
|
||||
@@ -47,8 +46,8 @@ When ready to release - specs are all green and the stories are done:
|
||||
|
||||
### Release the core NPM module
|
||||
|
||||
1. `npm adduser` to save your credentials locally
|
||||
1. `npm publish .` to publish what's in `package.json`
|
||||
1. `npm login` to save your credentials locally
|
||||
2. `npm publish .` to publish what's in `package.json`
|
||||
|
||||
### Release the docs
|
||||
|
||||
@@ -56,25 +55,13 @@ Probably only need to do this when releasing a minor version, and not a patch
|
||||
version. See [the README file in the docs repo](https://github.com/jasmine/jasmine.github.io/blob/master/README.md)
|
||||
for instructions.
|
||||
|
||||
1. `rake update_edge_jasmine`
|
||||
1. `npm run jsdoc`
|
||||
1. `rake release[${version}]` to copy the current edge docs to the new version
|
||||
1. Commit and push.
|
||||
### Release the `jasmine` NPM package
|
||||
|
||||
### Release the binding libraries
|
||||
See <https://github.com/jasmine/jasmine-npm/blob/main/RELEASE.md>.
|
||||
|
||||
#### NPM
|
||||
### Publish the GitHub release
|
||||
|
||||
1. Create release notes using Anchorman as above
|
||||
1. In `package.json`, update both the package version and the jasmine-core dependency version
|
||||
1. Commit and push.
|
||||
1. Wait for Circle CI to go green again.
|
||||
1. `grunt release `. (Note: This will publish the package by running `npm publish`.)
|
||||
|
||||
### Finally
|
||||
|
||||
For each of the above GitHub repos:
|
||||
1. Visit the releases page and find the tag just published.
|
||||
1. Paste in a link to the correct release notes for this release. The link should reference the blob and tag correctly, and the markdown file for the notes.
|
||||
1. If it is a pre-release, mark it as such.
|
||||
1. For core, attach the standalone zipfile.
|
||||
2. Paste in a link to the correct release notes for this release.
|
||||
3. If it is a pre-release, mark it as such.
|
||||
4. Attach the standalone zipfile.
|
||||
|
||||
@@ -11,7 +11,7 @@ module.exports = {
|
||||
},
|
||||
|
||||
files: [
|
||||
{ src: [ root("MIT.LICENSE") ] },
|
||||
{ src: [ root("LICENSE") ] },
|
||||
{
|
||||
src: [ "jasmine_favicon.png"],
|
||||
dest: standaloneLibDir,
|
||||
|
||||
@@ -45,10 +45,6 @@ module.exports = {
|
||||
src: ['src/boot/boot1.js'],
|
||||
dest: 'lib/jasmine-core/boot1.js'
|
||||
},
|
||||
nodeBoot: {
|
||||
src: ['src/boot/node_boot.js'],
|
||||
dest: 'lib/jasmine-core/node_boot.js'
|
||||
},
|
||||
options: {
|
||||
banner: license(),
|
||||
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
|
||||
a copy of this software and associated documentation files (the
|
||||
|
||||
@@ -6,47 +6,61 @@
|
||||
const jasmineRequire = require('./jasmine-core/jasmine.js');
|
||||
module.exports = jasmineRequire;
|
||||
|
||||
/**
|
||||
* Boots a copy of Jasmine and returns an object as described in {@link jasmine}.
|
||||
* @type {function}
|
||||
* @return {jasmine}
|
||||
*/
|
||||
module.exports.boot = require('./jasmine-core/node_boot.js');
|
||||
const boot = (function() {
|
||||
let jasmine, jasmineInterface;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* 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);
|
||||
return function bootWithoutGlobals(reinitialize) {
|
||||
if (!jasmineInterface || reinitialize === true) {
|
||||
jasmine = jasmineRequire.core(jasmineRequire);
|
||||
const env = jasmine.getEnv({ suppressLoadErrors: true });
|
||||
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'),
|
||||
fs = require('fs');
|
||||
|
||||
const rootPath = path.join(__dirname, 'jasmine-core'),
|
||||
bootFiles = ['boot0.js', 'boot1.js'],
|
||||
legacyBootFiles = ['boot.js'],
|
||||
nodeBootFiles = ['node_boot.js'],
|
||||
cssFiles = [],
|
||||
jsFiles = [],
|
||||
jsFilesToSkip = ['jasmine.js'].concat(bootFiles, legacyBootFiles, nodeBootFiles);
|
||||
jsFilesToSkip = ['jasmine.js'].concat(bootFiles, legacyBootFiles);
|
||||
|
||||
fs.readdirSync(rootPath).forEach(function(file) {
|
||||
if(fs.statSync(path.join(rootPath, file)).isFile()) {
|
||||
@@ -56,18 +70,18 @@ fs.readdirSync(rootPath).forEach(function(file) {
|
||||
break;
|
||||
case '.js':
|
||||
if (jsFilesToSkip.indexOf(file) < 0) {
|
||||
jsFiles.push(file);
|
||||
}
|
||||
jsFiles.push(file);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
module.exports.files = {
|
||||
self: __filename,
|
||||
path: rootPath,
|
||||
bootDir: rootPath,
|
||||
bootFiles: bootFiles,
|
||||
nodeBootFiles: nodeBootFiles,
|
||||
cssFiles: cssFiles,
|
||||
jsFiles: ['jasmine.js'].concat(jsFiles),
|
||||
imagesDir: path.join(__dirname, '../images')
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
/*
|
||||
Copyright (c) 2008-2022 Pivotal Labs
|
||||
Copyright (c) 2008-2019 Pivotal Labs
|
||||
Copyright (c) 2008-2025 The Jasmine developers
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
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-2025 The Jasmine developers
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
|
||||
@@ -1,24 +1,24 @@
|
||||
function Player() {
|
||||
}
|
||||
Player.prototype.play = function(song) {
|
||||
this.currentlyPlayingSong = song;
|
||||
this.isPlaying = true;
|
||||
};
|
||||
|
||||
Player.prototype.pause = function() {
|
||||
this.isPlaying = false;
|
||||
};
|
||||
|
||||
Player.prototype.resume = function() {
|
||||
if (this.isPlaying) {
|
||||
throw new Error("song is already playing");
|
||||
class Player {
|
||||
play(song) {
|
||||
this.currentlyPlayingSong = song;
|
||||
this.isPlaying = true;
|
||||
}
|
||||
|
||||
this.isPlaying = true;
|
||||
};
|
||||
pause() {
|
||||
this.isPlaying = false;
|
||||
}
|
||||
|
||||
Player.prototype.makeFavorite = function() {
|
||||
this.currentlyPlayingSong.persistFavoriteStatus(true);
|
||||
};
|
||||
resume() {
|
||||
if (this.isPlaying) {
|
||||
throw new Error('song is already playing');
|
||||
}
|
||||
|
||||
this.isPlaying = true;
|
||||
}
|
||||
|
||||
makeFavorite() {
|
||||
this.currentlyPlayingSong.persistFavoriteStatus(true);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Player;
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
function Song() {
|
||||
class Song {
|
||||
persistFavoriteStatus(value) {
|
||||
// something complicated
|
||||
throw new Error('not yet implemented');
|
||||
}
|
||||
}
|
||||
|
||||
Song.prototype.persistFavoriteStatus = function(value) {
|
||||
// something complicated
|
||||
throw new Error("not yet implemented");
|
||||
};
|
||||
|
||||
module.exports = Song;
|
||||
|
||||
@@ -3,11 +3,11 @@ beforeEach(function () {
|
||||
toBePlaying: function () {
|
||||
return {
|
||||
compare: function (actual, expected) {
|
||||
var player = actual;
|
||||
const player = actual;
|
||||
|
||||
return {
|
||||
pass: player.currentlyPlayingSong === expected && player.isPlaying
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,36 +1,37 @@
|
||||
describe("Player", function() {
|
||||
var Player = require('../../lib/jasmine_examples/Player');
|
||||
var Song = require('../../lib/jasmine_examples/Song');
|
||||
var player;
|
||||
var song;
|
||||
const Player = require('../../lib/jasmine_examples/Player');
|
||||
const Song = require('../../lib/jasmine_examples/Song');
|
||||
|
||||
describe('Player', function() {
|
||||
let player;
|
||||
let song;
|
||||
|
||||
beforeEach(function() {
|
||||
player = new Player();
|
||||
song = new Song();
|
||||
});
|
||||
|
||||
it("should be able to play a Song", function() {
|
||||
it('should be able to play a Song', function() {
|
||||
player.play(song);
|
||||
expect(player.currentlyPlayingSong).toEqual(song);
|
||||
|
||||
//demonstrates use of custom matcher
|
||||
// demonstrates use of custom matcher
|
||||
expect(player).toBePlaying(song);
|
||||
});
|
||||
|
||||
describe("when song has been paused", function() {
|
||||
describe('when song has been paused', function() {
|
||||
beforeEach(function() {
|
||||
player.play(song);
|
||||
player.pause();
|
||||
});
|
||||
|
||||
it("should indicate that the song is currently paused", function() {
|
||||
it('should indicate that the song is currently paused', function() {
|
||||
expect(player.isPlaying).toBeFalsy();
|
||||
|
||||
// demonstrates use of 'not' with a custom matcher
|
||||
expect(player).not.toBePlaying(song);
|
||||
});
|
||||
|
||||
it("should be possible to resume", function() {
|
||||
it('should be possible to resume', function() {
|
||||
player.resume();
|
||||
expect(player.isPlaying).toBeTruthy();
|
||||
expect(player.currentlyPlayingSong).toEqual(song);
|
||||
@@ -38,7 +39,7 @@ describe("Player", function() {
|
||||
});
|
||||
|
||||
// demonstrates use of spies to intercept and test method calls
|
||||
it("tells the current song if the user has made it a favorite", function() {
|
||||
it('tells the current song if the user has made it a favorite', function() {
|
||||
spyOn(song, 'persistFavoriteStatus');
|
||||
|
||||
player.play(song);
|
||||
@@ -48,13 +49,13 @@ describe("Player", function() {
|
||||
});
|
||||
|
||||
//demonstrates use of expected exceptions
|
||||
describe("#resume", function() {
|
||||
it("should throw an exception if song is already playing", function() {
|
||||
describe('#resume', function() {
|
||||
it('should throw an exception if song is already playing', function() {
|
||||
player.play(song);
|
||||
|
||||
expect(function() {
|
||||
player.resume();
|
||||
}).toThrowError("song is already playing");
|
||||
}).toThrowError('song is already playing');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,34 +1,34 @@
|
||||
describe("Player", function() {
|
||||
var player;
|
||||
var song;
|
||||
describe('Player', function() {
|
||||
let player;
|
||||
let song;
|
||||
|
||||
beforeEach(function() {
|
||||
player = new Player();
|
||||
song = new Song();
|
||||
});
|
||||
|
||||
it("should be able to play a Song", function() {
|
||||
it('should be able to play a Song', function() {
|
||||
player.play(song);
|
||||
expect(player.currentlyPlayingSong).toEqual(song);
|
||||
|
||||
//demonstrates use of custom matcher
|
||||
// demonstrates use of custom matcher
|
||||
expect(player).toBePlaying(song);
|
||||
});
|
||||
|
||||
describe("when song has been paused", function() {
|
||||
describe('when song has been paused', function() {
|
||||
beforeEach(function() {
|
||||
player.play(song);
|
||||
player.pause();
|
||||
});
|
||||
|
||||
it("should indicate that the song is currently paused", function() {
|
||||
it('should indicate that the song is currently paused', function() {
|
||||
expect(player.isPlaying).toBeFalsy();
|
||||
|
||||
// demonstrates use of 'not' with a custom matcher
|
||||
expect(player).not.toBePlaying(song);
|
||||
});
|
||||
|
||||
it("should be possible to resume", function() {
|
||||
it('should be possible to resume', function() {
|
||||
player.resume();
|
||||
expect(player.isPlaying).toBeTruthy();
|
||||
expect(player.currentlyPlayingSong).toEqual(song);
|
||||
@@ -36,7 +36,7 @@ describe("Player", function() {
|
||||
});
|
||||
|
||||
// demonstrates use of spies to intercept and test method calls
|
||||
it("tells the current song if the user has made it a favorite", function() {
|
||||
it('tells the current song if the user has made it a favorite', function() {
|
||||
spyOn(song, 'persistFavoriteStatus');
|
||||
|
||||
player.play(song);
|
||||
@@ -46,13 +46,13 @@ describe("Player", function() {
|
||||
});
|
||||
|
||||
//demonstrates use of expected exceptions
|
||||
describe("#resume", function() {
|
||||
it("should throw an exception if song is already playing", function() {
|
||||
describe('#resume', function() {
|
||||
it('should throw an exception if song is already playing', function() {
|
||||
player.play(song);
|
||||
|
||||
expect(function() {
|
||||
player.resume();
|
||||
}).toThrowError("song is already playing");
|
||||
}).toThrowError('song is already playing');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -3,7 +3,7 @@ beforeEach(function () {
|
||||
toBePlaying: function () {
|
||||
return {
|
||||
compare: function (actual, expected) {
|
||||
var player = actual;
|
||||
const player = actual;
|
||||
|
||||
return {
|
||||
pass: player.currentlyPlayingSong === expected && player.isPlaying
|
||||
|
||||
@@ -1,22 +1,22 @@
|
||||
function Player() {
|
||||
}
|
||||
Player.prototype.play = function(song) {
|
||||
this.currentlyPlayingSong = song;
|
||||
this.isPlaying = true;
|
||||
};
|
||||
|
||||
Player.prototype.pause = function() {
|
||||
this.isPlaying = false;
|
||||
};
|
||||
|
||||
Player.prototype.resume = function() {
|
||||
if (this.isPlaying) {
|
||||
throw new Error("song is already playing");
|
||||
class Player {
|
||||
play(song) {
|
||||
this.currentlyPlayingSong = song;
|
||||
this.isPlaying = true;
|
||||
}
|
||||
|
||||
this.isPlaying = true;
|
||||
};
|
||||
pause() {
|
||||
this.isPlaying = false;
|
||||
}
|
||||
|
||||
Player.prototype.makeFavorite = function() {
|
||||
this.currentlyPlayingSong.persistFavoriteStatus(true);
|
||||
};
|
||||
resume() {
|
||||
if (this.isPlaying) {
|
||||
throw new Error('song is already playing');
|
||||
}
|
||||
|
||||
this.isPlaying = true;
|
||||
}
|
||||
|
||||
makeFavorite() {
|
||||
this.currentlyPlayingSong.persistFavoriteStatus(true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
function Song() {
|
||||
class Song {
|
||||
persistFavoriteStatus(value) {
|
||||
// something complicated
|
||||
throw new Error('not yet implemented');
|
||||
}
|
||||
}
|
||||
|
||||
Song.prototype.persistFavoriteStatus = function(value) {
|
||||
// something complicated
|
||||
throw new Error("not yet implemented");
|
||||
};
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
/*
|
||||
Copyright (c) 2008-2022 Pivotal Labs
|
||||
Copyright (c) 2008-2019 Pivotal Labs
|
||||
Copyright (c) 2008-2025 The Jasmine developers
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
@@ -462,7 +463,11 @@ jasmineRequire.HtmlReporter = function(j$) {
|
||||
'tr',
|
||||
{},
|
||||
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)) {
|
||||
specDescription = 'SPEC HAS NO EXPECTATIONS ' + specDescription;
|
||||
}
|
||||
if (
|
||||
resultNode.result.status === 'pending' &&
|
||||
resultNode.result.pendingReason !== ''
|
||||
) {
|
||||
specDescription =
|
||||
specDescription +
|
||||
' PENDING WITH MESSAGE: ' +
|
||||
resultNode.result.pendingReason;
|
||||
if (resultNode.result.status === 'pending') {
|
||||
if (resultNode.result.pendingReason !== '') {
|
||||
specDescription +=
|
||||
' PENDING WITH MESSAGE: ' + resultNode.result.pendingReason;
|
||||
} else {
|
||||
specDescription += ' PENDING';
|
||||
}
|
||||
}
|
||||
specListNode.appendChild(
|
||||
createDom(
|
||||
|
||||
@@ -55,9 +55,6 @@ body {
|
||||
position: fixed;
|
||||
right: 100%;
|
||||
}
|
||||
.jasmine_html-reporter .jasmine-version {
|
||||
color: #aaa;
|
||||
}
|
||||
.jasmine_html-reporter .jasmine-banner {
|
||||
margin-top: 14px;
|
||||
}
|
||||
@@ -169,10 +166,11 @@ body {
|
||||
}
|
||||
.jasmine_html-reporter .jasmine-bar.jasmine-menu {
|
||||
background-color: #fff;
|
||||
color: #aaa;
|
||||
color: #000;
|
||||
}
|
||||
.jasmine_html-reporter .jasmine-bar.jasmine-menu a {
|
||||
color: #333;
|
||||
color: blue;
|
||||
text-decoration: underline;
|
||||
}
|
||||
.jasmine_html-reporter .jasmine-bar a {
|
||||
color: white;
|
||||
@@ -297,4 +295,7 @@ body {
|
||||
}
|
||||
.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;
|
||||
}
|
||||
.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",
|
||||
"license": "MIT",
|
||||
"version": "4.3.0",
|
||||
"version": "5.6.0",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/jasmine/jasmine.git"
|
||||
@@ -27,84 +27,35 @@
|
||||
"homepage": "https://jasmine.github.io",
|
||||
"main": "./lib/jasmine-core.js",
|
||||
"files": [
|
||||
"MIT.LICENSE",
|
||||
"LICENSE",
|
||||
"README.md",
|
||||
"images/*.{png,svg}",
|
||||
"lib/**/*.{js,css}",
|
||||
"package.json"
|
||||
],
|
||||
"devDependencies": {
|
||||
"eslint": "^7.32.0",
|
||||
"eslint": "^8.36.0",
|
||||
"eslint-plugin-compat": "^4.0.0",
|
||||
"glob": "^7.2.0",
|
||||
"glob": "^10.2.3",
|
||||
"grunt": "^1.0.4",
|
||||
"grunt-cli": "^1.3.2",
|
||||
"grunt-contrib-compress": "^2.0.0",
|
||||
"grunt-contrib-concat": "^2.0.0",
|
||||
"grunt-css-url-embed": "^1.11.1",
|
||||
"grunt-sass": "^3.0.2",
|
||||
"jasmine": "^4.1.0",
|
||||
"jasmine-browser-runner": "^1.0.0",
|
||||
"jsdom": "^19.0.0",
|
||||
"jasmine": "^5.0.0",
|
||||
"jasmine-browser-runner": "github:jasmine/jasmine-browser-runner",
|
||||
"jsdom": "^22.0.0",
|
||||
"load-grunt-tasks": "^5.1.0",
|
||||
"prettier": "1.17.1",
|
||||
"sass": "^1.45.1",
|
||||
"sass": "^1.58.3",
|
||||
"shelljs": "^0.8.3",
|
||||
"temp": "^0.9.0"
|
||||
},
|
||||
"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": [
|
||||
"Safari >= 14",
|
||||
"Safari >= 15",
|
||||
"Firefox >= 102",
|
||||
"last 2 Chrome versions",
|
||||
"last 2 Firefox versions",
|
||||
"Firefox >= 91",
|
||||
"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)_
|
||||
35
release_notes/5.3.0.md
Normal file
35
release_notes/5.3.0.md
Normal file
@@ -0,0 +1,35 @@
|
||||
# Jasmine Core 5.3.0 Release Notes
|
||||
|
||||
## Changes
|
||||
|
||||
* Improved performance in Safari
|
||||
* Merges [#2040](https://github.com/jasmine/jasmine/pull/2040) from @dcsaszar
|
||||
* Fixes [#2008](https://github.com/jasmine/jasmine/issues/2008)
|
||||
|
||||
* Improved performance in Playwright Webkit on Windows
|
||||
* Merges [#2034](https://github.com/jasmine/jasmine/pull/2034) from @m-akinc
|
||||
|
||||
* Throw if spying has no effect, as when spying on localStorage methods in Firefox and Safari 17
|
||||
* See [#2036](https://github.com/jasmine/jasmine/issues/2036) and [#2007](https://github.com/jasmine/jasmine/issues/2007)
|
||||
|
||||
|
||||
## Documentation improvements
|
||||
|
||||
* Added API reference for reporter capabilities
|
||||
|
||||
## Supported environments
|
||||
|
||||
This version has been tested in the following environments.
|
||||
|
||||
| Environment | Supported versions |
|
||||
|-------------------|--------------------|
|
||||
| Node | 18, 20, 22 |
|
||||
| Safari | 15-17 |
|
||||
| Chrome | 128 |
|
||||
| Firefox | 102, 115, 130 |
|
||||
| Edge | 128 |
|
||||
|
||||
|
||||
------
|
||||
|
||||
_Release Notes generated with _[Anchorman](http://github.com/infews/anchorman)_
|
||||
39
release_notes/5.4.0.md
Normal file
39
release_notes/5.4.0.md
Normal file
@@ -0,0 +1,39 @@
|
||||
# Jasmine Core 5.4.0 Release Notes
|
||||
|
||||
## Changes
|
||||
|
||||
* Fixed de-duplication of exception messages containing blank lines on Node and Chrome
|
||||
|
||||
This is particularly helpful when reporting testing-library errors, which
|
||||
have messages that contain blank lines and can be hundreds or even thousands
|
||||
of lines long.
|
||||
|
||||
* Document that the expected and actual properties of expectation results are deprecated
|
||||
|
||||
The values of these properties are not reliable in configurations where
|
||||
reporter messages are JSON serialized. They appear to have been seldom if ever
|
||||
used. They will be removed in the next major release.
|
||||
|
||||
* Added Firefox 128 (current ESR) to supported browsers
|
||||
|
||||
## Supported environments
|
||||
|
||||
This version has been tested in the following environments.
|
||||
|
||||
| Environment | Supported versions |
|
||||
|-------------------|-------------------------|
|
||||
| Node | 18, 20, 22 |
|
||||
| Safari | 15-17 |
|
||||
| Chrome | 129* |
|
||||
| Firefox | 102**, 115**, 128, 131* |
|
||||
| Edge | 129* |
|
||||
|
||||
\* Evergreen browser. Each version of Jasmine is tested against the latest
|
||||
version available at release time.<br>
|
||||
\** Environments that are past end of life are supported on a best-effort basis.
|
||||
They may be dropped in a future minor release of Jasmine if continued support
|
||||
becomes impractical.
|
||||
|
||||
------
|
||||
|
||||
_Release Notes generated with _[Anchorman](http://github.com/infews/anchorman)_
|
||||
34
release_notes/5.5.0.md
Normal file
34
release_notes/5.5.0.md
Normal file
@@ -0,0 +1,34 @@
|
||||
# Jasmine Core 5.5.0 Release Notes
|
||||
|
||||
## Changes
|
||||
|
||||
* Optionally enforce uniqueness of spec and suite names
|
||||
|
||||
This is off by default for backwards compatibility but can be enabled
|
||||
by setting the `forbidDuplicateNames` env config property to true.
|
||||
Fixes [#1633](https://github.com/jasmine/jasmine/issues/1633).
|
||||
|
||||
* Include property value mismatches in diffs even when there are missing or
|
||||
extra properties
|
||||
|
||||
## Supported environments
|
||||
|
||||
This version has been tested in the following environments.
|
||||
|
||||
| Environment | Supported versions |
|
||||
|-------------------|-------------------------|
|
||||
| Node | 18, 20, 22 |
|
||||
| Safari | 15-17 |
|
||||
| Chrome | 131* |
|
||||
| Firefox | 102**, 115**, 128, 132* |
|
||||
| Edge | 131* |
|
||||
|
||||
\* Evergreen browser. Each version of Jasmine is tested against the latest
|
||||
version available at release time.<br>
|
||||
\** Environments that are past end of life are supported on a best-effort basis.
|
||||
They may be dropped in a future minor release of Jasmine if continued support
|
||||
becomes impractical.
|
||||
|
||||
------
|
||||
|
||||
_Release Notes generated with _[Anchorman](http://github.com/infews/anchorman)_
|
||||
51
release_notes/5.6.0.md
Normal file
51
release_notes/5.6.0.md
Normal file
@@ -0,0 +1,51 @@
|
||||
# Jasmine Core 5.6.0 Release Notes
|
||||
|
||||
## Changes
|
||||
|
||||
* Added [toHaveNoOtherSpyInteractions](https://jasmine.github.io/api/5.6/matchers.html#toHaveNoOtherSpyInteractions) matcher
|
||||
* Merges [#2051](https://github.com/jasmine/jasmine/pull/2051) from @Eradev
|
||||
* Fixes [#1991](https://github.com/jasmine/jasmine/issues/1991)
|
||||
|
||||
* Added [toBeNullish](https://jasmine.github.io/api/5.6/matchers.html#toBeNullish) matcher
|
||||
* Merges [#2045](https://github.com/jasmine/jasmine/pull/2045) from @MattMcCherry
|
||||
|
||||
* Improved error messages when non-promises are passed to built-in async matchers
|
||||
* Merges [#2049](https://github.com/jasmine/jasmine/pull/2049) from @andiz2
|
||||
* Fixes [#2037](https://github.com/jasmine/jasmine/issues/2037)
|
||||
|
||||
* Added [toHaveClasses](https://jasmine.github.io/api/5.6/matchers.html#toHaveClasses) matcher
|
||||
* Merges [#2046](https://github.com/jasmine/jasmine/pull/2046) from @aYorky
|
||||
|
||||
## Documentation updates
|
||||
|
||||
* Demoted Safari to best-effort support
|
||||
|
||||
Due to limited availability of Safari versions for contributors and maintainers
|
||||
as well as in CI, Safari will be supported on the same best-effort basis as
|
||||
environments that are past end of life, such as previous Firefox ESR versions.
|
||||
See [this discussion](https://github.com/jasmine/jasmine/discussions/2050) for
|
||||
more information about why this change was made and what to expect.
|
||||
|
||||
|
||||
## Supported environments
|
||||
|
||||
This version has been tested in the following environments.
|
||||
|
||||
| Environment | Supported versions |
|
||||
|-------------------|-------------------------|
|
||||
| Node | 18, 20, 22 |
|
||||
| Safari | 15**, 16**, 17** |
|
||||
| Chrome | 133* |
|
||||
| Firefox | 102**, 115**, 128, 135* |
|
||||
| Edge | 132* |
|
||||
|
||||
\* Evergreen browser. Each version of Jasmine is tested against the latest
|
||||
version available at release time.<br>
|
||||
\** Supported on a best-effort basis. Support for these versions may be dropped
|
||||
if it becomes impractical, and bugs affecting only these versions may not be
|
||||
treated as release blockers.
|
||||
|
||||
|
||||
------
|
||||
|
||||
_Release Notes generated with _[Anchorman](http://github.com/infews/anchorman)_
|
||||
@@ -3,6 +3,7 @@
|
||||
run_browser() {
|
||||
browser=$1
|
||||
version=$2
|
||||
os="$3"
|
||||
description="$browser $version"
|
||||
if [ $version = "latest" ]; then
|
||||
version=""
|
||||
@@ -12,7 +13,7 @@ run_browser() {
|
||||
echo
|
||||
echo "Running $description"
|
||||
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
|
||||
echo "PASS: $description" >> "$passfile"
|
||||
@@ -23,12 +24,24 @@ run_browser() {
|
||||
|
||||
passfile=`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 128
|
||||
run_browser firefox 115
|
||||
run_browser firefox 102
|
||||
run_browser firefox 91
|
||||
run_browser safari 17
|
||||
run_browser safari 16
|
||||
run_browser safari 15
|
||||
run_browser safari 14
|
||||
run_browser MicrosoftEdge latest
|
||||
|
||||
echo
|
||||
|
||||
@@ -29,7 +29,7 @@ describe('AsyncExpectation', function() {
|
||||
|
||||
it('converts a fail to a pass', function() {
|
||||
const addExpectationResult = jasmine.createSpy('addExpectationResult'),
|
||||
actual = Promise.reject(),
|
||||
actual = Promise.reject(new Error('nope')),
|
||||
expectation = jasmineUnderTest.Expectation.asyncFactory({
|
||||
matchersUtil: new jasmineUnderTest.MatchersUtil({
|
||||
pp: function() {}
|
||||
@@ -138,7 +138,7 @@ describe('AsyncExpectation', function() {
|
||||
}
|
||||
},
|
||||
addExpectationResult = jasmine.createSpy('addExpectationResult'),
|
||||
actual = Promise.reject(),
|
||||
actual = Promise.reject(new Error('nope')),
|
||||
expectation = jasmineUnderTest.Expectation.asyncFactory({
|
||||
actual: actual,
|
||||
addExpectationResult: addExpectationResult,
|
||||
|
||||
@@ -9,160 +9,257 @@ describe('ClearStack', function() {
|
||||
});
|
||||
});
|
||||
|
||||
it('uses setImmediate when available', function() {
|
||||
const setImmediate = jasmine
|
||||
.createSpy('setImmediate')
|
||||
.and.callFake(function(fn) {
|
||||
fn();
|
||||
}),
|
||||
global = { setImmediate: setImmediate },
|
||||
clearStack = jasmineUnderTest.getClearStack(global);
|
||||
let called = false;
|
||||
|
||||
clearStack(function() {
|
||||
called = true;
|
||||
describe('in Safari', function() {
|
||||
usesQueueMicrotaskWithSetTimeout(function() {
|
||||
return {
|
||||
navigator: {
|
||||
userAgent:
|
||||
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit/605.0.8 (KHTML, like Gecko) Version/15.1 Safari/605.0.8'
|
||||
},
|
||||
// queueMicrotask should be used even though MessageChannel is present
|
||||
MessageChannel: fakeMessageChannel
|
||||
};
|
||||
});
|
||||
|
||||
expect(called).toBe(true);
|
||||
expect(setImmediate).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('uses setTimeout instead of setImmediate every 10 calls to make sure we release the CPU', function() {
|
||||
const setImmediate = jasmine.createSpy('setImmediate'),
|
||||
setTimeout = jasmine.createSpy('setTimeout'),
|
||||
global = { setImmediate: setImmediate, setTimeout: setTimeout },
|
||||
clearStack = jasmineUnderTest.getClearStack(global);
|
||||
|
||||
clearStack(function() {});
|
||||
clearStack(function() {});
|
||||
clearStack(function() {});
|
||||
clearStack(function() {});
|
||||
clearStack(function() {});
|
||||
clearStack(function() {});
|
||||
clearStack(function() {});
|
||||
clearStack(function() {});
|
||||
clearStack(function() {});
|
||||
|
||||
expect(setImmediate).toHaveBeenCalled();
|
||||
expect(setTimeout).not.toHaveBeenCalled();
|
||||
|
||||
clearStack(function() {});
|
||||
expect(setImmediate.calls.count()).toEqual(9);
|
||||
expect(setTimeout.calls.count()).toEqual(1);
|
||||
|
||||
clearStack(function() {});
|
||||
expect(setImmediate.calls.count()).toEqual(10);
|
||||
expect(setTimeout.calls.count()).toEqual(1);
|
||||
});
|
||||
|
||||
it('uses MessageChannels when available', function() {
|
||||
const fakeChannel = {
|
||||
port1: {},
|
||||
port2: {
|
||||
postMessage: function() {
|
||||
fakeChannel.port1.onmessage();
|
||||
}
|
||||
}
|
||||
},
|
||||
global = {
|
||||
MessageChannel: function() {
|
||||
return fakeChannel;
|
||||
}
|
||||
},
|
||||
clearStack = jasmineUnderTest.getClearStack(global);
|
||||
let called = false;
|
||||
|
||||
clearStack(function() {
|
||||
called = true;
|
||||
});
|
||||
|
||||
expect(called).toBe(true);
|
||||
});
|
||||
|
||||
it('uses setTimeout instead of MessageChannel every 10 calls to make sure we release the CPU', function() {
|
||||
const fakeChannel = {
|
||||
port1: {},
|
||||
port2: {
|
||||
postMessage: jasmine
|
||||
.createSpy('postMessage')
|
||||
.and.callFake(function() {
|
||||
fakeChannel.port1.onmessage();
|
||||
})
|
||||
}
|
||||
},
|
||||
setTimeout = jasmine.createSpy('setTimeout'),
|
||||
global = {
|
||||
it('uses MessageChannel to reduce setTimeout clamping', function() {
|
||||
const fakeChannel = fakeMessageChannel();
|
||||
spyOn(fakeChannel.port2, 'postMessage');
|
||||
const queueMicrotask = jasmine.createSpy('queueMicrotask');
|
||||
const global = {
|
||||
navigator: {
|
||||
userAgent:
|
||||
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit/605.0.8 (KHTML, like Gecko) Version/15.1 Safari/605.0.8'
|
||||
},
|
||||
MessageChannel: function() {
|
||||
return fakeChannel;
|
||||
},
|
||||
setTimeout: setTimeout
|
||||
},
|
||||
clearStack = jasmineUnderTest.getClearStack(global);
|
||||
queueMicrotask
|
||||
};
|
||||
|
||||
clearStack(function() {});
|
||||
clearStack(function() {});
|
||||
clearStack(function() {});
|
||||
clearStack(function() {});
|
||||
clearStack(function() {});
|
||||
clearStack(function() {});
|
||||
clearStack(function() {});
|
||||
clearStack(function() {});
|
||||
clearStack(function() {});
|
||||
const clearStack = jasmineUnderTest.getClearStack(global);
|
||||
|
||||
expect(fakeChannel.port2.postMessage).toHaveBeenCalled();
|
||||
expect(setTimeout).not.toHaveBeenCalled();
|
||||
for (let i = 0; i < 9; i++) {
|
||||
clearStack(function() {});
|
||||
}
|
||||
|
||||
clearStack(function() {});
|
||||
expect(fakeChannel.port2.postMessage.calls.count()).toEqual(9);
|
||||
expect(setTimeout.calls.count()).toEqual(1);
|
||||
expect(fakeChannel.port2.postMessage).not.toHaveBeenCalled();
|
||||
|
||||
clearStack(function() {});
|
||||
expect(fakeChannel.port2.postMessage.calls.count()).toEqual(10);
|
||||
expect(setTimeout.calls.count()).toEqual(1);
|
||||
clearStack(function() {});
|
||||
|
||||
expect(fakeChannel.port2.postMessage).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
|
||||
it('calls setTimeout when onmessage is called recursively', function() {
|
||||
const fakeChannel = {
|
||||
port1: {},
|
||||
port2: {
|
||||
postMessage: function() {
|
||||
fakeChannel.port1.onmessage();
|
||||
describe("in WebKit (Playwright's build for Windows)", function() {
|
||||
usesQueueMicrotaskWithSetTimeout(function() {
|
||||
return {
|
||||
navigator: {
|
||||
userAgent:
|
||||
'Mozilla/5.0 (Windows NT 6.2; Win64; x64) AppleWebKit/605.1.15 (KHTML, like Gecko)'
|
||||
},
|
||||
// queueMicrotask should be used even though MessageChannel is present
|
||||
MessageChannel: fakeMessageChannel
|
||||
};
|
||||
});
|
||||
});
|
||||
|
||||
describe('in browsers other than Safari', function() {
|
||||
usesMessageChannel(function() {
|
||||
return {
|
||||
navigator: {
|
||||
// Chrome's user agent string contains "Safari" so it's a good test case
|
||||
userAgent:
|
||||
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.0.0 Safari/537.36'
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
describe('when MessageChannel is unavailable', function() {
|
||||
usesQueueMicrotaskWithSetTimeout(function() {
|
||||
return {
|
||||
navigator: {
|
||||
userAgent: 'CERN-LineMode/2.15 libwww/2.17b3',
|
||||
MessageChannel: undefined
|
||||
}
|
||||
};
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('in Node', function() {
|
||||
usesQueueMicrotaskWithoutSetTimeout(function() {
|
||||
return {
|
||||
process: {
|
||||
versions: {
|
||||
node: '3.1415927'
|
||||
}
|
||||
}
|
||||
},
|
||||
setTimeout = jasmine.createSpy('setTimeout'),
|
||||
global = {
|
||||
};
|
||||
});
|
||||
});
|
||||
|
||||
function usesMessageChannel(makeGlobal) {
|
||||
it('uses MessageChannel', function() {
|
||||
const global = {
|
||||
...makeGlobal(),
|
||||
MessageChannel: fakeMessageChannel
|
||||
};
|
||||
const clearStack = jasmineUnderTest.getClearStack(global);
|
||||
let called = false;
|
||||
|
||||
clearStack(function() {
|
||||
called = true;
|
||||
});
|
||||
|
||||
expect(called).toBe(true);
|
||||
});
|
||||
|
||||
it('uses setTimeout instead of MessageChannel every 10 calls to make sure we release the CPU', function() {
|
||||
const fakeChannel = fakeMessageChannel();
|
||||
spyOn(fakeChannel.port2, 'postMessage');
|
||||
const setTimeout = jasmine.createSpy('setTimeout');
|
||||
const global = {
|
||||
...makeGlobal(),
|
||||
setTimeout,
|
||||
MessageChannel: function() {
|
||||
return fakeChannel;
|
||||
},
|
||||
setTimeout: setTimeout
|
||||
},
|
||||
clearStack = jasmineUnderTest.getClearStack(global),
|
||||
fn = jasmine.createSpy('second clearStack function');
|
||||
}
|
||||
};
|
||||
const clearStack = jasmineUnderTest.getClearStack(global);
|
||||
|
||||
clearStack(function() {
|
||||
clearStack(fn);
|
||||
for (let i = 0; i < 9; i++) {
|
||||
clearStack(function() {});
|
||||
}
|
||||
|
||||
expect(fakeChannel.port2.postMessage).toHaveBeenCalled();
|
||||
expect(setTimeout).not.toHaveBeenCalled();
|
||||
|
||||
clearStack(function() {});
|
||||
expect(fakeChannel.port2.postMessage).toHaveBeenCalledTimes(9);
|
||||
expect(setTimeout).toHaveBeenCalledTimes(1);
|
||||
|
||||
clearStack(function() {});
|
||||
expect(fakeChannel.port2.postMessage).toHaveBeenCalledTimes(10);
|
||||
expect(setTimeout).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
expect(fn).not.toHaveBeenCalled();
|
||||
expect(setTimeout).toHaveBeenCalledWith(fn, 0);
|
||||
});
|
||||
it('calls setTimeout when onmessage is called recursively', function() {
|
||||
const setTimeout = jasmine.createSpy('setTimeout');
|
||||
const global = {
|
||||
...makeGlobal(),
|
||||
setTimeout,
|
||||
MessageChannel: fakeMessageChannel
|
||||
};
|
||||
const clearStack = jasmineUnderTest.getClearStack(global);
|
||||
const fn = jasmine.createSpy('second clearStack function');
|
||||
|
||||
it('falls back to setTimeout', function() {
|
||||
const setTimeout = jasmine
|
||||
.createSpy('setTimeout')
|
||||
.and.callFake(function(fn) {
|
||||
clearStack(function() {
|
||||
clearStack(fn);
|
||||
});
|
||||
|
||||
expect(fn).not.toHaveBeenCalled();
|
||||
expect(setTimeout).toHaveBeenCalledWith(fn, 0);
|
||||
});
|
||||
}
|
||||
|
||||
function usesQueueMicrotaskWithSetTimeout(makeGlobal) {
|
||||
it('uses queueMicrotask', function() {
|
||||
const global = {
|
||||
...makeGlobal(),
|
||||
queueMicrotask: function(fn) {
|
||||
fn();
|
||||
}),
|
||||
global = { setTimeout: setTimeout },
|
||||
clearStack = jasmineUnderTest.getClearStack(global);
|
||||
let called = false;
|
||||
}
|
||||
};
|
||||
const clearStack = jasmineUnderTest.getClearStack(global);
|
||||
let called = false;
|
||||
|
||||
clearStack(function() {
|
||||
called = true;
|
||||
clearStack(function() {
|
||||
called = true;
|
||||
});
|
||||
|
||||
expect(called).toBe(true);
|
||||
});
|
||||
|
||||
expect(called).toBe(true);
|
||||
expect(setTimeout).toHaveBeenCalledWith(jasmine.any(Function), 0);
|
||||
});
|
||||
it('uses setTimeout instead of queueMicrotask every 10 calls to make sure we release the CPU', function() {
|
||||
const queueMicrotask = jasmine.createSpy('queueMicrotask');
|
||||
const setTimeout = jasmine.createSpy('setTimeout');
|
||||
const global = {
|
||||
...makeGlobal(),
|
||||
queueMicrotask,
|
||||
setTimeout
|
||||
};
|
||||
const clearStack = jasmineUnderTest.getClearStack(global);
|
||||
|
||||
for (let i = 0; i < 9; i++) {
|
||||
clearStack(function() {});
|
||||
}
|
||||
|
||||
expect(queueMicrotask).toHaveBeenCalled();
|
||||
expect(setTimeout).not.toHaveBeenCalled();
|
||||
|
||||
clearStack(function() {});
|
||||
expect(queueMicrotask).toHaveBeenCalledTimes(9);
|
||||
expect(setTimeout).toHaveBeenCalledTimes(1);
|
||||
|
||||
clearStack(function() {});
|
||||
expect(queueMicrotask).toHaveBeenCalledTimes(10);
|
||||
expect(setTimeout).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
}
|
||||
|
||||
function usesQueueMicrotaskWithoutSetTimeout(makeGlobal) {
|
||||
it('uses queueMicrotask', function() {
|
||||
const global = {
|
||||
...makeGlobal(),
|
||||
queueMicrotask: function(fn) {
|
||||
fn();
|
||||
}
|
||||
};
|
||||
const clearStack = jasmineUnderTest.getClearStack(global);
|
||||
let called = false;
|
||||
|
||||
clearStack(function() {
|
||||
called = true;
|
||||
});
|
||||
|
||||
expect(called).toBe(true);
|
||||
});
|
||||
|
||||
it('does not use setTimeout', function() {
|
||||
const queueMicrotask = jasmine.createSpy('queueMicrotask');
|
||||
const setTimeout = jasmine.createSpy('setTimeout');
|
||||
const global = {
|
||||
...makeGlobal(),
|
||||
queueMicrotask,
|
||||
setTimeout
|
||||
};
|
||||
const clearStack = jasmineUnderTest.getClearStack(global);
|
||||
|
||||
clearStack(function() {});
|
||||
clearStack(function() {});
|
||||
clearStack(function() {});
|
||||
clearStack(function() {});
|
||||
clearStack(function() {});
|
||||
clearStack(function() {});
|
||||
clearStack(function() {});
|
||||
clearStack(function() {});
|
||||
clearStack(function() {});
|
||||
clearStack(function() {});
|
||||
|
||||
expect(queueMicrotask).toHaveBeenCalledTimes(10);
|
||||
expect(setTimeout).not.toHaveBeenCalled();
|
||||
});
|
||||
}
|
||||
|
||||
function fakeMessageChannel() {
|
||||
const channel = {
|
||||
port1: {},
|
||||
port2: {
|
||||
postMessage: function() {
|
||||
channel.port1.onmessage();
|
||||
}
|
||||
}
|
||||
};
|
||||
return channel;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -80,6 +80,19 @@ describe('Env', function() {
|
||||
);
|
||||
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() {
|
||||
@@ -251,6 +264,15 @@ describe('Env', function() {
|
||||
|
||||
describe('#fdescribe', function() {
|
||||
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() {
|
||||
@@ -372,6 +394,13 @@ describe('Env', function() {
|
||||
env.fit('huge timeout', function() {}, 2147483648);
|
||||
}).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() {
|
||||
@@ -394,6 +423,28 @@ describe('Env', function() {
|
||||
env.beforeEach(function() {}, 2147483648);
|
||||
}).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() {
|
||||
@@ -416,6 +467,47 @@ describe('Env', function() {
|
||||
env.beforeAll(function() {}, 2147483648);
|
||||
}).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() {
|
||||
@@ -438,6 +530,28 @@ describe('Env', function() {
|
||||
env.afterEach(function() {}, 2147483648);
|
||||
}).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() {
|
||||
@@ -460,6 +574,47 @@ describe('Env', function() {
|
||||
env.afterAll(function() {}, 2147483648);
|
||||
}).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() {
|
||||
@@ -592,6 +747,32 @@ describe('Env', function() {
|
||||
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() {
|
||||
@@ -608,4 +789,46 @@ describe('Env', function() {
|
||||
).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() {
|
||||
const error = new Error('an error');
|
||||
const subject = new jasmineUnderTest.ExceptionFormatter({
|
||||
jasmineFile: jasmine.util.jasmineFile()
|
||||
});
|
||||
const result = subject.stack(error);
|
||||
jasmine.debugLog('Original stack trace: ' + error.stack);
|
||||
jasmine.debugLog('Filtered stack trace: ' + result);
|
||||
const lines = result.split('\n');
|
||||
|
||||
if (lines[0].match(/an error/)) {
|
||||
@@ -197,7 +218,7 @@ describe('ExceptionFormatter', function() {
|
||||
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');
|
||||
error.someProperty = 'hello there';
|
||||
|
||||
@@ -206,6 +227,19 @@ describe('ExceptionFormatter', function() {
|
||||
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() {
|
||||
it('filters the message from V8-style stack traces', function() {
|
||||
const error = {
|
||||
@@ -256,5 +290,61 @@ describe('ExceptionFormatter', function() {
|
||||
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() {
|
||||
it('calls the added handler on error', function() {
|
||||
const fakeGlobal = { onerror: null },
|
||||
handler = jasmine.createSpy('errorHandler'),
|
||||
errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
|
||||
const fakeGlobal = browserGlobal();
|
||||
const handler = jasmine.createSpy('errorHandler');
|
||||
const errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
|
||||
|
||||
errors.install();
|
||||
errors.pushListener(handler);
|
||||
|
||||
fakeGlobal.onerror('foo');
|
||||
|
||||
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);
|
||||
const error = new Error('nope');
|
||||
dispatchErrorEvent(fakeGlobal, { error });
|
||||
|
||||
expect(handler).toHaveBeenCalledWith(
|
||||
fooError.message,
|
||||
'foo.js',
|
||||
1,
|
||||
1,
|
||||
fooError
|
||||
jasmine.is(error),
|
||||
jasmine.objectContaining({ error: jasmine.is(error) })
|
||||
);
|
||||
});
|
||||
|
||||
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() {
|
||||
const fakeGlobal = { onerror: null },
|
||||
handler1 = jasmine.createSpy('errorHandler1'),
|
||||
handler2 = jasmine.createSpy('errorHandler2'),
|
||||
errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
|
||||
const fakeGlobal = browserGlobal();
|
||||
const handler1 = jasmine.createSpy('errorHandler1');
|
||||
const handler2 = jasmine.createSpy('errorHandler2');
|
||||
const errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
|
||||
|
||||
errors.install();
|
||||
errors.pushListener(handler1);
|
||||
errors.pushListener(handler2);
|
||||
|
||||
fakeGlobal.onerror('foo');
|
||||
const error = new Error('nope');
|
||||
dispatchErrorEvent(fakeGlobal, { error });
|
||||
|
||||
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() {
|
||||
const fakeGlobal = { onerror: null },
|
||||
handler1 = jasmine.createSpy('errorHandler1'),
|
||||
handler2 = jasmine.createSpy('errorHandler2'),
|
||||
errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
|
||||
const fakeGlobal = browserGlobal();
|
||||
const handler1 = jasmine.createSpy('errorHandler1');
|
||||
const handler2 = jasmine.createSpy('errorHandler2');
|
||||
const errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
|
||||
|
||||
errors.install();
|
||||
errors.pushListener(handler1);
|
||||
@@ -77,9 +67,13 @@ describe('GlobalErrors', function() {
|
||||
|
||||
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();
|
||||
});
|
||||
|
||||
@@ -90,31 +84,27 @@ describe('GlobalErrors', function() {
|
||||
}).toThrowError('popListener expects a listener');
|
||||
});
|
||||
|
||||
it('uninstalls itself, putting back a previous callback', function() {
|
||||
const originalCallback = jasmine.createSpy('error'),
|
||||
fakeGlobal = { onerror: originalCallback },
|
||||
errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
|
||||
|
||||
expect(fakeGlobal.onerror).toBe(originalCallback);
|
||||
it('uninstalls itself', function() {
|
||||
const fakeGlobal = browserGlobal();
|
||||
const errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
|
||||
function unrelatedListener() {}
|
||||
|
||||
errors.install();
|
||||
|
||||
expect(fakeGlobal.onerror).not.toBe(originalCallback);
|
||||
|
||||
fakeGlobal.addEventListener('error', unrelatedListener);
|
||||
errors.uninstall();
|
||||
|
||||
expect(fakeGlobal.onerror).toBe(originalCallback);
|
||||
expect(fakeGlobal.listeners_.error).toEqual([unrelatedListener]);
|
||||
});
|
||||
|
||||
it('rethrows the original error when there is no handler', function() {
|
||||
const fakeGlobal = {},
|
||||
errors = new jasmineUnderTest.GlobalErrors(fakeGlobal),
|
||||
originalError = new Error('nope');
|
||||
const fakeGlobal = browserGlobal();
|
||||
const errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
|
||||
const originalError = new Error('nope');
|
||||
|
||||
errors.install();
|
||||
|
||||
try {
|
||||
fakeGlobal.onerror(originalError);
|
||||
dispatchErrorEvent(fakeGlobal, { error: originalError });
|
||||
} catch (e) {
|
||||
expect(e).toBe(originalError);
|
||||
}
|
||||
@@ -286,128 +276,117 @@ describe('GlobalErrors', function() {
|
||||
|
||||
describe('Reporting unhandled promise rejections in the browser', function() {
|
||||
it('subscribes and unsubscribes from the unhandledrejection event', function() {
|
||||
const fakeGlobal = jasmine.createSpyObj('globalErrors', [
|
||||
'addEventListener',
|
||||
'removeEventListener',
|
||||
'onerror'
|
||||
]),
|
||||
errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
|
||||
const fakeGlobal = browserGlobal();
|
||||
const errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
|
||||
|
||||
errors.install();
|
||||
expect(fakeGlobal.addEventListener).toHaveBeenCalledWith(
|
||||
'unhandledrejection',
|
||||
expect(fakeGlobal.listeners_.unhandledrejection).toEqual([
|
||||
jasmine.any(Function)
|
||||
);
|
||||
]);
|
||||
|
||||
const addedListener = fakeGlobal.addEventListener.calls.argsFor(0)[1];
|
||||
errors.uninstall();
|
||||
|
||||
expect(fakeGlobal.removeEventListener).toHaveBeenCalledWith(
|
||||
'unhandledrejection',
|
||||
addedListener
|
||||
);
|
||||
expect(fakeGlobal.listeners_.unhandledrejection).toEqual([]);
|
||||
});
|
||||
|
||||
it('reports rejections whose reason is a string', function() {
|
||||
const fakeGlobal = jasmine.createSpyObj('globalErrors', [
|
||||
'addEventListener',
|
||||
'removeEventListener',
|
||||
'onerror'
|
||||
]),
|
||||
handler = jasmine.createSpy('errorHandler'),
|
||||
errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
|
||||
const fakeGlobal = browserGlobal();
|
||||
const handler = jasmine.createSpy('errorHandler');
|
||||
const errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
|
||||
|
||||
errors.install();
|
||||
errors.pushListener(handler);
|
||||
|
||||
const addedListener = fakeGlobal.addEventListener.calls.argsFor(0)[1];
|
||||
addedListener({ reason: 'nope' });
|
||||
const event = { 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() {
|
||||
const fakeGlobal = jasmine.createSpyObj('globalErrors', [
|
||||
'addEventListener',
|
||||
'removeEventListener',
|
||||
'onerror'
|
||||
]),
|
||||
handler = jasmine.createSpy('errorHandler'),
|
||||
errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
|
||||
const fakeGlobal = browserGlobal();
|
||||
const handler = jasmine.createSpy('errorHandler');
|
||||
const errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
|
||||
|
||||
errors.install();
|
||||
errors.pushListener(handler);
|
||||
|
||||
const addedListener = fakeGlobal.addEventListener.calls.argsFor(0)[1];
|
||||
const reason = new Error('bar');
|
||||
|
||||
addedListener({ reason: reason });
|
||||
const event = { reason };
|
||||
dispatchUnhandledRejectionEvent(fakeGlobal, event);
|
||||
|
||||
expect(handler).toHaveBeenCalledWith(
|
||||
jasmine.objectContaining({
|
||||
jasmineMessage: 'Unhandled promise rejection: Error: bar',
|
||||
message: reason.message,
|
||||
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() {
|
||||
it('overriding global.onerror intercepts rejections whose reason is a string', 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];
|
||||
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();
|
||||
});
|
||||
});
|
||||
function uncaughtExceptionListener(global) {
|
||||
// Grab the right listener
|
||||
expect(global.process.on.calls.argsFor(0)[0]).toEqual(
|
||||
'uncaughtException'
|
||||
);
|
||||
return global.process.on.calls.argsFor(0)[1];
|
||||
}
|
||||
});
|
||||
|
||||
describe('#setOverrideListener', function() {
|
||||
it('overrides the existing handlers in browsers until removed', function() {
|
||||
const fakeGlobal = { onerror: null };
|
||||
const fakeGlobal = browserGlobal();
|
||||
const handler0 = jasmine.createSpy('handler0');
|
||||
const handler1 = jasmine.createSpy('handler1');
|
||||
const overrideHandler = jasmine.createSpy('overrideHandler');
|
||||
@@ -417,19 +396,18 @@ describe('GlobalErrors', function() {
|
||||
errors.pushListener(handler0);
|
||||
errors.setOverrideListener(overrideHandler, () => {});
|
||||
errors.pushListener(handler1);
|
||||
fakeGlobal.onerror('foo');
|
||||
fakeGlobal.onerror(null, null, null, null, new Error('bar'));
|
||||
dispatchErrorEvent(fakeGlobal, { error: 'foo' });
|
||||
|
||||
expect(overrideHandler).toHaveBeenCalledWith('foo');
|
||||
expect(overrideHandler).toHaveBeenCalledWith(new Error('bar'));
|
||||
expect(handler0).not.toHaveBeenCalled();
|
||||
expect(handler1).not.toHaveBeenCalled();
|
||||
|
||||
errors.removeOverrideListener();
|
||||
|
||||
fakeGlobal.onerror('baz');
|
||||
const event = { error: 'baz' };
|
||||
dispatchErrorEvent(fakeGlobal, event);
|
||||
expect(overrideHandler).not.toHaveBeenCalledWith('baz');
|
||||
expect(handler1).toHaveBeenCalledWith('baz');
|
||||
expect(handler1).toHaveBeenCalledWith('baz', event);
|
||||
});
|
||||
|
||||
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() {
|
||||
const fakeGlobal = { onerror: null };
|
||||
const errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
|
||||
const errors = new jasmineUnderTest.GlobalErrors(browserGlobal());
|
||||
|
||||
errors.setOverrideListener(() => {}, () => {});
|
||||
expect(function() {
|
||||
@@ -541,9 +518,8 @@ describe('GlobalErrors', function() {
|
||||
|
||||
describe('#removeOverrideListener', function() {
|
||||
it("calls the handler's onRemove callback", function() {
|
||||
const fakeGlobal = { onerror: null };
|
||||
const onRemove = jasmine.createSpy('onRemove');
|
||||
const errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
|
||||
const errors = new jasmineUnderTest.GlobalErrors(browserGlobal());
|
||||
|
||||
errors.setOverrideListener(() => {}, onRemove);
|
||||
errors.removeOverrideListener();
|
||||
@@ -552,10 +528,43 @@ describe('GlobalErrors', function() {
|
||||
});
|
||||
|
||||
it('does not throw if there is no handler', function() {
|
||||
const fakeGlobal = { onerror: null };
|
||||
const errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
|
||||
const errors = new jasmineUnderTest.GlobalErrors(browserGlobal());
|
||||
|
||||
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();
|
||||
});
|
||||
|
||||
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() {
|
||||
it('does not log a deprecation or report a failure', function(done) {
|
||||
it('does not report a failure', function(done) {
|
||||
const queueableFn1 = {
|
||||
fn: function() {
|
||||
return Promise.resolve('not an error');
|
||||
}
|
||||
},
|
||||
failFn = jasmine.createSpy('fail'),
|
||||
deprecated = jasmine.createSpy('deprecated'),
|
||||
queueRunner = new jasmineUnderTest.QueueRunner({
|
||||
queueableFns: [queueableFn1],
|
||||
deprecated: deprecated,
|
||||
fail: failFn,
|
||||
onComplete: function() {
|
||||
expect(deprecated).not.toHaveBeenCalled();
|
||||
expect(failFn).not.toHaveBeenCalled();
|
||||
done();
|
||||
}
|
||||
@@ -406,17 +383,12 @@ describe('QueueRunner', function() {
|
||||
}
|
||||
},
|
||||
nextQueueableFn = { fn: jasmine.createSpy('nextFn') },
|
||||
deprecated = jasmine.createSpy('deprecated'),
|
||||
queueRunner = new jasmineUnderTest.QueueRunner({
|
||||
deprecated: deprecated,
|
||||
queueableFns: [queueableFn, nextQueueableFn]
|
||||
});
|
||||
queueRunner.execute();
|
||||
jasmine.clock().tick(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() {
|
||||
@@ -487,6 +459,32 @@ describe('QueueRunner', function() {
|
||||
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() {
|
||||
const queueableFn = {
|
||||
fn: function(done) {
|
||||
@@ -520,6 +518,40 @@ describe('QueueRunner', function() {
|
||||
clearStack.calls.argsFor(0)[0]();
|
||||
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() {
|
||||
@@ -623,11 +655,13 @@ describe('QueueRunner', function() {
|
||||
queueRunner.execute();
|
||||
|
||||
expect(onException).toHaveBeenCalledWith(
|
||||
'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.'
|
||||
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.'
|
||||
)
|
||||
);
|
||||
});
|
||||
|
||||
@@ -643,15 +677,17 @@ describe('QueueRunner', function() {
|
||||
queueRunner.execute();
|
||||
|
||||
expect(onException).toHaveBeenCalledWith(
|
||||
'An asynchronous ' +
|
||||
'before/it/after function was defined with the async keyword but ' +
|
||||
'also took a done callback. Either remove the done callback ' +
|
||||
'(recommended) or remove the async keyword.'
|
||||
new Error(
|
||||
'An asynchronous ' +
|
||||
'before/it/after function was defined with the async keyword but ' +
|
||||
'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'),
|
||||
onExceptionCallback = jasmine.createSpy('on exception callback'),
|
||||
queueRunner = new jasmineUnderTest.QueueRunner({
|
||||
@@ -659,24 +695,11 @@ describe('QueueRunner', function() {
|
||||
});
|
||||
|
||||
queueRunner.execute();
|
||||
queueRunner.handleFinalError(error.message, 'fake.js', 1, 1, error);
|
||||
queueRunner.handleFinalError(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() {
|
||||
const queueableFn = {
|
||||
type: 'queueable',
|
||||
|
||||
@@ -18,13 +18,12 @@ describe('ReportDispatcher', function() {
|
||||
queueRunnerFactory
|
||||
),
|
||||
reporter = jasmine.createSpyObj('reporter', ['foo', 'bar']),
|
||||
anotherReporter = jasmine.createSpyObj('reporter', ['foo', 'bar']),
|
||||
completeCallback = jasmine.createSpy('complete');
|
||||
anotherReporter = jasmine.createSpyObj('reporter', ['foo', 'bar']);
|
||||
|
||||
dispatcher.addReporter(reporter);
|
||||
dispatcher.addReporter(anotherReporter);
|
||||
|
||||
dispatcher.foo(123, 456, completeCallback);
|
||||
dispatcher.foo(123, 456);
|
||||
|
||||
expect(queueRunnerFactory).toHaveBeenCalledWith(
|
||||
jasmine.objectContaining({
|
||||
@@ -47,7 +46,7 @@ describe('ReportDispatcher', function() {
|
||||
|
||||
queueRunnerFactory.calls.reset();
|
||||
|
||||
dispatcher.bar('a', 'b', completeCallback);
|
||||
dispatcher.bar('a', 'b');
|
||||
|
||||
expect(queueRunnerFactory).toHaveBeenCalledWith(
|
||||
jasmine.objectContaining({
|
||||
@@ -91,11 +90,10 @@ describe('ReportDispatcher', function() {
|
||||
['foo', 'bar'],
|
||||
queueRunnerFactory
|
||||
),
|
||||
reporter = jasmine.createSpyObj('reporter', ['foo', 'bar']),
|
||||
completeCallback = jasmine.createSpy('complete');
|
||||
reporter = jasmine.createSpyObj('reporter', ['foo', 'bar']);
|
||||
|
||||
dispatcher.provideFallbackReporter(reporter);
|
||||
dispatcher.foo(123, 456, completeCallback);
|
||||
dispatcher.foo(123, 456);
|
||||
|
||||
expect(queueRunnerFactory).toHaveBeenCalledWith(
|
||||
jasmine.objectContaining({
|
||||
@@ -116,12 +114,11 @@ describe('ReportDispatcher', function() {
|
||||
queueRunnerFactory
|
||||
),
|
||||
reporter = jasmine.createSpyObj('reporter', ['foo', 'bar']),
|
||||
fallbackReporter = jasmine.createSpyObj('otherReporter', ['foo', 'bar']),
|
||||
completeCallback = jasmine.createSpy('complete');
|
||||
fallbackReporter = jasmine.createSpyObj('otherReporter', ['foo', 'bar']);
|
||||
|
||||
dispatcher.provideFallbackReporter(fallbackReporter);
|
||||
dispatcher.addReporter(reporter);
|
||||
dispatcher.foo(123, 456, completeCallback);
|
||||
dispatcher.foo(123, 456);
|
||||
|
||||
expect(queueRunnerFactory).toHaveBeenCalledWith(
|
||||
jasmine.objectContaining({
|
||||
@@ -143,11 +140,10 @@ describe('ReportDispatcher', function() {
|
||||
queueRunnerFactory
|
||||
),
|
||||
reporter1 = jasmine.createSpyObj('reporter1', ['foo', 'bar']),
|
||||
reporter2 = jasmine.createSpyObj('reporter2', ['foo', 'bar']),
|
||||
completeCallback = jasmine.createSpy('complete');
|
||||
reporter2 = jasmine.createSpyObj('reporter2', ['foo', 'bar']);
|
||||
|
||||
dispatcher.addReporter(reporter1);
|
||||
dispatcher.foo(123, completeCallback);
|
||||
dispatcher.foo(123);
|
||||
expect(queueRunnerFactory).toHaveBeenCalledWith(
|
||||
jasmine.objectContaining({
|
||||
queueableFns: [{ fn: jasmine.any(Function) }],
|
||||
@@ -161,7 +157,7 @@ describe('ReportDispatcher', function() {
|
||||
|
||||
dispatcher.clearReporters();
|
||||
dispatcher.addReporter(reporter2);
|
||||
dispatcher.bar(456, completeCallback);
|
||||
dispatcher.bar(456);
|
||||
|
||||
expect(queueRunnerFactory).toHaveBeenCalledWith(
|
||||
jasmine.objectContaining({
|
||||
|
||||
@@ -195,6 +195,8 @@ describe('Spec', function() {
|
||||
onStart: startCallback,
|
||||
resultCallback: resultCallback,
|
||||
description: 'with a spec',
|
||||
parentSuiteId: 'suite1',
|
||||
filename: 'someSpecFile.js',
|
||||
getSpecName: function() {
|
||||
return 'a suite with a spec';
|
||||
},
|
||||
@@ -219,6 +221,8 @@ describe('Spec', function() {
|
||||
status: 'pending',
|
||||
description: 'with a spec',
|
||||
fullName: 'a suite with a spec',
|
||||
parentSuiteId: 'suite1',
|
||||
filename: 'someSpecFile.js',
|
||||
failedExpectations: [],
|
||||
passedExpectations: [],
|
||||
deprecationWarnings: [],
|
||||
|
||||
@@ -94,6 +94,30 @@ describe('SpyRegistry', function() {
|
||||
}).not.toThrowError(/is not declared writable or has no setter/);
|
||||
});
|
||||
|
||||
it('throws if assigning to the property is a no-op', function() {
|
||||
const scope = {};
|
||||
|
||||
function original() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
Object.defineProperty(scope, 'myFunc', {
|
||||
get() {
|
||||
return original;
|
||||
},
|
||||
set() {}
|
||||
});
|
||||
|
||||
const spyRegistry = new jasmineUnderTest.SpyRegistry({
|
||||
createSpy: createSpy
|
||||
});
|
||||
expect(function() {
|
||||
spyRegistry.spyOn(scope, 'myFunc');
|
||||
}).toThrowError(
|
||||
"<spyOn> : Can't spy on myFunc because assigning to it had no effect"
|
||||
);
|
||||
});
|
||||
|
||||
it('overrides the method on the object and returns the spy', function() {
|
||||
const originalFunctionWasCalled = false,
|
||||
spyRegistry = new jasmineUnderTest.SpyRegistry({
|
||||
|
||||
@@ -51,6 +51,27 @@ describe('StackTrace', function() {
|
||||
]);
|
||||
});
|
||||
|
||||
it('understands Chrome/Edge style traces with messages containing blank lines', function() {
|
||||
const error = {
|
||||
message: 'line 1\n\nline 2',
|
||||
stack:
|
||||
'Error: line 1\n\nline 2\n' +
|
||||
' at UserContext.<anonymous> (http://localhost:8888/__spec__/core/UtilSpec.js:115:19)\n' +
|
||||
' at QueueRunner.run (http://localhost:8888/__jasmine__/jasmine.js:4320:20)'
|
||||
};
|
||||
|
||||
const result = new jasmineUnderTest.StackTrace(error);
|
||||
|
||||
expect(result.message).toEqual('Error: line 1\n\nline 2');
|
||||
const rawFrames = result.frames.map(function(f) {
|
||||
return f.raw;
|
||||
});
|
||||
expect(rawFrames).toEqual([
|
||||
' at UserContext.<anonymous> (http://localhost:8888/__spec__/core/UtilSpec.js:115:19)',
|
||||
' at QueueRunner.run (http://localhost:8888/__jasmine__/jasmine.js:4320:20)'
|
||||
]);
|
||||
});
|
||||
|
||||
it('understands Node style traces', function() {
|
||||
const error = {
|
||||
message: 'nope',
|
||||
@@ -95,7 +116,7 @@ describe('StackTrace', function() {
|
||||
]);
|
||||
});
|
||||
|
||||
it('understands Safari <=14/Firefox/Phantom-OS X style traces', function() {
|
||||
it('understands Safari <=14/Firefox style traces', function() {
|
||||
const error = {
|
||||
message: 'nope',
|
||||
stack:
|
||||
@@ -149,7 +170,7 @@ describe('StackTrace', function() {
|
||||
]);
|
||||
});
|
||||
|
||||
it('does not mistake gibberish for Safari/Firefox/Phantom-OS X style traces', function() {
|
||||
it('does not mistake gibberish for Safari/Firefox style traces', function() {
|
||||
const error = {
|
||||
message: 'nope',
|
||||
stack: 'randomcharsnotincludingwhitespace'
|
||||
@@ -159,36 +180,6 @@ describe('StackTrace', function() {
|
||||
expect(result.frames).toEqual([{ raw: error.stack }]);
|
||||
});
|
||||
|
||||
it('understands Phantom-Linux style traces', function() {
|
||||
const error = {
|
||||
message: 'nope',
|
||||
stack:
|
||||
' at UserContext.<anonymous> (http://localhost:8888/__spec__/core/UtilSpec.js:115:19)\n' +
|
||||
' at QueueRunner.run (http://localhost:8888/__jasmine__/jasmine.js:4320:20)'
|
||||
};
|
||||
|
||||
const result = new jasmineUnderTest.StackTrace(error);
|
||||
|
||||
expect(result.message).toBeFalsy();
|
||||
expect(result.style).toEqual('v8');
|
||||
expect(result.frames).toEqual([
|
||||
{
|
||||
raw:
|
||||
' at UserContext.<anonymous> (http://localhost:8888/__spec__/core/UtilSpec.js:115:19)',
|
||||
func: 'UserContext.<anonymous>',
|
||||
file: 'http://localhost:8888/__spec__/core/UtilSpec.js',
|
||||
line: 115
|
||||
},
|
||||
{
|
||||
raw:
|
||||
' at QueueRunner.run (http://localhost:8888/__jasmine__/jasmine.js:4320:20)',
|
||||
func: 'QueueRunner.run',
|
||||
file: 'http://localhost:8888/__jasmine__/jasmine.js',
|
||||
line: 4320
|
||||
}
|
||||
]);
|
||||
});
|
||||
|
||||
it('ignores blank lines', function() {
|
||||
const error = {
|
||||
message: 'nope',
|
||||
@@ -241,7 +232,7 @@ describe('StackTrace', function() {
|
||||
]);
|
||||
});
|
||||
|
||||
it('consideres different types of errors', function() {
|
||||
it('considers different types of errors', function() {
|
||||
const error = {
|
||||
message: 'nope',
|
||||
stack:
|
||||
|
||||
@@ -175,4 +175,202 @@ describe('SuiteBuilder', function() {
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
describe('Duplicate name handling', function() {
|
||||
describe('When forbidDuplicateNames is true', function() {
|
||||
let env;
|
||||
|
||||
beforeEach(function() {
|
||||
env = { configuration: () => ({ forbidDuplicateNames: true }) };
|
||||
});
|
||||
|
||||
it('forbids duplicate spec names', function() {
|
||||
const suiteBuilder = new jasmineUnderTest.SuiteBuilder({ env });
|
||||
|
||||
expect(function() {
|
||||
suiteBuilder.describe('a suite', function() {
|
||||
suiteBuilder.describe('a nested suite', function() {
|
||||
suiteBuilder.it('a spec');
|
||||
suiteBuilder.it('a spec');
|
||||
});
|
||||
});
|
||||
}).toThrowError(
|
||||
'Duplicate spec name "a spec" found in "a suite a nested suite"'
|
||||
);
|
||||
});
|
||||
|
||||
it('forbids duplicate spec names in the top suite', function() {
|
||||
const suiteBuilder = new jasmineUnderTest.SuiteBuilder({ env });
|
||||
|
||||
expect(function() {
|
||||
suiteBuilder.it('another spec');
|
||||
suiteBuilder.it('another spec');
|
||||
}).toThrowError(
|
||||
'Duplicate spec name "another spec" found in top suite'
|
||||
);
|
||||
});
|
||||
|
||||
it('forbids duplicate suite names', function() {
|
||||
const suiteBuilder = new jasmineUnderTest.SuiteBuilder({ env });
|
||||
|
||||
expect(function() {
|
||||
suiteBuilder.describe('a suite', function() {
|
||||
suiteBuilder.describe('a nested suite', function() {
|
||||
suiteBuilder.describe('another suite', function() {
|
||||
suiteBuilder.it('a spec');
|
||||
});
|
||||
suiteBuilder.describe('another suite', function() {
|
||||
suiteBuilder.it('a spec');
|
||||
});
|
||||
});
|
||||
});
|
||||
}).toThrowError(
|
||||
'Duplicate suite name "another suite" found in "a suite a nested suite"'
|
||||
);
|
||||
});
|
||||
|
||||
it('forbids duplicate suite names in the top suite', function() {
|
||||
const suiteBuilder = new jasmineUnderTest.SuiteBuilder({ env });
|
||||
|
||||
expect(function() {
|
||||
suiteBuilder.describe('a suite', function() {
|
||||
suiteBuilder.it('a spec');
|
||||
});
|
||||
suiteBuilder.describe('a suite', function() {
|
||||
suiteBuilder.it('a spec');
|
||||
});
|
||||
}).toThrowError('Duplicate suite name "a suite" found in top suite');
|
||||
});
|
||||
|
||||
it('allows spec and suite names to be duplicated in different suites', function() {
|
||||
const suiteBuilder = new jasmineUnderTest.SuiteBuilder({ env });
|
||||
|
||||
expect(function() {
|
||||
suiteBuilder.describe('suite a', function() {
|
||||
suiteBuilder.describe('dupe suite', function() {
|
||||
suiteBuilder.it('dupe spec');
|
||||
suiteBuilder.describe('child suite', function() {
|
||||
suiteBuilder.it('dupe spec');
|
||||
});
|
||||
});
|
||||
});
|
||||
suiteBuilder.describe('suite b', function() {
|
||||
suiteBuilder.describe('dupe suite', function() {
|
||||
suiteBuilder.it('dupe spec');
|
||||
});
|
||||
});
|
||||
}).not.toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
describe('When forbidDuplicateNames is false', function() {
|
||||
let env;
|
||||
|
||||
beforeEach(function() {
|
||||
env = { configuration: () => ({ forbidDuplicateNames: false }) };
|
||||
});
|
||||
|
||||
it('allows duplicate spec and suite names', function() {
|
||||
const suiteBuilder = new jasmineUnderTest.SuiteBuilder({ env });
|
||||
|
||||
expect(function() {
|
||||
suiteBuilder.describe('dupe suite', function() {
|
||||
suiteBuilder.it('dupe spec');
|
||||
suiteBuilder.it('dupe spec');
|
||||
});
|
||||
suiteBuilder.describe('dupe suite', function() {
|
||||
suiteBuilder.it('dupe spec');
|
||||
suiteBuilder.it('dupe spec');
|
||||
});
|
||||
}).not.toThrow();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
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([]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -378,4 +378,31 @@ describe('Suite', function() {
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#hasChildWithDescription', function() {
|
||||
it('returns true if there is a child with the given description', function() {
|
||||
const subject = new jasmineUnderTest.Suite({});
|
||||
const description = 'a spec';
|
||||
subject.addChild({ description });
|
||||
|
||||
expect(subject.hasChildWithDescription(description)).toBeTrue();
|
||||
});
|
||||
|
||||
it('returns false if there is no child with the given description', function() {
|
||||
const subject = new jasmineUnderTest.Suite({});
|
||||
subject.addChild({ description: 'a different spec' });
|
||||
|
||||
expect(subject.hasChildWithDescription('a spec')).toBeFalse();
|
||||
});
|
||||
|
||||
it('does not recurse into child suites', function() {
|
||||
const subject = new jasmineUnderTest.Suite({});
|
||||
const childSuite = new jasmineUnderTest.Suite({});
|
||||
subject.addChild(childSuite);
|
||||
const description = 'a spec';
|
||||
childSuite.addChild(description);
|
||||
|
||||
expect(subject.hasChildWithDescription('a spec')).toBeFalse();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -274,7 +274,7 @@ describe('TreeProcessor', function() {
|
||||
expect(result.valid).toBe(true);
|
||||
});
|
||||
|
||||
it('runs a single leaf', function() {
|
||||
it('runs a single leaf', async function() {
|
||||
const leaf = new Leaf(),
|
||||
node = new Node({ children: [leaf], userContext: { root: 'context' } }),
|
||||
queueRunner = jasmine.createSpy('queueRunner'),
|
||||
@@ -282,25 +282,27 @@ describe('TreeProcessor', function() {
|
||||
tree: node,
|
||||
runnableIds: [leaf.id],
|
||||
queueRunnerFactory: queueRunner
|
||||
}),
|
||||
treeComplete = jasmine.createSpy('treeComplete');
|
||||
});
|
||||
|
||||
processor.execute(treeComplete);
|
||||
const promise = processor.execute();
|
||||
|
||||
expect(queueRunner).toHaveBeenCalledWith({
|
||||
onComplete: treeComplete,
|
||||
onComplete: jasmine.any(Function),
|
||||
onException: jasmine.any(Function),
|
||||
userContext: { root: 'context' },
|
||||
queueableFns: [{ fn: jasmine.any(Function) }],
|
||||
onMultipleDone: null
|
||||
});
|
||||
|
||||
queueRunner.calls.mostRecent().args[0].queueableFns[0].fn('foo');
|
||||
|
||||
const queueRunnerArgs = queueRunner.calls.mostRecent().args[0];
|
||||
queueRunnerArgs.queueableFns[0].fn('foo');
|
||||
expect(leaf.execute).toHaveBeenCalledWith(queueRunner, 'foo', false, false);
|
||||
|
||||
queueRunnerArgs.onComplete();
|
||||
await expectAsync(promise).toBeResolvedTo(undefined);
|
||||
});
|
||||
|
||||
it('runs a node with no children', function() {
|
||||
it('runs a node with no children', async function() {
|
||||
const node = new Node({ userContext: { node: 'context' } }),
|
||||
root = new Node({ children: [node], userContext: { root: 'context' } }),
|
||||
nodeStart = jasmine.createSpy('nodeStart'),
|
||||
@@ -313,21 +315,20 @@ describe('TreeProcessor', function() {
|
||||
nodeComplete: nodeComplete,
|
||||
queueRunnerFactory: queueRunner
|
||||
}),
|
||||
treeComplete = jasmine.createSpy('treeComplete'),
|
||||
nodeDone = jasmine.createSpy('nodeDone');
|
||||
|
||||
processor.execute(treeComplete);
|
||||
const promise = processor.execute();
|
||||
|
||||
expect(queueRunner).toHaveBeenCalledWith({
|
||||
onComplete: treeComplete,
|
||||
onComplete: jasmine.any(Function),
|
||||
onException: jasmine.any(Function),
|
||||
userContext: { root: 'context' },
|
||||
queueableFns: [{ fn: jasmine.any(Function) }],
|
||||
onMultipleDone: null
|
||||
});
|
||||
|
||||
queueRunner.calls.mostRecent().args[0].queueableFns[0].fn(nodeDone);
|
||||
|
||||
const queueRunnerArgs = queueRunner.calls.mostRecent().args[0];
|
||||
queueRunnerArgs.queueableFns[0].fn(nodeDone);
|
||||
expect(queueRunner).toHaveBeenCalledWith({
|
||||
onComplete: jasmine.any(Function),
|
||||
onMultipleDone: null,
|
||||
@@ -348,6 +349,9 @@ describe('TreeProcessor', function() {
|
||||
{ my: 'result' },
|
||||
jasmine.any(Function)
|
||||
);
|
||||
|
||||
queueRunnerArgs.onComplete();
|
||||
await expectAsync(promise).toBeResolvedTo(undefined);
|
||||
});
|
||||
|
||||
it('runs a node with children', function() {
|
||||
|
||||
@@ -145,7 +145,7 @@ describe('base helpers', 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(
|
||||
false
|
||||
);
|
||||
@@ -179,7 +179,10 @@ describe('base helpers', function() {
|
||||
f2 = jasmine.createSpy('setTimeout callback for ' + (max + 1));
|
||||
|
||||
// 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);
|
||||
setTimeout(function() {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -349,7 +349,7 @@ describe('Matchers (Integration)', function() {
|
||||
});
|
||||
|
||||
verifyFailsAsync(function(env) {
|
||||
return env.expectAsync(Promise.reject()).toBeResolved();
|
||||
return env.expectAsync(Promise.reject(new Error('nope'))).toBeResolved();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -469,6 +469,24 @@ describe('Matchers (Integration)', function() {
|
||||
});
|
||||
});
|
||||
|
||||
describe('toBeNullish', function() {
|
||||
verifyPasses(function(env) {
|
||||
env.expect(undefined).toBeNullish();
|
||||
});
|
||||
|
||||
verifyPasses(function(env) {
|
||||
env.expect(null).toBeNullish();
|
||||
});
|
||||
|
||||
verifyFails(function(env) {
|
||||
env.expect(1).toBeNullish();
|
||||
});
|
||||
|
||||
verifyFails(function(env) {
|
||||
env.expect('').toBeNullish();
|
||||
});
|
||||
});
|
||||
|
||||
describe('toContain', function() {
|
||||
verifyPasses(function(env) {
|
||||
env.addCustomEqualityTester(function(a, b) {
|
||||
@@ -610,23 +628,31 @@ describe('Matchers (Integration)', function() {
|
||||
});
|
||||
|
||||
describe('toHaveClass', function() {
|
||||
beforeEach(function() {
|
||||
this.domHelpers = jasmine.getEnv().domHelpers();
|
||||
});
|
||||
|
||||
verifyPasses(function(env) {
|
||||
const domHelpers = jasmine.getEnv().domHelpers();
|
||||
const el = domHelpers.createElementWithClassName('foo');
|
||||
const el = specHelpers.domHelpers.createElementWithClassName('foo');
|
||||
env.expect(el).toHaveClass('foo');
|
||||
});
|
||||
|
||||
verifyFails(function(env) {
|
||||
const domHelpers = jasmine.getEnv().domHelpers();
|
||||
const el = domHelpers.createElementWithClassName('foo');
|
||||
const el = specHelpers.domHelpers.createElementWithClassName('foo');
|
||||
env.expect(el).toHaveClass('bar');
|
||||
});
|
||||
});
|
||||
|
||||
describe('toHaveClasses', function() {
|
||||
verifyPasses(function(env) {
|
||||
const el = specHelpers.domHelpers.createElementWithClassName(
|
||||
'foo bar baz'
|
||||
);
|
||||
env.expect(el).toHaveClasses(['bar', 'baz']);
|
||||
});
|
||||
|
||||
verifyFails(function(env) {
|
||||
const el = specHelpers.domHelpers.createElementWithClassName('foo bar');
|
||||
env.expect(el).toHaveClasses(['bar', 'baz']);
|
||||
});
|
||||
});
|
||||
|
||||
describe('toHaveSpyInteractions', function() {
|
||||
let spyObj;
|
||||
beforeEach(function() {
|
||||
@@ -649,6 +675,23 @@ describe('Matchers (Integration)', function() {
|
||||
});
|
||||
});
|
||||
|
||||
describe('toHaveNoOtherSpyInteractions', function() {
|
||||
let spyObj;
|
||||
|
||||
beforeEach(function() {
|
||||
spyObj = env.createSpyObj('NewClass', ['spyA', 'spyB']);
|
||||
});
|
||||
|
||||
verifyPasses(function(env) {
|
||||
env.expect(spyObj).toHaveNoOtherSpyInteractions();
|
||||
});
|
||||
|
||||
verifyFails(function(env) {
|
||||
spyObj.spyA();
|
||||
env.expect(spyObj).toHaveNoOtherSpyInteractions();
|
||||
});
|
||||
});
|
||||
|
||||
describe('toMatch', function() {
|
||||
verifyPasses(function(env) {
|
||||
env.expect('foo').toMatch(/oo$/);
|
||||
|
||||
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;
|
||||
|
||||
beforeEach(function() {
|
||||
jasmine.getEnv().registerIntegrationMatchers();
|
||||
specHelpers.registerIntegrationMatchers();
|
||||
env = new jasmineUnderTest.Env();
|
||||
env.configure({ random: false });
|
||||
});
|
||||
@@ -626,7 +626,7 @@ describe('spec running', function() {
|
||||
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 = [];
|
||||
let spec1;
|
||||
let spec2;
|
||||
@@ -648,13 +648,12 @@ describe('spec running', function() {
|
||||
actions.push('spec3');
|
||||
});
|
||||
|
||||
expect(function() {
|
||||
env.execute([spec2.id, spec3.id, spec1.id]);
|
||||
}).toThrowError(/beforeAll/);
|
||||
const promise = env.execute([spec2.id, spec3.id, spec1.id]);
|
||||
await expectAsync(promise).toBeRejectedWithError(/beforeAll/);
|
||||
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 = [];
|
||||
let spec1;
|
||||
let spec2;
|
||||
@@ -676,9 +675,8 @@ describe('spec running', function() {
|
||||
actions.push('spec3');
|
||||
});
|
||||
|
||||
expect(function() {
|
||||
env.execute([spec2.id, spec3.id, spec1.id]);
|
||||
}).toThrowError(/afterAll/);
|
||||
const promise = env.execute([spec2.id, spec3.id, spec1.id]);
|
||||
await expectAsync(promise).toBeRejectedWithError(/afterAll/);
|
||||
expect(actions).toEqual([]);
|
||||
});
|
||||
|
||||
|
||||
@@ -38,6 +38,8 @@ describe('toBePending', function() {
|
||||
return matcher.compare(actual);
|
||||
}
|
||||
|
||||
expect(f).toThrowError('Expected toBePending to be called on a promise.');
|
||||
expect(f).toThrowError(
|
||||
'Expected toBePending to be called on a promise but was on a string.'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -28,6 +28,8 @@ describe('toBeRejected', function() {
|
||||
return matcher.compare(actual);
|
||||
}
|
||||
|
||||
expect(f).toThrowError('Expected toBeRejected to be called on a promise.');
|
||||
expect(f).toThrowError(
|
||||
'Expected toBeRejected to be called on a promise but was on a string.'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -232,7 +232,7 @@ describe('#toBeRejectedWithError', function() {
|
||||
}
|
||||
|
||||
expect(f).toThrowError(
|
||||
'Expected toBeRejectedWithError to be called on a promise.'
|
||||
'Expected toBeRejectedWithError to be called on a promise but was on a string.'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -83,7 +83,7 @@ describe('#toBeRejectedWith', function() {
|
||||
}
|
||||
|
||||
expect(f).toThrowError(
|
||||
'Expected toBeRejectedWith to be called on a promise.'
|
||||
'Expected toBeRejectedWith to be called on a promise but was on a string.'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -35,6 +35,8 @@ describe('toBeResolved', function() {
|
||||
return matcher.compare(actual);
|
||||
}
|
||||
|
||||
expect(f).toThrowError('Expected toBeResolved to be called on a promise.');
|
||||
expect(f).toThrowError(
|
||||
'Expected toBeResolved to be called on a promise but was on a string.'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -93,7 +93,7 @@ describe('#toBeResolvedTo', function() {
|
||||
}
|
||||
|
||||
expect(f).toThrowError(
|
||||
'Expected toBeResolvedTo to be called on a promise.'
|
||||
'Expected toBeResolvedTo to be called on a promise but was on a string.'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -757,7 +757,9 @@ describe('matchersUtil', function() {
|
||||
const a2 = new TypedArrayCtor(2);
|
||||
a1[0] = a2[0] = 0;
|
||||
a1[1] = a2[1] = 1;
|
||||
expect(matchersUtil.equals(a1, a2)).toBe(true);
|
||||
const diffBuilder = new jasmineUnderTest.DiffBuilder();
|
||||
expect(matchersUtil.equals(a1, a2, diffBuilder)).toBe(true);
|
||||
jasmine.debugLog('Diff: ' + diffBuilder.getMessage());
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
57
spec/core/matchers/toBeNullishSpec.js
Normal file
57
spec/core/matchers/toBeNullishSpec.js
Normal file
@@ -0,0 +1,57 @@
|
||||
describe('toBeNullish', function() {
|
||||
it('passes for null values', function() {
|
||||
const matcher = jasmineUnderTest.matchers.toBeNullish();
|
||||
const result = matcher.compare(null);
|
||||
expect(result.pass).toBe(true);
|
||||
});
|
||||
|
||||
it('passes for undefined values', function() {
|
||||
const matcher = jasmineUnderTest.matchers.toBeNullish();
|
||||
const result = matcher.compare(void 0);
|
||||
expect(result.pass).toBe(true);
|
||||
});
|
||||
|
||||
it('fails when matching defined values', function() {
|
||||
const matcher = jasmineUnderTest.matchers.toBeNullish();
|
||||
const result = matcher.compare('foo');
|
||||
expect(result.pass).toBe(false);
|
||||
});
|
||||
|
||||
describe('falsy values', () => {
|
||||
it('fails for 0', function() {
|
||||
const matcher = jasmineUnderTest.matchers.toBeNullish();
|
||||
const result = matcher.compare(0);
|
||||
expect(result.pass).toBe(false);
|
||||
});
|
||||
|
||||
it('fails for -0', function() {
|
||||
const matcher = jasmineUnderTest.matchers.toBeNullish();
|
||||
const result = matcher.compare(-0);
|
||||
expect(result.pass).toBe(false);
|
||||
});
|
||||
|
||||
it('fails for empty string', function() {
|
||||
const matcher = jasmineUnderTest.matchers.toBeNullish();
|
||||
const result = matcher.compare('');
|
||||
expect(result.pass).toBe(false);
|
||||
});
|
||||
|
||||
it('fails for false', function() {
|
||||
const matcher = jasmineUnderTest.matchers.toBeNullish();
|
||||
const result = matcher.compare(false);
|
||||
expect(result.pass).toBe(false);
|
||||
});
|
||||
|
||||
it('fails for NaN', function() {
|
||||
const matcher = jasmineUnderTest.matchers.toBeNullish();
|
||||
const result = matcher.compare(NaN);
|
||||
expect(result.pass).toBe(false);
|
||||
});
|
||||
|
||||
it('fails for 0n', function() {
|
||||
const matcher = jasmineUnderTest.matchers.toBeNullish();
|
||||
const result = matcher.compare(BigInt(0));
|
||||
expect(result.pass).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -96,6 +96,17 @@ describe('toEqual', function() {
|
||||
expect(compareEquals(actual, expected).message).toEqual(message);
|
||||
});
|
||||
|
||||
it('reports mismatches as well as missing or extra properties', function() {
|
||||
const actual = { x: { z: 2 } },
|
||||
expected = { x: { y: 1, z: 3 } },
|
||||
message =
|
||||
'Expected $.x to have properties\n' +
|
||||
' y: 1\n' +
|
||||
'Expected $.x.z = 2 to equal 3.';
|
||||
|
||||
expect(compareEquals(actual, expected).message).toEqual(message);
|
||||
});
|
||||
|
||||
it('reports missing symbol properties', function() {
|
||||
const actual = { x: {} },
|
||||
expected = { x: { [Symbol('y')]: 1 } },
|
||||
|
||||
@@ -112,4 +112,20 @@ describe('toHaveBeenCalledBefore', function() {
|
||||
'Expected spy first spy to not have been called before spy second spy, but it was'
|
||||
);
|
||||
});
|
||||
|
||||
it('set the correct calls as verified when passing', function() {
|
||||
const matcher = jasmineUnderTest.matchers.toHaveBeenCalledBefore(),
|
||||
firstSpy = new jasmineUnderTest.Spy('first spy'),
|
||||
secondSpy = new jasmineUnderTest.Spy('second spy');
|
||||
|
||||
firstSpy();
|
||||
secondSpy();
|
||||
|
||||
matcher.compare(firstSpy, secondSpy);
|
||||
|
||||
expect(firstSpy.calls.count()).toBe(1);
|
||||
expect(firstSpy.calls.unverifiedCount()).toBe(0);
|
||||
expect(secondSpy.calls.count()).toBe(1);
|
||||
expect(secondSpy.calls.unverifiedCount()).toBe(0);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -105,4 +105,18 @@ describe('toHaveBeenCalledOnceWith', function() {
|
||||
matcher.compare(fn);
|
||||
}).toThrowError(/Expected a spy, but got Function./);
|
||||
});
|
||||
|
||||
it('set the correct calls as verified when passing', function() {
|
||||
const pp = jasmineUnderTest.makePrettyPrinter(),
|
||||
util = new jasmineUnderTest.MatchersUtil({ pp: pp }),
|
||||
matcher = jasmineUnderTest.matchers.toHaveBeenCalledOnceWith(util),
|
||||
calledSpy = new jasmineUnderTest.Spy('called-spy');
|
||||
|
||||
calledSpy('x');
|
||||
|
||||
matcher.compare(calledSpy, 'x');
|
||||
|
||||
expect(calledSpy.calls.count()).toBe(1);
|
||||
expect(calledSpy.calls.unverifiedCount()).toBe(0);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -50,4 +50,16 @@ describe('toHaveBeenCalled', function() {
|
||||
'Expected spy sample-spy to have been called.'
|
||||
);
|
||||
});
|
||||
|
||||
it('set the correct calls as verified when passing', function() {
|
||||
const matcher = jasmineUnderTest.matchers.toHaveBeenCalled(),
|
||||
spy = new jasmineUnderTest.Spy('sample-spy');
|
||||
|
||||
spy();
|
||||
|
||||
matcher.compare(spy);
|
||||
|
||||
expect(spy.calls.count()).toBe(1);
|
||||
expect(spy.calls.unverifiedCount()).toBe(0);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -87,4 +87,17 @@ describe('toHaveBeenCalledTimes', function() {
|
||||
' times.'
|
||||
);
|
||||
});
|
||||
|
||||
it('set the correct calls as verified when passing', function() {
|
||||
const matcher = jasmineUnderTest.matchers.toHaveBeenCalledTimes(),
|
||||
spy = new jasmineUnderTest.Spy('sample-spy');
|
||||
|
||||
spy();
|
||||
spy();
|
||||
|
||||
matcher.compare(spy, 2);
|
||||
|
||||
expect(spy.calls.count()).toBe(2);
|
||||
expect(spy.calls.unverifiedCount()).toBe(0);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -2,6 +2,7 @@ describe('toHaveBeenCalledWith', function() {
|
||||
it('passes when the actual was called with matching parameters', function() {
|
||||
const matchersUtil = {
|
||||
contains: jasmine.createSpy('delegated-contains').and.returnValue(true),
|
||||
equals: jasmine.createSpy('delegated-equals').and.returnValue(true),
|
||||
pp: jasmineUnderTest.makePrettyPrinter()
|
||||
},
|
||||
matcher = jasmineUnderTest.matchers.toHaveBeenCalledWith(matchersUtil),
|
||||
@@ -92,4 +93,20 @@ describe('toHaveBeenCalledWith', function() {
|
||||
matcher.compare(fn);
|
||||
}).toThrowError(/Expected a spy, but got Function./);
|
||||
});
|
||||
|
||||
it('set the correct calls as verified when passing', function() {
|
||||
const matchersUtil = {
|
||||
contains: jasmine.createSpy('delegated-contains').and.returnValue(true),
|
||||
equals: jasmine.createSpy('delegated-equals').and.returnValue(true),
|
||||
pp: jasmineUnderTest.makePrettyPrinter()
|
||||
},
|
||||
matcher = jasmineUnderTest.matchers.toHaveBeenCalledWith(matchersUtil),
|
||||
calledSpy = new jasmineUnderTest.Spy('called-spy');
|
||||
|
||||
calledSpy('a', 'b');
|
||||
matcher.compare(calledSpy, 'a', 'b');
|
||||
|
||||
expect(calledSpy.calls.count()).toBe(1);
|
||||
expect(calledSpy.calls.unverifiedCount()).toBe(0);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,12 +1,8 @@
|
||||
describe('toHaveClass', function() {
|
||||
beforeEach(function() {
|
||||
this.domHelpers = jasmine.getEnv().domHelpers();
|
||||
});
|
||||
|
||||
it('fails for a DOM element that lacks the expected class', function() {
|
||||
const matcher = jasmineUnderTest.matchers.toHaveClass(),
|
||||
result = matcher.compare(
|
||||
this.domHelpers.createElementWithClassName(''),
|
||||
specHelpers.domHelpers.createElementWithClassName(''),
|
||||
'foo'
|
||||
);
|
||||
|
||||
@@ -15,7 +11,7 @@ describe('toHaveClass', function() {
|
||||
|
||||
it('passes for a DOM element that has the expected class', function() {
|
||||
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, 'bar').pass).toBe(true);
|
||||
@@ -24,7 +20,7 @@ describe('toHaveClass', function() {
|
||||
|
||||
it('fails for a DOM element that only has other classes', function() {
|
||||
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);
|
||||
});
|
||||
@@ -42,7 +38,7 @@ describe('toHaveClass', function() {
|
||||
matcher.compare(undefined, 'foo');
|
||||
}).toThrowError('undefined is not a DOM element');
|
||||
|
||||
const textNode = this.domHelpers.document.createTextNode('');
|
||||
const textNode = specHelpers.domHelpers.document.createTextNode('');
|
||||
expect(function() {
|
||||
matcher.compare(textNode, 'foo');
|
||||
}).toThrowError('HTMLNode is not a DOM element');
|
||||
|
||||
48
spec/core/matchers/toHaveClassesSpec.js
Normal file
48
spec/core/matchers/toHaveClassesSpec.js
Normal file
@@ -0,0 +1,48 @@
|
||||
describe('toHaveClasses', function() {
|
||||
it('fails for a DOM element that lacks all the expected classes', function() {
|
||||
const matcher = jasmineUnderTest.matchers.toHaveClasses(),
|
||||
result = matcher.compare(
|
||||
specHelpers.domHelpers.createElementWithClassName(''),
|
||||
['foo', 'bar']
|
||||
);
|
||||
|
||||
expect(result.pass).toBe(false);
|
||||
});
|
||||
|
||||
it('passes for a DOM element that has all the expected classes', function() {
|
||||
const matcher = jasmineUnderTest.matchers.toHaveClasses(),
|
||||
el = specHelpers.domHelpers.createElementWithClassName('foo bar baz');
|
||||
|
||||
expect(matcher.compare(el, ['foo', 'bar']).pass).toBe(true);
|
||||
});
|
||||
|
||||
it('fails for a DOM element that only has some matching classes', function() {
|
||||
const matcher = jasmineUnderTest.matchers.toHaveClasses(),
|
||||
el = specHelpers.domHelpers.createElementWithClassName('foo bar');
|
||||
|
||||
expect(matcher.compare(el, ['foo', 'can']).pass).toBe(false);
|
||||
});
|
||||
|
||||
it('throws an exception when actual is not a DOM element', function() {
|
||||
const matcher = jasmineUnderTest.matchers.toHaveClasses({
|
||||
pp: jasmineUnderTest.makePrettyPrinter()
|
||||
});
|
||||
|
||||
expect(function() {
|
||||
matcher.compare('x', ['foo']);
|
||||
}).toThrowError("'x' is not a DOM element");
|
||||
|
||||
expect(function() {
|
||||
matcher.compare(undefined, ['foo']);
|
||||
}).toThrowError('undefined is not a DOM element');
|
||||
|
||||
const textNode = specHelpers.domHelpers.document.createTextNode('');
|
||||
expect(function() {
|
||||
matcher.compare(textNode, ['foo']);
|
||||
}).toThrowError('HTMLNode is not a DOM element');
|
||||
|
||||
expect(function() {
|
||||
matcher.compare({ classList: '' }, ['foo']);
|
||||
}).toThrowError("Object({ classList: '' }) is not a DOM element");
|
||||
});
|
||||
});
|
||||
154
spec/core/matchers/toHaveNoOtherSpyInteractionsSpec.js
Normal file
154
spec/core/matchers/toHaveNoOtherSpyInteractionsSpec.js
Normal file
@@ -0,0 +1,154 @@
|
||||
describe('toHaveNoOtherSpyInteractions', function() {
|
||||
it('passes when there are no spy interactions', function() {
|
||||
let matcher = jasmineUnderTest.matchers.toHaveNoOtherSpyInteractions();
|
||||
let spyObj = jasmineUnderTest
|
||||
.getEnv()
|
||||
.createSpyObj('NewClass', ['spyA', 'spyB']);
|
||||
|
||||
let result = matcher.compare(spyObj);
|
||||
expect(result.pass).toBeTrue();
|
||||
});
|
||||
|
||||
it('passes when there are multiple spy interactions where checked by toHaveBeenCalled', function() {
|
||||
let matcher = jasmineUnderTest.matchers.toHaveNoOtherSpyInteractions();
|
||||
let toHaveBeenCalledMatcher = jasmineUnderTest.matchers.toHaveBeenCalled();
|
||||
let spyObj = jasmineUnderTest
|
||||
.getEnv()
|
||||
.createSpyObj('NewClass', ['spyA', 'spyB']);
|
||||
|
||||
spyObj.spyA();
|
||||
spyObj.spyB();
|
||||
spyObj.spyA();
|
||||
toHaveBeenCalledMatcher.compare(spyObj.spyA);
|
||||
toHaveBeenCalledMatcher.compare(spyObj.spyB);
|
||||
let result = matcher.compare(spyObj);
|
||||
expect(result.pass).toBeTrue();
|
||||
});
|
||||
|
||||
it('fails when there are spy interactions', function() {
|
||||
const matchersUtil = new jasmineUnderTest.MatchersUtil({
|
||||
pp: jasmineUnderTest.makePrettyPrinter()
|
||||
});
|
||||
let matcher = jasmineUnderTest.matchers.toHaveNoOtherSpyInteractions(
|
||||
matchersUtil
|
||||
);
|
||||
let spyObj = jasmineUnderTest
|
||||
.getEnv()
|
||||
.createSpyObj('NewClass', ['spyA', 'spyB']);
|
||||
|
||||
spyObj.spyA('x');
|
||||
|
||||
let result = matcher.compare(spyObj);
|
||||
expect(result.pass).toBeFalse();
|
||||
expect(result.message).toEqual(
|
||||
'Expected a spy object to have no other spy interactions, but it had the following calls:\n' +
|
||||
" NewClass.spyA called with [ 'x' ]."
|
||||
);
|
||||
});
|
||||
|
||||
it('shows the right message is negated', function() {
|
||||
const matchersUtil = new jasmineUnderTest.MatchersUtil({
|
||||
pp: jasmineUnderTest.makePrettyPrinter()
|
||||
});
|
||||
let matcher = jasmineUnderTest.matchers.toHaveNoOtherSpyInteractions(
|
||||
matchersUtil
|
||||
);
|
||||
let spyObj = jasmineUnderTest
|
||||
.getEnv()
|
||||
.createSpyObj('NewClass', ['spyA', 'spyB']);
|
||||
|
||||
spyObj.spyA();
|
||||
spyObj.spyB();
|
||||
|
||||
let result = matcher.compare(spyObj);
|
||||
expect(result.pass).toBeFalse();
|
||||
expect(result.message).toEqual(
|
||||
'Expected a spy object to have no other spy interactions, but it had the following calls:\n' +
|
||||
' NewClass.spyA called with [ ],\n' +
|
||||
' NewClass.spyB called with [ ].'
|
||||
);
|
||||
});
|
||||
|
||||
it('passes when only non-observed spy object interactions are interacted', function() {
|
||||
let matcher = jasmineUnderTest.matchers.toHaveNoOtherSpyInteractions();
|
||||
let spyObj = jasmineUnderTest
|
||||
.getEnv()
|
||||
.createSpyObj('NewClass', ['spyA', 'spyB']);
|
||||
spyObj.otherMethod = function() {};
|
||||
|
||||
spyObj.otherMethod();
|
||||
|
||||
let result = matcher.compare(spyObj);
|
||||
expect(result.pass).toBeTrue();
|
||||
expect(result.message).toEqual(
|
||||
"Expected a spy object to have other spy interactions but it didn't."
|
||||
);
|
||||
});
|
||||
|
||||
it(`throws an error if a non-object is passed`, function() {
|
||||
let matcher = jasmineUnderTest.matchers.toHaveNoOtherSpyInteractions();
|
||||
|
||||
expect(function() {
|
||||
matcher.compare(true);
|
||||
}).toThrowError(Error, /Expected an object, but got/);
|
||||
|
||||
expect(function() {
|
||||
matcher.compare(123);
|
||||
}).toThrowError(Error, /Expected an object, but got/);
|
||||
|
||||
expect(function() {
|
||||
matcher.compare('string');
|
||||
}).toThrowError(Error, /Expected an object, but got/);
|
||||
});
|
||||
|
||||
it('throws an error if arguments are passed', function() {
|
||||
let matcher = jasmineUnderTest.matchers.toHaveNoOtherSpyInteractions();
|
||||
let spyObj = jasmineUnderTest
|
||||
.getEnv()
|
||||
.createSpyObj('mySpyObj', ['spyA', 'spyB']);
|
||||
|
||||
expect(function() {
|
||||
matcher.compare(spyObj, 'an argument');
|
||||
}).toThrowError(Error, /Does not take arguments/);
|
||||
});
|
||||
|
||||
it('throws an error if the spy object has no spies', function() {
|
||||
let matcher = jasmineUnderTest.matchers.toHaveNoOtherSpyInteractions();
|
||||
const spyObj = jasmineUnderTest
|
||||
.getEnv()
|
||||
.createSpyObj('mySpyObj', ['notSpy']);
|
||||
// Removing spy since spy objects cannot be created without spies.
|
||||
spyObj.notSpy = function() {};
|
||||
|
||||
expect(function() {
|
||||
matcher.compare(spyObj);
|
||||
}).toThrowError(
|
||||
Error,
|
||||
/Expected an object with spies, but object has no spies/
|
||||
);
|
||||
});
|
||||
|
||||
it('handles multiple interactions with a single spy', function() {
|
||||
const matchersUtil = new jasmineUnderTest.MatchersUtil({
|
||||
pp: jasmineUnderTest.makePrettyPrinter()
|
||||
}),
|
||||
matcher = jasmineUnderTest.matchers.toHaveNoOtherSpyInteractions(
|
||||
matchersUtil
|
||||
),
|
||||
toHaveBeenCalledWithMatcher = jasmineUnderTest.matchers.toHaveBeenCalledWith(
|
||||
matchersUtil
|
||||
),
|
||||
spyObj = jasmineUnderTest
|
||||
.getEnv()
|
||||
.createSpyObj('NewClass', ['spyA', 'spyB']);
|
||||
|
||||
spyObj.spyA('x');
|
||||
spyObj.spyA('y');
|
||||
|
||||
toHaveBeenCalledWithMatcher.compare(spyObj.spyA, 'x');
|
||||
|
||||
let result = matcher.compare(spyObj);
|
||||
|
||||
expect(result.pass).toBeFalse();
|
||||
});
|
||||
});
|
||||
@@ -15,6 +15,17 @@ describe('toHaveSize', function() {
|
||||
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() {
|
||||
const matcher = jasmineUnderTest.matchers.toHaveSize(),
|
||||
result = matcher.compare({ a: 1, b: 2 }, 2);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
(function(env) {
|
||||
(function() {
|
||||
function browserVersion(matchFn) {
|
||||
const userAgent = jasmine.getGlobal().navigator.userAgent;
|
||||
if (!userAgent) {
|
||||
@@ -10,7 +10,7 @@
|
||||
return match ? parseFloat(match[1]) : void 0;
|
||||
}
|
||||
|
||||
env.firefoxVersion = browserVersion(function(userAgent) {
|
||||
specHelpers.firefoxVersion = browserVersion(function(userAgent) {
|
||||
return /Firefox\/([0-9]{0,})/.exec(userAgent);
|
||||
});
|
||||
})(jasmine.getEnv());
|
||||
})();
|
||||
|
||||
@@ -1,24 +1,20 @@
|
||||
(function(env) {
|
||||
function domHelpers() {
|
||||
let doc;
|
||||
(function() {
|
||||
let doc;
|
||||
|
||||
if (typeof document !== 'undefined') {
|
||||
doc = document;
|
||||
} else {
|
||||
const JSDOM = require('jsdom').JSDOM;
|
||||
const dom = new JSDOM();
|
||||
doc = dom.window.document;
|
||||
}
|
||||
|
||||
return {
|
||||
document: doc,
|
||||
createElementWithClassName: function(className) {
|
||||
const el = this.document.createElement('div');
|
||||
el.className = className;
|
||||
return el;
|
||||
}
|
||||
};
|
||||
if (typeof document !== 'undefined') {
|
||||
doc = document;
|
||||
} else {
|
||||
const JSDOM = require('jsdom').JSDOM;
|
||||
const dom = new JSDOM();
|
||||
doc = dom.window.document;
|
||||
}
|
||||
|
||||
env.domHelpers = domHelpers;
|
||||
})(jasmine.getEnv());
|
||||
specHelpers.domHelpers = {
|
||||
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) {
|
||||
env.registerIntegrationMatchers = function() {
|
||||
(function() {
|
||||
specHelpers.registerIntegrationMatchers = function() {
|
||||
jasmine.addMatchers({
|
||||
toHaveFailedExpectationsForRunnable: function() {
|
||||
return {
|
||||
@@ -51,4 +51,4 @@
|
||||
}
|
||||
});
|
||||
};
|
||||
})(jasmine.getEnv());
|
||||
})();
|
||||
|
||||
@@ -12,14 +12,12 @@
|
||||
};
|
||||
|
||||
function getSourceFiles() {
|
||||
const src_files = ['core/**/*.js', 'version.js'].map(function(file) {
|
||||
return path.join(__dirname, '../../', 'src/', file);
|
||||
});
|
||||
const globs = ['../../src/core/**/*.js', '../../src/version.js'];
|
||||
const srcFiles = globs.flatMap(g => glob.sync(g, { cwd: __dirname }));
|
||||
|
||||
const files = src_files.flatMap(g => glob.sync(g));
|
||||
files.forEach(function(resolvedFile) {
|
||||
require(resolvedFile);
|
||||
});
|
||||
for (const file of srcFiles) {
|
||||
require(file);
|
||||
}
|
||||
}
|
||||
|
||||
getSourceFiles();
|
||||
|
||||
@@ -1411,6 +1411,23 @@ describe('HtmlReporter', function() {
|
||||
describe('and there are pending specs', function() {
|
||||
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() {
|
||||
container = document.createElement('div');
|
||||
const getContainer = function() {
|
||||
@@ -1429,21 +1446,10 @@ describe('HtmlReporter', function() {
|
||||
reporter.initialize();
|
||||
|
||||
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() {
|
||||
reportWithSpecStatus(pendingSpecStatus());
|
||||
const alertBar = container.querySelector('.jasmine-alert .jasmine-bar');
|
||||
|
||||
expect(alertBar.innerHTML).toMatch(
|
||||
@@ -1452,17 +1458,36 @@ describe('HtmlReporter', function() {
|
||||
});
|
||||
|
||||
it('reports no failure details', function() {
|
||||
reportWithSpecStatus(pendingSpecStatus());
|
||||
const specFailure = container.querySelector('.jasmine-failures');
|
||||
|
||||
expect(specFailure.childNodes.length).toEqual(0);
|
||||
});
|
||||
|
||||
it('displays the custom pending reason', function() {
|
||||
reportWithSpecStatus({
|
||||
...pendingSpecStatus(),
|
||||
pendingReason: 'my custom pending reason'
|
||||
});
|
||||
const pendingDetails = container.querySelector(
|
||||
'.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() {
|
||||
if (jasmine.getEnv().firefoxVersion) {
|
||||
if (specHelpers.firefoxVersion) {
|
||||
const pp = jasmineUnderTest.makePrettyPrinter();
|
||||
let err;
|
||||
try {
|
||||
|
||||
@@ -78,15 +78,10 @@ describe('npm package', function() {
|
||||
|
||||
it('has bootFiles', function() {
|
||||
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) {
|
||||
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() {
|
||||
@@ -118,7 +113,7 @@ describe('npm package', function() {
|
||||
const files = fs.readdirSync(path.resolve(this.tmpDir, 'package'));
|
||||
files.sort();
|
||||
expect(files).toEqual([
|
||||
'MIT.LICENSE',
|
||||
'LICENSE',
|
||||
'README.md',
|
||||
'images',
|
||||
'lib',
|
||||
|
||||
@@ -18,6 +18,7 @@ module.exports = {
|
||||
specDir: 'spec',
|
||||
specFiles: ['**/*[Ss]pec.js', '!npmPackage/**/*'],
|
||||
helpers: [
|
||||
'helpers/init.js',
|
||||
'helpers/generator.js',
|
||||
'helpers/BrowserFlags.js',
|
||||
'helpers/domHelpers.js',
|
||||
@@ -28,16 +29,19 @@ module.exports = {
|
||||
random: true,
|
||||
browser: {
|
||||
name: process.env.JASMINE_BROWSER || 'firefox',
|
||||
useSauce: process.env.USE_SAUCE === 'true',
|
||||
sauce: {
|
||||
name: `jasmine-core ${new Date().toISOString()}`,
|
||||
os: process.env.SAUCE_OS,
|
||||
useRemoteSeleniumGrid: process.env.USE_SAUCE === 'true',
|
||||
remoteSeleniumGrid: {
|
||||
url: 'https://ondemand.saucelabs.com/wd/hub',
|
||||
browserVersion: process.env.SAUCE_BROWSER_VERSION,
|
||||
build: `Core ${process.env.TRAVIS_BUILD_NUMBER || 'Ran locally'}`,
|
||||
tags: ['Jasmine-Core'],
|
||||
tunnelIdentifier: process.env.SAUCE_TUNNEL_IDENTIFIER,
|
||||
username: process.env.SAUCE_USERNAME,
|
||||
accessKey: process.env.SAUCE_ACCESS_KEY
|
||||
platformName: process.env.SAUCE_OS,
|
||||
'sauce:options': {
|
||||
name: `jasmine-core ${new Date().toISOString()}`,
|
||||
build: `Core ${process.env.CIRCLE_BUILD_NUM || 'Ran locally'}`,
|
||||
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"
|
||||
],
|
||||
"helpers": [
|
||||
"helpers/init.js",
|
||||
"helpers/domHelpers.js",
|
||||
"helpers/integrationMatchers.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;
|
||||
};
|
||||
@@ -125,6 +125,10 @@ getJasmineRequireObj().CallTracker = function(j$) {
|
||||
this.saveArgumentsByValue = function() {
|
||||
opts.cloneArgs = true;
|
||||
};
|
||||
|
||||
this.unverifiedCount = function() {
|
||||
return calls.reduce((count, call) => count + (call.verified ? 0 : 1), 0);
|
||||
};
|
||||
}
|
||||
|
||||
return CallTracker;
|
||||
|
||||
@@ -1,8 +1,64 @@
|
||||
getJasmineRequireObj().clearStack = function(j$) {
|
||||
const maxInlineCallCount = 10;
|
||||
|
||||
function messageChannelImpl(global, setTimeout) {
|
||||
const channel = new global.MessageChannel();
|
||||
function browserQueueMicrotaskImpl(global) {
|
||||
const unclampedSetTimeout = getUnclampedSetTimeout(global);
|
||||
const { queueMicrotask } = global;
|
||||
let currentCallCount = 0;
|
||||
return function clearStack(fn) {
|
||||
currentCallCount++;
|
||||
|
||||
if (currentCallCount < maxInlineCallCount) {
|
||||
queueMicrotask(fn);
|
||||
} else {
|
||||
currentCallCount = 0;
|
||||
unclampedSetTimeout(fn);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function nodeQueueMicrotaskImpl(global) {
|
||||
const { queueMicrotask } = global;
|
||||
|
||||
return function(fn) {
|
||||
queueMicrotask(fn);
|
||||
};
|
||||
}
|
||||
|
||||
function messageChannelImpl(global) {
|
||||
const { setTimeout } = global;
|
||||
const postMessage = getPostMessage(global);
|
||||
|
||||
let currentCallCount = 0;
|
||||
return function clearStack(fn) {
|
||||
currentCallCount++;
|
||||
|
||||
if (currentCallCount < maxInlineCallCount) {
|
||||
postMessage(fn);
|
||||
} else {
|
||||
currentCallCount = 0;
|
||||
setTimeout(fn);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function getUnclampedSetTimeout(global) {
|
||||
const { setTimeout } = global;
|
||||
if (j$.util.isUndefined(global.MessageChannel)) {
|
||||
return setTimeout;
|
||||
}
|
||||
|
||||
const postMessage = getPostMessage(global);
|
||||
return function unclampedSetTimeout(fn) {
|
||||
postMessage(function() {
|
||||
setTimeout(fn);
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
function getPostMessage(global) {
|
||||
const { MessageChannel, setTimeout } = global;
|
||||
const channel = new MessageChannel();
|
||||
let head = {};
|
||||
let tail = head;
|
||||
|
||||
@@ -13,7 +69,7 @@ getJasmineRequireObj().clearStack = function(j$) {
|
||||
delete head.task;
|
||||
|
||||
if (taskRunning) {
|
||||
global.setTimeout(task, 0);
|
||||
setTimeout(task, 0);
|
||||
} else {
|
||||
try {
|
||||
taskRunning = true;
|
||||
@@ -24,44 +80,44 @@ getJasmineRequireObj().clearStack = function(j$) {
|
||||
}
|
||||
};
|
||||
|
||||
let currentCallCount = 0;
|
||||
return function clearStack(fn) {
|
||||
currentCallCount++;
|
||||
|
||||
if (currentCallCount < maxInlineCallCount) {
|
||||
tail = tail.next = { task: fn };
|
||||
channel.port2.postMessage(0);
|
||||
} else {
|
||||
currentCallCount = 0;
|
||||
setTimeout(fn);
|
||||
}
|
||||
return function postMessage(fn) {
|
||||
tail = tail.next = { task: fn };
|
||||
channel.port2.postMessage(0);
|
||||
};
|
||||
}
|
||||
|
||||
function getClearStack(global) {
|
||||
let currentCallCount = 0;
|
||||
const realSetTimeout = global.setTimeout;
|
||||
const setTimeoutImpl = function clearStack(fn) {
|
||||
Function.prototype.apply.apply(realSetTimeout, [global, [fn, 0]]);
|
||||
};
|
||||
const NODE_JS =
|
||||
global.process &&
|
||||
global.process.versions &&
|
||||
typeof global.process.versions.node === 'string';
|
||||
|
||||
if (j$.isFunction_(global.setImmediate)) {
|
||||
const realSetImmediate = global.setImmediate;
|
||||
return function(fn) {
|
||||
currentCallCount++;
|
||||
// Windows builds of WebKit have a fairly generic user agent string when no application name is provided:
|
||||
// e.g. "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/605.1.15 (KHTML, like Gecko)"
|
||||
const SAFARI_OR_WIN_WEBKIT =
|
||||
global.navigator &&
|
||||
/(^((?!chrome|android).)*safari)|(Win64; x64\) AppleWebKit\/[0-9.]+ \(KHTML, like Gecko\)$)/i.test(
|
||||
global.navigator.userAgent
|
||||
);
|
||||
|
||||
if (currentCallCount < maxInlineCallCount) {
|
||||
realSetImmediate(fn);
|
||||
} else {
|
||||
currentCallCount = 0;
|
||||
|
||||
setTimeoutImpl(fn);
|
||||
}
|
||||
};
|
||||
} else if (!j$.util.isUndefined(global.MessageChannel)) {
|
||||
return messageChannelImpl(global, setTimeoutImpl);
|
||||
if (NODE_JS) {
|
||||
// Unlike browsers, Node doesn't require us to do a periodic setTimeout
|
||||
// so we avoid the overhead.
|
||||
return nodeQueueMicrotaskImpl(global);
|
||||
} else if (
|
||||
SAFARI_OR_WIN_WEBKIT ||
|
||||
j$.util.isUndefined(global.MessageChannel) /* tests */
|
||||
) {
|
||||
// queueMicrotask is dramatically faster than MessageChannel in Safari
|
||||
// and other WebKit-based browsers, such as the one distributed by Playwright
|
||||
// to test Safari-like behavior on Windows.
|
||||
// Some of our own integration tests provide a mock queueMicrotask in all
|
||||
// environments because it's simpler to mock than MessageChannel.
|
||||
return browserQueueMicrotaskImpl(global);
|
||||
} else {
|
||||
return setTimeoutImpl;
|
||||
// MessageChannel is faster than queueMicrotask in supported browsers
|
||||
// other than Safari.
|
||||
return messageChannelImpl(global);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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$) {
|
||||
function DelayedFunctionScheduler() {
|
||||
this.scheduledLookup_ = [];
|
||||
@@ -23,6 +26,9 @@ getJasmineRequireObj().DelayedFunctionScheduler = function(j$) {
|
||||
) {
|
||||
let f;
|
||||
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() {
|
||||
// eslint-disable-next-line no-eval
|
||||
return eval(funcToCall);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user