Compare commits
126 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0e604de0db | ||
|
|
e7ca9c5765 | ||
|
|
cbff6f95cb | ||
|
|
361640f52e | ||
|
|
e5d46e8624 | ||
|
|
8f6b3c49cc | ||
|
|
df6ab05280 | ||
|
|
9ea027dbff | ||
|
|
7cc7da4abc | ||
|
|
8be98e73ca | ||
|
|
52aaf63d22 | ||
|
|
a09fdd3284 | ||
|
|
89f3e9449d | ||
|
|
ba033c520d | ||
|
|
fc935e89c6 | ||
|
|
4bd2feda7d | ||
|
|
bcf699f145 | ||
|
|
d4f29491c9 | ||
|
|
5b1c932f89 | ||
|
|
7a8d6e44e3 | ||
|
|
dac349397e | ||
|
|
5ff7e7f9a1 | ||
|
|
7b2ab822c6 | ||
|
|
033260300a | ||
|
|
cb66b54f8b | ||
|
|
f4a8102a80 | ||
|
|
8f539f17b2 | ||
|
|
e5c543a0a1 | ||
|
|
7d697faf95 | ||
|
|
6f23151a5e | ||
|
|
e53c7ed8d1 | ||
|
|
dcd44a0edf | ||
|
|
f0a5ea9d0f | ||
|
|
491b513aa3 | ||
|
|
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 |
@@ -4,6 +4,10 @@
|
||||
version: 2.1
|
||||
|
||||
executors:
|
||||
node22:
|
||||
docker:
|
||||
- image: cimg/node:22.0.0
|
||||
working_directory: ~/workspace
|
||||
node20:
|
||||
docker:
|
||||
- image: cimg/node:20.0.0
|
||||
@@ -57,7 +61,7 @@ jobs:
|
||||
at: .
|
||||
- run:
|
||||
name: Run tests in parallel
|
||||
command: npx grunt execSpecsInParallel
|
||||
command: npm run test:parallel
|
||||
|
||||
test_browsers: &test_browsers
|
||||
executor: node18
|
||||
@@ -67,12 +71,14 @@ jobs:
|
||||
- run:
|
||||
name: Install Sauce Connect
|
||||
command: |
|
||||
cd /tmp
|
||||
curl https://saucelabs.com/downloads/sc-4.7.1-linux.tar.gz | tar zxf -
|
||||
chmod +x sc-4.7.1-linux/bin/sc
|
||||
tmpdir=$(mktemp -d)
|
||||
cd "$tmpdir"
|
||||
curl https://saucelabs.com/downloads/sauce-connect/5.2.2/sauce-connect-5.2.2_linux.x86_64.tar.gz | tar zxf -
|
||||
chmod +x sc
|
||||
mkdir ~/workspace/bin
|
||||
cp sc-4.7.1-linux/bin/sc ~/workspace/bin
|
||||
~/workspace/bin/sc --version
|
||||
cp sc ~/workspace/bin
|
||||
echo "Sauce Connect version info:"
|
||||
~/workspace/bin/sc version
|
||||
- run:
|
||||
name: Run tests
|
||||
command: |
|
||||
@@ -80,13 +86,13 @@ 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_WORKFLOW_JOB_ID
|
||||
scripts/start-sauce-connect sauce-pidfile
|
||||
export SAUCE_TUNNEL_NAME=$CIRCLE_WORKFLOW_JOB_ID
|
||||
scripts/start-sauce-connect
|
||||
set +o errexit
|
||||
scripts/run-all-browsers
|
||||
exitcode=$?
|
||||
set -o errexit
|
||||
scripts/stop-sauce-connect $(cat sauce-pidfile)
|
||||
scripts/stop-sauce-connect
|
||||
exit $exitcode
|
||||
|
||||
workflows:
|
||||
@@ -94,12 +100,20 @@ workflows:
|
||||
|
||||
push:
|
||||
jobs:
|
||||
- build:
|
||||
executor: node22
|
||||
name: build_node_22
|
||||
- build:
|
||||
executor: node20
|
||||
name: build_node_20
|
||||
- build:
|
||||
executor: node18
|
||||
name: build_node_18
|
||||
- test_node:
|
||||
executor: node22
|
||||
name: test_node_22
|
||||
requires:
|
||||
- build_node_22
|
||||
- test_node:
|
||||
executor: node20
|
||||
name: test_node_20
|
||||
@@ -115,6 +129,11 @@ workflows:
|
||||
name: test_parallel_node_18
|
||||
requires:
|
||||
- build_node_18
|
||||
- test_parallel:
|
||||
executor: node22
|
||||
name: test_parallel_node_22
|
||||
requires:
|
||||
- build_node_22
|
||||
- test_parallel:
|
||||
executor: node20
|
||||
name: test_parallel_node_20
|
||||
|
||||
@@ -3,6 +3,6 @@ charset = utf-8
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
|
||||
[*.{js, json, sh, yml}]
|
||||
[*.{js,mjs,json,sh,yml}]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
|
||||
33
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
33
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
@@ -13,7 +13,7 @@ body:
|
||||
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:
|
||||
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](https://jasmine.github.io/pages/faq.html#async). 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?
|
||||
@@ -22,19 +22,22 @@ body:
|
||||
## 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.**
|
||||
## Explain how to reproduce the bug
|
||||
|
||||
**Working steps to reproduce are required for all bug reports.** Please help us help you by creating complete but minimal instructions for reproducing the bug.
|
||||
|
||||
The steps to reproduce could be:
|
||||
|
||||
* A code snippet that reproduces the problem when run by itself in a newly generated empty `jasmine` or `jasmine-browser-runner` project, or in the standalone distribution.
|
||||
* A set of steps that reproduce the problem when followed exactly as they're written in an empty directory.
|
||||
* A link to a Git repository or zip/tar file containing a [minimal, reproducible example](https://stackoverflow.com/help/minimal-reproducible-example). This option is required for all bugs that can only be reproduced with third-party libraries, including Angular and Karma.
|
||||
|
||||
Please **test your steps** by starting with an empty directory and following them exactly as they're written. Bug reports with steps to reproduce that are unclear, don't work, or include an unreasonable amount of extraneous code will likely 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
|
||||
@@ -51,14 +54,6 @@ body:
|
||||
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:
|
||||
@@ -82,10 +77,11 @@ body:
|
||||
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
|
||||
label: Node.js and/or browser version
|
||||
placeholder: E.g. "node 16.2.0" or "Safari 15"
|
||||
validations:
|
||||
required: true
|
||||
@@ -96,3 +92,4 @@ body:
|
||||
placeholder: E.g. "Windows 10", "MacOS 12.5", "MCC Interim Linux 0.99.p8"
|
||||
validations:
|
||||
required: true
|
||||
|
||||
|
||||
3
.github/ISSUE_TEMPLATE/config.yml
vendored
3
.github/ISSUE_TEMPLATE/config.yml
vendored
@@ -1,5 +1,8 @@
|
||||
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.
|
||||
|
||||
73
.github/ISSUE_TEMPLATE/support_request.yml
vendored
73
.github/ISSUE_TEMPLATE/support_request.yml
vendored
@@ -1,73 +0,0 @@
|
||||
name: Question or Support Request
|
||||
description: I need help using Jasmine
|
||||
labels: ["question"]
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Jasmine is supported by volunteers working in their free time. Although we're generally willing to help, we're going to ask you to put in some effort first to help us help you.
|
||||
|
||||
## Troubleshooting
|
||||
Please take the time to rule out problems with your code or third party libraries before opening an issue. If you're running into an error, try to determine whether the error is coming from Jasmine, another library, or your own code.
|
||||
|
||||
Check the [FAQ](https://jasmine.github.io/pages/faq.html) and any other relevant [documentation](https://jasmine.github.io/pages/docs_home.html) to see if your question has already been answered. Consider searching [Stack Overflow](https://stackoverflow.com/questions/tagged/jasmine) and past issues in this repository for related questions as well.
|
||||
|
||||
## Special troubleshooting steps for asynchronous scenarios
|
||||
If the issue has to do with testing asynchronous code, please read the [async tutorial](https://jasmine.github.io/tutorials/async) and the async section of the FAQ. In particular, check for the following common errors:
|
||||
|
||||
* Are you trying to write a synchronous test for asynchronous code?
|
||||
* Does the test signal completion before the code under test finishes?
|
||||
* Do expectations run before the code that they're trying to verify?
|
||||
|
||||
## Consider asking Angular questions in an Angular forum
|
||||
|
||||
Questions like "how do I test this Angular service" are mostly about Angular, not Jasmine. You'll likely get better responses in an Angular forum. Here's a rule of thumb: If you can't demonstrate the problem without Angular, you probably have an Angular question.
|
||||
|
||||
## Try the latest version of Jasmine
|
||||
If at all possible, upgrade to the latest versions of `jasmine-core` and any other relevant packages (e.g. `jasmine`, `jasmine-browser-runner`).
|
||||
|
||||
## Put together a [minimal, reproducible example](https://stackoverflow.com/help/minimal-reproducible-example)
|
||||
Please help us help you by creating a minimal but complete setup that demonstrates the problem. Remove any code and libraries that aren't absolutely necessary, but make sure it doesn't depend on any code you haven't included. In many cases a simple code snippet is enough. In cases involving external libraries, *especially* Karma or Angular, we're likely to need a runable Git repository or jsbin/stackblitz/etc.
|
||||
|
||||
- type: textarea
|
||||
id: question
|
||||
attributes:
|
||||
label: Your question
|
||||
description: Clearly describe what you'd like help with.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: code-sample
|
||||
attributes:
|
||||
label: Example code that demonstrates the problem
|
||||
description: Please include either a code snippet that demonstrates the problem or a link to a repository or jsbin/stackblitz/etc containing a minimal, reproducible example as described above.
|
||||
render: JavaScript
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
id: jasmine-core-version
|
||||
attributes:
|
||||
label: jasmine-core version
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: other-versions
|
||||
attributes:
|
||||
label: Versions of other relevant packages
|
||||
placeholder: |
|
||||
jasmine-browser-runner 1.2.0
|
||||
fancy-reporter 132.4.8
|
||||
- type: input
|
||||
id: browser-or-node-version
|
||||
attributes:
|
||||
label: Node.js or browser version
|
||||
placeholder: E.g. "node 16.2.0" or "Safari 15"
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
id: os
|
||||
attributes:
|
||||
label: Operating System
|
||||
placeholder: E.g. "Windows 10", "MacOS 12.5", "MCC Interim Linux 0.99.p8"
|
||||
validations:
|
||||
required: true
|
||||
3
.prettierrc
Normal file
3
.prettierrc
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"singleQuote": true
|
||||
}
|
||||
104
Gruntfile.js
104
Gruntfile.js
@@ -1,104 +0,0 @@
|
||||
module.exports = function(grunt) {
|
||||
var pkg = require("./package.json");
|
||||
global.jasmineVersion = pkg.version;
|
||||
|
||||
grunt.initConfig({
|
||||
pkg: pkg,
|
||||
concat: require('./grunt/config/concat.js'),
|
||||
sass: require('./grunt/config/sass.js'),
|
||||
compress: require('./grunt/config/compress.js'),
|
||||
cssUrlEmbed: require('./grunt/config/cssUrlEmbed.js')
|
||||
});
|
||||
|
||||
require('load-grunt-tasks')(grunt);
|
||||
|
||||
grunt.loadTasks('grunt/tasks');
|
||||
|
||||
grunt.registerTask('default', ['sass:dist', "cssUrlEmbed"]);
|
||||
|
||||
grunt.registerTask('buildDistribution',
|
||||
'Builds and lints jasmine.js, jasmine-html.js, jasmine.css',
|
||||
[
|
||||
'sass:dist',
|
||||
"cssUrlEmbed",
|
||||
'concat'
|
||||
]
|
||||
);
|
||||
|
||||
grunt.registerTask("execSpecsInNode",
|
||||
"Run Jasmine core specs in Node.js",
|
||||
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.loadConfigFile('./spec/support/jasmine.json');
|
||||
jasmine.exitOnCompletion = false;
|
||||
jasmine.execute().then(
|
||||
result => done(result.overallStatus === 'passed'),
|
||||
err => {
|
||||
console.error(err);
|
||||
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() {
|
||||
require("shelljs").exec("node_modules/.bin/jasmine JASMINE_CONFIG_PATH=spec/support/jasmine-performance.json");
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
function verifyNoGlobals(fn) {
|
||||
const initialGlobals = Object.keys(global);
|
||||
fn();
|
||||
|
||||
const extras = Object.keys(global).filter(k => !initialGlobals.includes(k));
|
||||
|
||||
if (extras.length !== 0) {
|
||||
throw new Error('Globals were unexpectedly created: ' + extras.join(', '));
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
29
README.md
29
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 name="README"><img src="https://raw.githubusercontent.com/jasmine/jasmine/main/images/jasmine-horizontal.svg" width="400px" alt="Jasmine"></a>
|
||||
|
||||
# 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 | 18, 20 |
|
||||
| Safari | 15-16 |
|
||||
| Chrome | Evergreen |
|
||||
| Firefox | Evergreen, 102 |
|
||||
| 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).
|
||||
|
||||
11
RELEASE.md
11
RELEASE.md
@@ -41,13 +41,13 @@ When ready to release - specs are all green and the stories are done:
|
||||
|
||||
### Build standalone distribution
|
||||
|
||||
1. Build the standalone distribution with `grunt buildStandaloneDist`
|
||||
1. Build the standalone distribution with `npm run buildStandaloneDist`
|
||||
1. This will generate `dist/jasmine-standalone-<version>.zip`, which you will upload later (see "Finally" below).
|
||||
|
||||
### 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
|
||||
|
||||
@@ -55,11 +55,6 @@ 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
|
||||
|
||||
See <https://github.com/jasmine/jasmine-npm/blob/main/RELEASE.md>.
|
||||
|
||||
54
eslint.config.mjs
Normal file
54
eslint.config.mjs
Normal file
@@ -0,0 +1,54 @@
|
||||
import { defineConfig } from "eslint/config";
|
||||
import globals from "globals";
|
||||
import path from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import js from "@eslint/js";
|
||||
import { FlatCompat } from "@eslint/eslintrc";
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = path.dirname(__filename);
|
||||
const compat = new FlatCompat({
|
||||
baseDirectory: __dirname,
|
||||
recommendedConfig: js.configs.recommended,
|
||||
allConfig: js.configs.all
|
||||
});
|
||||
|
||||
export default defineConfig([{
|
||||
extends: compat.extends("plugin:compat/recommended"),
|
||||
|
||||
languageOptions: {
|
||||
globals: {
|
||||
...globals.browser,
|
||||
...globals.node,
|
||||
},
|
||||
|
||||
ecmaVersion: 2018,
|
||||
sourceType: "commonjs",
|
||||
},
|
||||
|
||||
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",
|
||||
"no-console": "error",
|
||||
},
|
||||
}]);
|
||||
@@ -1,57 +0,0 @@
|
||||
var standaloneLibDir = "lib/jasmine-" + jasmineVersion;
|
||||
|
||||
function root(path) { return "./" + path; }
|
||||
function libJasmineCore(path) { return root("lib/jasmine-core/" + path); }
|
||||
function dist(path) { return root("dist/" + path); }
|
||||
|
||||
module.exports = {
|
||||
standalone: {
|
||||
options: {
|
||||
archive: root("dist/jasmine-standalone-" + global.jasmineVersion + ".zip")
|
||||
},
|
||||
|
||||
files: [
|
||||
{ src: [ root("MIT.LICENSE") ] },
|
||||
{
|
||||
src: [ "jasmine_favicon.png"],
|
||||
dest: standaloneLibDir,
|
||||
expand: true,
|
||||
cwd: root("images")
|
||||
},
|
||||
{
|
||||
src: [
|
||||
"jasmine.js",
|
||||
"jasmine-html.js",
|
||||
"jasmine.css"
|
||||
],
|
||||
dest: standaloneLibDir,
|
||||
expand: true,
|
||||
cwd: libJasmineCore("")
|
||||
},
|
||||
{
|
||||
src: [ "boot0.js", "boot1.js" ],
|
||||
dest: standaloneLibDir,
|
||||
expand: true,
|
||||
cwd: libJasmineCore("")
|
||||
},
|
||||
{
|
||||
src: [ "SpecRunner.html" ],
|
||||
dest: root(""),
|
||||
expand: true,
|
||||
cwd: dist("tmp")
|
||||
},
|
||||
{
|
||||
src: [ "*.js" ],
|
||||
dest: "src",
|
||||
expand: true,
|
||||
cwd: libJasmineCore("example/src/")
|
||||
},
|
||||
{
|
||||
src: [ "*.js" ],
|
||||
dest: "spec",
|
||||
expand: true,
|
||||
cwd: libJasmineCore("example/spec/")
|
||||
}
|
||||
]
|
||||
}
|
||||
};
|
||||
@@ -1,56 +0,0 @@
|
||||
var grunt = require('grunt');
|
||||
|
||||
function license() {
|
||||
var currentYear = "" + new Date(Date.now()).getFullYear();
|
||||
|
||||
return grunt.template.process(
|
||||
grunt.file.read("grunt/templates/licenseBanner.js.jst"),
|
||||
{ data: { currentYear: currentYear}});
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
'jasmine-html': {
|
||||
src: [
|
||||
'src/html/requireHtml.js',
|
||||
'src/html/HtmlReporter.js',
|
||||
'src/html/HtmlSpecFilter.js',
|
||||
'src/html/ResultsNode.js',
|
||||
'src/html/QueryString.js',
|
||||
'src/html/**/*.js'
|
||||
],
|
||||
dest: 'lib/jasmine-core/jasmine-html.js'
|
||||
},
|
||||
jasmine: {
|
||||
src: [
|
||||
'src/core/requireCore.js',
|
||||
'src/core/matchers/requireMatchers.js',
|
||||
'src/core/base.js',
|
||||
'src/core/util.js',
|
||||
'src/core/Spec.js',
|
||||
'src/core/Order.js',
|
||||
'src/core/Env.js',
|
||||
'src/core/JsApiReporter.js',
|
||||
'src/core/PrettyPrinter',
|
||||
'src/core/Suite',
|
||||
'src/core/**/*.js',
|
||||
'src/version.js'
|
||||
],
|
||||
dest: 'lib/jasmine-core/jasmine.js'
|
||||
},
|
||||
boot0: {
|
||||
src: ['src/boot/boot0.js'],
|
||||
dest: 'lib/jasmine-core/boot0.js'
|
||||
},
|
||||
boot1: {
|
||||
src: ['src/boot/boot1.js'],
|
||||
dest: 'lib/jasmine-core/boot1.js'
|
||||
},
|
||||
options: {
|
||||
banner: license(),
|
||||
process: {
|
||||
data: {
|
||||
version: global.jasmineVersion
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -1,7 +0,0 @@
|
||||
module.exports = {
|
||||
encodeWithBaseDir: {
|
||||
files: {
|
||||
"lib/jasmine-core/jasmine.css": ["lib/jasmine-core/jasmine.css"]
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -1,13 +0,0 @@
|
||||
const sass = require('sass');
|
||||
|
||||
module.exports = {
|
||||
options: {
|
||||
implementation: sass,
|
||||
sourceComments: false
|
||||
},
|
||||
dist: {
|
||||
files: {
|
||||
"lib/jasmine-core/jasmine.css": "src/html/jasmine.scss"
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -1,31 +0,0 @@
|
||||
var grunt = require("grunt");
|
||||
|
||||
function standaloneTmpDir(path) { return "dist/tmp/" + path; }
|
||||
|
||||
grunt.registerTask("build:compileSpecRunner",
|
||||
"Processes the spec runner template and writes to a tmp file",
|
||||
function() {
|
||||
var runnerHtml = grunt.template.process(
|
||||
grunt.file.read("grunt/templates/SpecRunner.html.jst"),
|
||||
{ data: { jasmineVersion: global.jasmineVersion }});
|
||||
|
||||
grunt.file.write(standaloneTmpDir("SpecRunner.html"), runnerHtml);
|
||||
}
|
||||
);
|
||||
|
||||
grunt.registerTask("build:cleanSpecRunner",
|
||||
"Deletes the tmp spec runner file",
|
||||
function() {
|
||||
grunt.file.delete(standaloneTmpDir(""));
|
||||
}
|
||||
);
|
||||
|
||||
grunt.registerTask("buildStandaloneDist",
|
||||
"Builds a standalone distribution",
|
||||
[
|
||||
"buildDistribution",
|
||||
"build:compileSpecRunner",
|
||||
"compress:standalone",
|
||||
"build:cleanSpecRunner"
|
||||
]
|
||||
);
|
||||
@@ -1,5 +1,6 @@
|
||||
/*
|
||||
Copyright (c) 2008-2023 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
|
||||
@@ -20,6 +21,7 @@ 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
This file starts the process of "booting" Jasmine. It initializes Jasmine,
|
||||
makes its globals available, and creates the env. This file should be loaded
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
/*
|
||||
Copyright (c) 2008-2023 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
|
||||
@@ -20,6 +21,7 @@ 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
This file finishes 'booting' Jasmine, performing all of the necessary
|
||||
initialization before executing the loaded environment and all of a project's
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
/*
|
||||
Copyright (c) 2008-2023 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
|
||||
@@ -20,6 +21,7 @@ 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.
|
||||
*/
|
||||
|
||||
// eslint-disable-next-line no-var
|
||||
var jasmineRequire = window.jasmineRequire || require('./jasmine.js');
|
||||
|
||||
@@ -154,8 +156,10 @@ jasmineRequire.HtmlReporter = function(j$) {
|
||||
if (noExpectations(result)) {
|
||||
const noSpecMsg = "Spec '" + result.fullName + "' has no expectations.";
|
||||
if (result.status === 'failed') {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error(noSpecMsg);
|
||||
} else {
|
||||
// eslint-disable-next-line no-console
|
||||
console.warn(noSpecMsg);
|
||||
}
|
||||
}
|
||||
@@ -462,7 +466,11 @@ jasmineRequire.HtmlReporter = function(j$) {
|
||||
'tr',
|
||||
{},
|
||||
createDom('td', {}, entry.timestamp.toString()),
|
||||
createDom('td', {}, entry.message)
|
||||
createDom(
|
||||
'td',
|
||||
{ className: 'jasmine-debug-log-msg' },
|
||||
entry.message
|
||||
)
|
||||
)
|
||||
);
|
||||
});
|
||||
|
||||
@@ -295,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
82
package.json
82
package.json
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "jasmine-core",
|
||||
"license": "MIT",
|
||||
"version": "5.1.0",
|
||||
"version": "5.7.0",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/jasmine/jasmine.git"
|
||||
@@ -15,9 +15,11 @@
|
||||
],
|
||||
"scripts": {
|
||||
"posttest": "eslint \"src/**/*.js\" \"spec/**/*.js\" && prettier --check \"src/**/*.js\" \"spec/**/*.js\"",
|
||||
"test": "grunt --stack execSpecsInNode",
|
||||
"test": "node scripts/runSpecsInNode.js",
|
||||
"test:parallel": "node scripts/runSpecsInParallel.js",
|
||||
"cleanup": "prettier --write \"src/**/*.js\" \"spec/**/*.js\"",
|
||||
"build": "grunt buildDistribution",
|
||||
"build": "node scripts/buildDistribution.js",
|
||||
"buildStandaloneDist": "node scripts/buildStandaloneDist.js",
|
||||
"serve": "node spec/support/localJasmineBrowser.js",
|
||||
"serve:performance": "node spec/support/localJasmineBrowser.js jasmine-browser-performance.json",
|
||||
"ci": "node spec/support/ci.js",
|
||||
@@ -27,79 +29,29 @@
|
||||
"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": "^8.36.0",
|
||||
"eslint-plugin-compat": "^4.0.0",
|
||||
"@eslint/eslintrc": "^3.3.1",
|
||||
"@eslint/js": "^9.24.0",
|
||||
"archiver": "^7.0.1",
|
||||
"css-url-embed": "^0.1.0",
|
||||
"ejs": "^3.1.10",
|
||||
"eslint": "^9.24.0",
|
||||
"eslint-plugin-compat": "^6.0.2",
|
||||
"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",
|
||||
"globals": "^16.0.0",
|
||||
"jasmine": "^5.0.0",
|
||||
"jasmine-browser-runner": "github:jasmine/jasmine-browser-runner",
|
||||
"jsdom": "^22.0.0",
|
||||
"load-grunt-tasks": "^5.1.0",
|
||||
"jsdom": "^26.0.0",
|
||||
"prettier": "1.17.1",
|
||||
"rimraf": "^5.0.10",
|
||||
"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",
|
||||
"no-debugger": "error"
|
||||
}
|
||||
"shelljs": "^0.9.2"
|
||||
},
|
||||
"browserslist": [
|
||||
"Safari >= 15",
|
||||
|
||||
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)_
|
||||
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)_
|
||||
64
release_notes/5.7.0.md
Normal file
64
release_notes/5.7.0.md
Normal file
@@ -0,0 +1,64 @@
|
||||
# Jasmine Core 5.7.0 Release Notes
|
||||
|
||||
## New features
|
||||
|
||||
* Added [Clock#autoTick](https://jasmine.github.io/api/5.7/Clock.html#autoTick)
|
||||
to automatically tick the clock asynchronously
|
||||
* Merges #2042 from @atscott and @stephenfarrar
|
||||
* Fixes #1725
|
||||
* Fixes #1932
|
||||
|
||||
* Expose [spec path](https://jasmine.github.io/api/5.7/Spec.html#getPath) as an
|
||||
array of names in addition to the existing concatenated name
|
||||
|
||||
This is meant to support tools like IDE integrations that need to filter a run
|
||||
to an exact set of suites/specs.
|
||||
|
||||
|
||||
## Documentation improvements
|
||||
|
||||
* Documented that [SpecResult#filename](https://jasmine.github.io/api/5.7/global.html#SpecResult)
|
||||
and [SuiteResult#filename](https://jasmine.github.io/api/5.7/global.html#SuiteResult)
|
||||
are wrong when zone.js is present and in some cases where it/describe/etc are
|
||||
replaced
|
||||
* Updated docs for expected and actual properties of
|
||||
[expectation results](https://jasmine.github.io/api/5.7/global.html#ExpectationResult)
|
||||
|
||||
|
||||
## Internal improvements
|
||||
|
||||
* Rewrote the build system to not use Grunt
|
||||
|
||||
Although Grunt has served Jasmine well over the years, it was keeping us tied
|
||||
to an aging and increasingly questionable set of dev dependencies.
|
||||
|
||||
* Updated to eslint 9
|
||||
* Removed mostly-unmaintained dev dependency 'temp'
|
||||
* Updated most other dev dependencies to latest versions
|
||||
* Fixed sass deprecation warning
|
||||
* Updated to Sauce Connect 5
|
||||
* Made stop-sauce-connect script more robust
|
||||
|
||||
|
||||
## Supported environments
|
||||
|
||||
This version has been tested in the following environments.
|
||||
|
||||
| Environment | Supported versions |
|
||||
|-------------------|-------------------------|
|
||||
| Node | 18**, 20, 22 |
|
||||
| Safari | 15**, 16**, 17** |
|
||||
| Chrome | 135* |
|
||||
| Firefox | 102**, 115**, 128, 137* |
|
||||
| Edge | 135* |
|
||||
|
||||
\* 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)_
|
||||
3
scripts/buildDistribution.js
Normal file
3
scripts/buildDistribution.js
Normal file
@@ -0,0 +1,3 @@
|
||||
const buildDistribution = require('./lib/buildDistribution');
|
||||
|
||||
buildDistribution();
|
||||
92
scripts/buildStandaloneDist.js
Normal file
92
scripts/buildStandaloneDist.js
Normal file
@@ -0,0 +1,92 @@
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const glob = require('glob');
|
||||
const ejs = require('ejs');
|
||||
const archiver = require('archiver');
|
||||
const { rimrafSync } = require('rimraf');
|
||||
const buildDistribution = require('./lib/buildDistribution');
|
||||
|
||||
const tmpDir = 'dist/tmp'
|
||||
|
||||
if (!fs.existsSync(tmpDir)) {
|
||||
if (!fs.existsSync(path.dirname(tmpDir))) {
|
||||
fs.mkdirSync(path.dirname(tmpDir));
|
||||
}
|
||||
fs.mkdirSync(tmpDir);
|
||||
}
|
||||
|
||||
buildStandaloneDist().finally(function() {
|
||||
rimrafSync(tmpDir);
|
||||
});
|
||||
|
||||
async function buildStandaloneDist() {
|
||||
buildDistribution();
|
||||
const pkg = JSON.parse(fs.readFileSync('package.json'));
|
||||
compileSpecRunner(pkg.version);
|
||||
await zipStandaloneDist(pkg.version);
|
||||
}
|
||||
|
||||
function compileSpecRunner(jasmineVersion) {
|
||||
const template = fs.readFileSync('src/SpecRunner.html.ejs',
|
||||
{encoding: 'utf8'});
|
||||
const runnerHtml = ejs.render(template, { jasmineVersion });
|
||||
fs.writeFileSync('dist/tmp/SpecRunner.html', runnerHtml,
|
||||
{encoding: 'utf8'});
|
||||
}
|
||||
|
||||
async function zipStandaloneDist(jasmineVersion) {
|
||||
const fileGroups = [
|
||||
{
|
||||
src: [
|
||||
'LICENSE',
|
||||
'dist/tmp/SpecRunner.html',
|
||||
]
|
||||
},
|
||||
{
|
||||
src: [
|
||||
'images/jasmine_favicon.png',
|
||||
'lib/jasmine-core/jasmine.js',
|
||||
'lib/jasmine-core/jasmine-html.js',
|
||||
'lib/jasmine-core/jasmine.css',
|
||||
'lib/jasmine-core/boot0.js',
|
||||
'lib/jasmine-core/boot1.js',
|
||||
],
|
||||
destDir: 'lib/jasmine-' + jasmineVersion
|
||||
},
|
||||
{
|
||||
src: glob.sync('lib/jasmine-core/example/src/*.js'),
|
||||
destDir: 'src'
|
||||
},
|
||||
{
|
||||
src: glob.sync('lib/jasmine-core/example/spec/*.js'),
|
||||
destDir: 'spec'
|
||||
}
|
||||
];
|
||||
|
||||
const destPath = `./dist/jasmine-standalone-${jasmineVersion}.zip`;
|
||||
const output = fs.createWriteStream(destPath);
|
||||
const archive = archiver('zip');
|
||||
|
||||
const done = new Promise(function(resolve, reject) {
|
||||
output.on('close', resolve);
|
||||
archive.on('warning', reject);
|
||||
archive.on('error', reject);
|
||||
});
|
||||
|
||||
archive.pipe(output);
|
||||
|
||||
for (const group of fileGroups) {
|
||||
for (const srcPath of group.src) {
|
||||
let destPath = path.basename(srcPath);
|
||||
|
||||
if (group.destDir) {
|
||||
destPath = `${group.destDir}/${destPath}`;
|
||||
}
|
||||
|
||||
archive.file(srcPath, {name: destPath});
|
||||
}
|
||||
}
|
||||
|
||||
archive.finalize();
|
||||
await done;
|
||||
}
|
||||
129
scripts/lib/buildDistribution.js
Normal file
129
scripts/lib/buildDistribution.js
Normal file
@@ -0,0 +1,129 @@
|
||||
const fs = require('fs');
|
||||
const sass = require('sass');
|
||||
const glob = require('glob');
|
||||
const ejs = require('ejs');
|
||||
const cssUrlEmbed = require('css-url-embed');
|
||||
|
||||
function buildDistribution() {
|
||||
compileSass();
|
||||
embedCssAssets();
|
||||
concatFiles();
|
||||
}
|
||||
|
||||
function embedCssAssets() {
|
||||
const cssPath = 'lib/jasmine-core/jasmine.css';
|
||||
cssUrlEmbed.processFile(cssPath, cssPath, function(filePath) {
|
||||
if (filePath.endsWith('.png')) {
|
||||
return 'image/png';
|
||||
} else if (filePath.endsWith('.svg')) {
|
||||
return 'image/svg+xml';
|
||||
} else {
|
||||
throw new Error(`Don't know MIME type for file: ${filePath}`);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function compileSass() {
|
||||
const output = sass.compile('src/html/jasmine.scss');
|
||||
fs.writeFileSync('lib/jasmine-core/jasmine.css', output.css,
|
||||
{encoding: 'utf8'});
|
||||
}
|
||||
|
||||
function concatFiles() {
|
||||
const pkg = JSON.parse(fs.readFileSync('package.json'));
|
||||
const configs = [
|
||||
{
|
||||
src: [
|
||||
'src/html/requireHtml.js',
|
||||
'src/html/HtmlReporter.js',
|
||||
'src/html/HtmlSpecFilter.js',
|
||||
'src/html/ResultsNode.js',
|
||||
'src/html/QueryString.js',
|
||||
'src/html/**/*.js'
|
||||
],
|
||||
dest: 'lib/jasmine-core/jasmine-html.js',
|
||||
},
|
||||
{
|
||||
dest: 'lib/jasmine-core/jasmine.js',
|
||||
src: [
|
||||
'src/core/requireCore.js',
|
||||
'src/core/matchers/requireMatchers.js',
|
||||
'src/core/base.js',
|
||||
'src/core/util.js',
|
||||
'src/core/Spec.js',
|
||||
'src/core/Order.js',
|
||||
'src/core/Env.js',
|
||||
'src/core/JsApiReporter.js',
|
||||
'src/core/PrettyPrinter',
|
||||
'src/core/Suite',
|
||||
'src/core/**/*.js',
|
||||
{
|
||||
template: 'src/version.js',
|
||||
data: {version: pkg.version}
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
dest: 'lib/jasmine-core/boot0.js',
|
||||
src: ['src/boot/boot0.js'],
|
||||
},
|
||||
{
|
||||
dest: 'lib/jasmine-core/boot1.js',
|
||||
src: ['src/boot/boot1.js'],
|
||||
}
|
||||
];
|
||||
const licenseBanner = {
|
||||
template: 'src/licenseBanner.js.ejs',
|
||||
data: {currentYear: new Date(Date.now()).getFullYear()}
|
||||
};
|
||||
|
||||
for (const {src, dest} of configs) {
|
||||
src.unshift(licenseBanner);
|
||||
|
||||
function expand(srcListEntry) {
|
||||
if (typeof srcListEntry === 'object') {
|
||||
return srcListEntry;
|
||||
}
|
||||
|
||||
return glob.sync(srcListEntry)
|
||||
.sort(function (a, b) {
|
||||
// Match the sort order of previous build tools, so that the
|
||||
// output is the same.
|
||||
a = a.toLowerCase();
|
||||
b = b.toLowerCase();
|
||||
|
||||
if (a < b) {
|
||||
return -1;
|
||||
} else if (a === b) {
|
||||
return 0;
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const srcs = src.flatMap(expand);
|
||||
const seen = new Set();
|
||||
const chunks = [];
|
||||
|
||||
for (const s of srcs) {
|
||||
let content;
|
||||
|
||||
if (!seen.has(s)) {
|
||||
if (s.template) {
|
||||
const template = fs.readFileSync(s.template, {encoding: 'utf8'});
|
||||
content = ejs.render(template, s.data);
|
||||
} else {
|
||||
content = fs.readFileSync(s, {encoding: 'utf8'});
|
||||
}
|
||||
|
||||
chunks.push(content);
|
||||
seen.add(s);
|
||||
}
|
||||
}
|
||||
|
||||
fs.writeFileSync(dest, chunks.join('\n'), {encoding: 'utf8'});
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = buildDistribution;
|
||||
@@ -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,9 +24,22 @@ 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 safari 17
|
||||
run_browser safari 16
|
||||
run_browser safari 15
|
||||
run_browser MicrosoftEdge latest
|
||||
|
||||
28
scripts/runSpecsInNode.js
Normal file
28
scripts/runSpecsInNode.js
Normal file
@@ -0,0 +1,28 @@
|
||||
verifyNoGlobals(() => require('../lib/jasmine-core.js').noGlobals());
|
||||
|
||||
const Jasmine = require('jasmine');
|
||||
const jasmineCore = require('../lib/jasmine-core.js');
|
||||
const runner = new Jasmine({jasmineCore: jasmineCore});
|
||||
|
||||
runner.loadConfigFile('./spec/support/jasmine.json');
|
||||
runner.exitOnCompletion = false;
|
||||
runner.execute()
|
||||
.then(
|
||||
result => result.overallStatus === 'passed',
|
||||
err => {
|
||||
console.error(err);
|
||||
return false;
|
||||
}
|
||||
)
|
||||
.then(ok => process.exit(ok ? 0 : 1));
|
||||
|
||||
function verifyNoGlobals(fn) {
|
||||
const initialGlobals = Object.keys(global);
|
||||
fn();
|
||||
|
||||
const extras = Object.keys(global).filter(k => !initialGlobals.includes(k));
|
||||
|
||||
if (extras.length !== 0) {
|
||||
throw new Error('Globals were unexpectedly created: ' + extras.join(', '));
|
||||
}
|
||||
}
|
||||
28
scripts/runSpecsInParallel.js
Normal file
28
scripts/runSpecsInParallel.js
Normal file
@@ -0,0 +1,28 @@
|
||||
const ParallelRunner = require('jasmine/parallel');
|
||||
const jasmineCore = require('../lib/jasmine-core.js');
|
||||
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 runner = new ParallelRunner({jasmineCore, numWorkers});
|
||||
|
||||
runner.loadConfigFile('./spec/support/jasmine.json')
|
||||
.then(() => {
|
||||
runner.exitOnCompletion = false;
|
||||
return runner.execute();
|
||||
})
|
||||
.then(
|
||||
jasmineDoneInfo => jasmineDoneInfo.overallStatus === 'passed',
|
||||
err => {
|
||||
console.error(err);
|
||||
return false;
|
||||
}
|
||||
)
|
||||
.then(ok => process.exit(ok ? 0 : 1));
|
||||
@@ -2,32 +2,19 @@
|
||||
set -o errexit
|
||||
set -o pipefail
|
||||
|
||||
if [ $# -gt 1 -o "$1" = "--help" ]; then
|
||||
echo "Usage: $0 [pidfile]" 1>&2
|
||||
exit
|
||||
fi
|
||||
|
||||
if [ -z "$1" ]; then
|
||||
pidfile=`mktemp`
|
||||
else
|
||||
pidfile="$1"
|
||||
if [ -z "$SAUCE_TUNNEL_NAME" ]; then
|
||||
echo "SAUCE_TUNNEL_NAME must be set" 1>&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
outfile=`mktemp`
|
||||
echo "Starting Sauce Connect"
|
||||
if [ -z "$SAUCE_TUNNEL_IDENTIFIER" ]; then
|
||||
sc -u "$SAUCE_USERNAME" -k "$SAUCE_ACCESS_KEY" -X 4445 --pidfile "$pidfile" 2>&1 | tee "$outfile" &
|
||||
else
|
||||
sc -u "$SAUCE_USERNAME" -k "$SAUCE_ACCESS_KEY" -X 4445 --pidfile "$pidfile" -i "$SAUCE_TUNNEL_IDENTIFIER" 2>&1 | tee "$outfile" &
|
||||
fi
|
||||
sc legacy --proxy-localhost --tunnel-domains localhost --region us-west \
|
||||
-u "$SAUCE_USERNAME" -k "$SAUCE_ACCESS_KEY" \
|
||||
-X 4445 -i "$SAUCE_TUNNEL_NAME" 2>&1 | tee "$outfile" &
|
||||
|
||||
while ! fgrep "Sauce Connect is up, you may start your tests." "$outfile" > /dev/null; do
|
||||
while ! fgrep "Sauce Connect is up, you may start your tests" "$outfile" > /dev/null; do
|
||||
sleep 1
|
||||
|
||||
if ! ps -p $(cat "$pidfile") > /dev/null; then
|
||||
echo "Sauce Connect exited"
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
if ! nc -z localhost 4445; then
|
||||
|
||||
@@ -2,32 +2,38 @@
|
||||
set -o errexit
|
||||
set -o pipefail
|
||||
|
||||
if [ -z "$1" ]; then
|
||||
echo "Usage: $0 sauce-connect-pid" 1>&2
|
||||
exit
|
||||
fi
|
||||
|
||||
pid="$1"
|
||||
echo "PID: $pid"
|
||||
|
||||
echo "Stopping Sauce Connect"
|
||||
# Sauce Connect docs say that we can just kill -9 it if we don't care about
|
||||
# failing any ongoing sessions. In practice, that sometimes works but usually
|
||||
# leaks a tunnel so badly that you can't even stop it from the web UI.
|
||||
# Instead of doing that, we give Sauce Connect some time to shut down
|
||||
# gracefully and then give up.
|
||||
kill -INT $pid
|
||||
|
||||
# Sauce Connect 4 docs said that we can just kill -9 it if we don't care about
|
||||
# failing any ongoing sessions. In practice, that sometimes worked but usually
|
||||
# leaked a tunnel so badly that you couldn't even stop it from the web UI.
|
||||
#
|
||||
# Sauce Connect 5 appears to be *much* more prone to hung jobs. Hung jobs have
|
||||
# a shutdown deadline of *three hours*, however, they appear to usually exit
|
||||
# within a minute or so. Unfortunately the only thing the Sauce Connect 5 docs
|
||||
# say about shutdown is that "you can stop your tunnel from the terminal where
|
||||
# Sauce Connect is running by entering Ctrl+C". Nothing is said about what to
|
||||
# do if Sauce Connect doesn't exit on it own or about non-interactive usage.
|
||||
#
|
||||
# So we do our best to be well-behaved without assuming that Sauce Connect
|
||||
# always is: send it the same signal that it would get if an interactive user
|
||||
# hit ctrl-c, wait a while for it to exit, then give up so that the CI task
|
||||
# doesn't keep running indefinitely.
|
||||
|
||||
if ! pkill -INT '^sc$'; then
|
||||
echo "sc does not appear to be running" 1>&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Wait up to 2 minutes, then give up if it's still running
|
||||
n=0
|
||||
while [ $n -lt 120 ] && ps -p $pid > /dev/null; do
|
||||
while [ $n -lt 120 ] && pgrep '^sc$' > /dev/null; do
|
||||
sleep 1
|
||||
kill -INT $pid 2> /dev/null || true
|
||||
pkill -INT '^sc$' || true
|
||||
n=$(($n + 1))
|
||||
done
|
||||
|
||||
if ps -p $pid > /dev/null; then
|
||||
if pgrep '^sc$' > /dev/null; then
|
||||
echo "Could not shut down Sauce Connect"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
exit $exitcode
|
||||
|
||||
@@ -20,6 +20,47 @@ describe('ClearStack', function() {
|
||||
MessageChannel: fakeMessageChannel
|
||||
};
|
||||
});
|
||||
|
||||
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;
|
||||
},
|
||||
queueMicrotask
|
||||
};
|
||||
|
||||
const clearStack = jasmineUnderTest.getClearStack(global);
|
||||
|
||||
for (let i = 0; i < 9; i++) {
|
||||
clearStack(function() {});
|
||||
}
|
||||
|
||||
expect(fakeChannel.port2.postMessage).not.toHaveBeenCalled();
|
||||
|
||||
clearStack(function() {});
|
||||
|
||||
expect(fakeChannel.port2.postMessage).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
|
||||
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() {
|
||||
|
||||
@@ -687,6 +687,122 @@ describe('Clock (acceptance)', function() {
|
||||
expect(recurring1.calls.count()).toBe(4);
|
||||
});
|
||||
|
||||
describe('auto tick mode', () => {
|
||||
let delayedFunctionScheduler;
|
||||
let mockDate;
|
||||
let clock;
|
||||
|
||||
beforeEach(() => {
|
||||
delayedFunctionScheduler = new jasmineUnderTest.DelayedFunctionScheduler();
|
||||
mockDate = {
|
||||
install: function() {},
|
||||
tick: function() {},
|
||||
uninstall: function() {}
|
||||
};
|
||||
clock = new jasmineUnderTest.Clock(
|
||||
// We use the real window for global or firefox is displeased when we try to call a real setTimeout on an object "that doesn't implement window".
|
||||
typeof window !== 'undefined' ? window : { setTimeout: setTimeout },
|
||||
function() {
|
||||
return delayedFunctionScheduler;
|
||||
},
|
||||
mockDate
|
||||
);
|
||||
clock.install();
|
||||
clock.autoTick();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
clock.uninstall();
|
||||
});
|
||||
|
||||
it('can run setTimeouts/setIntervals asynchronously', function() {
|
||||
const recurring = jasmine.createSpy('recurring'),
|
||||
fn1 = jasmine.createSpy('fn1'),
|
||||
fn2 = jasmine.createSpy('fn2'),
|
||||
fn3 = jasmine.createSpy('fn3');
|
||||
|
||||
const intervalId = clock.setInterval(recurring, 50);
|
||||
// In a microtask, add some timeouts.
|
||||
Promise.resolve()
|
||||
.then(function() {
|
||||
return new Promise(function(resolve) {
|
||||
clock.setTimeout(resolve, 25);
|
||||
});
|
||||
})
|
||||
.then(function() {
|
||||
fn1();
|
||||
return new Promise(function(resolve) {
|
||||
clock.setTimeout(resolve, 200);
|
||||
});
|
||||
})
|
||||
.then(function() {
|
||||
fn2();
|
||||
return new Promise(function(resolve) {
|
||||
clock.setTimeout(resolve, 100);
|
||||
});
|
||||
})
|
||||
.then(function() {
|
||||
fn3();
|
||||
});
|
||||
|
||||
expect(recurring).not.toHaveBeenCalled();
|
||||
expect(fn1).not.toHaveBeenCalled();
|
||||
expect(fn2).not.toHaveBeenCalled();
|
||||
expect(fn3).not.toHaveBeenCalled();
|
||||
|
||||
return new Promise(resolve => clock.setTimeout(resolve, 50))
|
||||
.then(function() {
|
||||
expect(recurring).toHaveBeenCalledTimes(1);
|
||||
expect(fn1).toHaveBeenCalled();
|
||||
expect(fn2).not.toHaveBeenCalled();
|
||||
expect(fn3).not.toHaveBeenCalled();
|
||||
|
||||
return new Promise(resolve => clock.setTimeout(resolve, 175));
|
||||
})
|
||||
.then(function() {
|
||||
expect(recurring).toHaveBeenCalledTimes(4);
|
||||
expect(fn1).toHaveBeenCalled();
|
||||
expect(fn2).toHaveBeenCalled();
|
||||
expect(fn3).not.toHaveBeenCalled();
|
||||
|
||||
clock.clearInterval(intervalId);
|
||||
return new Promise(resolve => clock.setTimeout(resolve, 100));
|
||||
})
|
||||
.then(function() {
|
||||
expect(recurring).toHaveBeenCalledTimes(4);
|
||||
expect(fn1).toHaveBeenCalled();
|
||||
expect(fn2).toHaveBeenCalled();
|
||||
expect(fn3).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
it('speeds up the execution of the timers in all browsers', async () => {
|
||||
const startTimeMs = performance.now() / 1000;
|
||||
await new Promise(resolve => clock.setTimeout(resolve, 5000));
|
||||
await new Promise(resolve => clock.setTimeout(resolve, 5000));
|
||||
await new Promise(resolve => clock.setTimeout(resolve, 5000));
|
||||
await new Promise(resolve => clock.setTimeout(resolve, 5000));
|
||||
const endTimeMs = performance.now() / 1000;
|
||||
// Ensure we didn't take 20s to complete the awaits above and, in fact, can do it in a fraction of a second
|
||||
expect(endTimeMs - startTimeMs).toBeLessThan(100);
|
||||
});
|
||||
|
||||
it('is easy to test async functions with interleaved timers and microtasks', async () => {
|
||||
async function blackBoxWithLotsOfAsyncStuff() {
|
||||
await new Promise(r => clock.setTimeout(r, 10));
|
||||
await Promise.resolve();
|
||||
await Promise.resolve();
|
||||
await new Promise(r => clock.setTimeout(r, 20));
|
||||
await Promise.resolve();
|
||||
await Promise.resolve();
|
||||
await Promise.resolve();
|
||||
return 'done';
|
||||
}
|
||||
const result = await blackBoxWithLotsOfAsyncStuff();
|
||||
expect(result).toBe('done');
|
||||
});
|
||||
});
|
||||
|
||||
it('can clear a previously set timeout', function() {
|
||||
const clearedFn = jasmine.createSpy('clearedFn'),
|
||||
delayedFunctionScheduler = new jasmineUnderTest.DelayedFunctionScheduler(),
|
||||
|
||||
@@ -264,6 +264,42 @@ describe('DelayedFunctionScheduler', function() {
|
||||
expect(fn).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('runs the next scheduled funtion', function() {
|
||||
const scheduler = new jasmineUnderTest.DelayedFunctionScheduler();
|
||||
const fn = jasmine.createSpy('fn');
|
||||
const tickSpy = jasmine.createSpy('tick');
|
||||
|
||||
scheduler.scheduleFunction(fn, 10, [], false, 'foo');
|
||||
|
||||
expect(fn).not.toHaveBeenCalled();
|
||||
|
||||
scheduler.runNextQueuedFunction(tickSpy);
|
||||
|
||||
expect(fn).toHaveBeenCalled();
|
||||
expect(tickSpy).toHaveBeenCalledWith(10);
|
||||
});
|
||||
|
||||
it('runs the only a single scheduled funtion in a time slot', function() {
|
||||
const scheduler = new jasmineUnderTest.DelayedFunctionScheduler();
|
||||
const fn1 = jasmine.createSpy('fn');
|
||||
const fn2 = jasmine.createSpy('fn2');
|
||||
const tickSpy = jasmine.createSpy('tick');
|
||||
|
||||
scheduler.scheduleFunction(fn1, 10, [], false, 'foo1');
|
||||
scheduler.scheduleFunction(fn2, 10, [], false, 'foo2');
|
||||
|
||||
scheduler.runNextQueuedFunction(tickSpy);
|
||||
|
||||
expect(fn1).toHaveBeenCalled();
|
||||
expect(fn2).not.toHaveBeenCalled();
|
||||
expect(tickSpy).toHaveBeenCalledWith(10);
|
||||
|
||||
tickSpy.calls.reset();
|
||||
scheduler.runNextQueuedFunction(tickSpy);
|
||||
expect(fn2).toHaveBeenCalled();
|
||||
expect(tickSpy).toHaveBeenCalledWith(0);
|
||||
});
|
||||
|
||||
it('updates the mockDate per scheduled time', function() {
|
||||
const scheduler = new jasmineUnderTest.DelayedFunctionScheduler(),
|
||||
tickDate = jasmine.createSpy('tickDate');
|
||||
|
||||
@@ -205,7 +205,6 @@ describe('Env', function() {
|
||||
|
||||
it('throws an error when given arguments', function() {
|
||||
expect(function() {
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
env.describe('done method', function(done) {});
|
||||
}).toThrowError('describe does not expect any arguments');
|
||||
});
|
||||
|
||||
@@ -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/)) {
|
||||
|
||||
@@ -328,6 +328,62 @@ describe('GlobalErrors', function() {
|
||||
});
|
||||
});
|
||||
|
||||
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')
|
||||
);
|
||||
});
|
||||
|
||||
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 = browserGlobal();
|
||||
|
||||
@@ -239,7 +239,6 @@ describe('QueueRunner', function() {
|
||||
|
||||
it("sets a timeout if requested for asynchronous functions so they don't go on forever", function() {
|
||||
const timeout = 3,
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
beforeFn = { fn: function(done) {}, type: 'before', timeout: timeout },
|
||||
queueableFn = { fn: jasmine.createSpy('fn'), type: 'queueable' },
|
||||
onComplete = jasmine.createSpy('onComplete'),
|
||||
@@ -287,7 +286,6 @@ describe('QueueRunner', function() {
|
||||
});
|
||||
|
||||
it('by default does not set a timeout for asynchronous functions', function() {
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
const beforeFn = { fn: function(done) {} },
|
||||
queueableFn = { fn: jasmine.createSpy('fn') },
|
||||
onComplete = jasmine.createSpy('onComplete'),
|
||||
@@ -310,7 +308,6 @@ describe('QueueRunner', function() {
|
||||
|
||||
it('clears the timeout when an async function throws an exception, to prevent additional exception reporting', function() {
|
||||
const queueableFn = {
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
fn: function(done) {
|
||||
throw new Error('error!');
|
||||
}
|
||||
@@ -409,7 +406,6 @@ describe('QueueRunner', function() {
|
||||
|
||||
it('continues running functions when an exception is thrown in async code without timing out', function() {
|
||||
const queueableFn = {
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
fn: function(done) {
|
||||
throwAsync();
|
||||
},
|
||||
@@ -459,6 +455,31 @@ describe('QueueRunner', function() {
|
||||
expect(nextQueueableFn.fn).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('handles a global error event with a message but no error', function() {
|
||||
const queueableFn = {
|
||||
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) {
|
||||
@@ -492,6 +513,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() {
|
||||
@@ -581,7 +636,6 @@ describe('QueueRunner', function() {
|
||||
|
||||
it('issues an error if the function also takes a parameter', function() {
|
||||
const queueableFn = {
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
fn: function(done) {
|
||||
return new StubPromise();
|
||||
}
|
||||
@@ -606,7 +660,6 @@ describe('QueueRunner', function() {
|
||||
});
|
||||
|
||||
it('issues a more specific error if the function is `async`', function() {
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
async function fn(done) {}
|
||||
const onException = jasmine.createSpy('onException'),
|
||||
queueRunner = new jasmineUnderTest.QueueRunner({
|
||||
@@ -660,7 +713,6 @@ describe('QueueRunner', function() {
|
||||
|
||||
it('continues running the functions even after an exception is thrown in an async spec', function() {
|
||||
const queueableFn = {
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
fn: function(done) {
|
||||
throw new Error('error');
|
||||
}
|
||||
|
||||
@@ -197,8 +197,8 @@ describe('Spec', function() {
|
||||
description: 'with a spec',
|
||||
parentSuiteId: 'suite1',
|
||||
filename: 'someSpecFile.js',
|
||||
getSpecName: function() {
|
||||
return 'a suite with a spec';
|
||||
getPath: function() {
|
||||
return ['a suite', 'with a spec'];
|
||||
},
|
||||
queueableFn: { fn: null }
|
||||
});
|
||||
@@ -485,17 +485,33 @@ describe('Spec', function() {
|
||||
});
|
||||
|
||||
it('can return its full name', function() {
|
||||
const specNameSpy = jasmine
|
||||
.createSpy('specNameSpy')
|
||||
.and.returnValue('expected val');
|
||||
const getPath = jasmine
|
||||
.createSpy('getPath')
|
||||
.and.returnValue(['expected', 'val']);
|
||||
|
||||
const spec = new jasmineUnderTest.Spec({
|
||||
getSpecName: specNameSpy,
|
||||
getPath,
|
||||
queueableFn: { fn: null }
|
||||
});
|
||||
|
||||
expect(spec.getFullName()).toBe('expected val');
|
||||
expect(specNameSpy.calls.mostRecent().args[0].id).toEqual(spec.id);
|
||||
expect(getPath.calls.mostRecent().args[0]).toBe(spec);
|
||||
});
|
||||
|
||||
it('can return its full path', function() {
|
||||
const getPath = jasmine
|
||||
.createSpy('getPath')
|
||||
.and.returnValue(['expected val']);
|
||||
|
||||
const spec = new jasmineUnderTest.Spec({
|
||||
getPath,
|
||||
queueableFn: { fn: null }
|
||||
});
|
||||
|
||||
expect(spec.getPath()).toEqual(['expected val']);
|
||||
expect(getPath.calls.mostRecent().args[0]).toBe(spec);
|
||||
|
||||
expect(spec.metadata.getPath()).toEqual(['expected val']);
|
||||
});
|
||||
|
||||
describe('when a spec is marked pending during execution', function() {
|
||||
@@ -586,8 +602,8 @@ describe('Spec', function() {
|
||||
spec = new jasmineUnderTest.Spec({
|
||||
onLateError: onLateError,
|
||||
queueableFn: { fn: function() {} },
|
||||
getSpecName: function() {
|
||||
return 'a spec';
|
||||
getPath: function() {
|
||||
return ['a spec'];
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -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({
|
||||
|
||||
@@ -97,17 +97,11 @@ describe('Spies', function() {
|
||||
it('preserves arity of original function', function() {
|
||||
const functions = [
|
||||
function nullary() {},
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
function unary(arg) {},
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
function binary(arg1, arg2) {},
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
function ternary(arg1, arg2, arg3) {},
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
function quaternary(arg1, arg2, arg3, arg4) {},
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
function quinary(arg1, arg2, arg3, arg4, arg5) {},
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
function senary(arg1, arg2, arg3, arg4, arg5, arg6) {}
|
||||
];
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -163,6 +163,20 @@ describe('SuiteBuilder', function() {
|
||||
expect(spec2.id).toMatch(/^spec[0-9]+$/);
|
||||
expect(spec1.id).not.toEqual(spec2.id);
|
||||
});
|
||||
|
||||
it('gives each spec a full path', function() {
|
||||
const env = { configuration: () => ({}) };
|
||||
const suiteBuilder = new jasmineUnderTest.SuiteBuilder({ env });
|
||||
let spec;
|
||||
|
||||
suiteBuilder.describe('a suite', function() {
|
||||
suiteBuilder.describe('a nested suite', function() {
|
||||
spec = suiteBuilder[fnName]('a spec', function() {});
|
||||
});
|
||||
});
|
||||
|
||||
expect(spec.getPath()).toEqual(['a suite', 'a nested suite', 'a spec']);
|
||||
});
|
||||
}
|
||||
|
||||
function sameInstanceAs(expected) {
|
||||
@@ -176,6 +190,117 @@ 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();
|
||||
|
||||
@@ -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();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -3,7 +3,7 @@ describe('Env integration', function() {
|
||||
const isBrowser = typeof window !== 'undefined';
|
||||
|
||||
beforeEach(function() {
|
||||
jasmine.getEnv().registerIntegrationMatchers();
|
||||
specHelpers.registerIntegrationMatchers();
|
||||
env = new jasmineUnderTest.Env();
|
||||
});
|
||||
|
||||
@@ -1071,7 +1071,6 @@ describe('Env integration', function() {
|
||||
env.describe('my suite', function() {
|
||||
env.it('my spec', function() {});
|
||||
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
env.afterAll(function(afterAllDone) {
|
||||
throw error;
|
||||
});
|
||||
@@ -1315,8 +1314,9 @@ describe('Env integration', function() {
|
||||
'works with constructors when using callThrough spy strategy',
|
||||
function() {
|
||||
function MyClass(foo) {
|
||||
if (!(this instanceof MyClass))
|
||||
if (!(this instanceof MyClass)) {
|
||||
throw new Error('You must use the new keyword.');
|
||||
}
|
||||
this.foo = foo;
|
||||
}
|
||||
const subject = { MyClass: MyClass };
|
||||
@@ -1568,7 +1568,6 @@ describe('Env integration', function() {
|
||||
env.addReporter(reporter);
|
||||
jasmineUnderTest.DEFAULT_TIMEOUT_INTERVAL = 8414;
|
||||
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
env.it("async spec that doesn't call done", function(underTestCallback) {
|
||||
env.expect(true).toBeTruthy();
|
||||
jasmine.clock().tick(8416);
|
||||
@@ -1641,13 +1640,13 @@ describe('Env integration', function() {
|
||||
realSetTimeout(function() {
|
||||
try {
|
||||
jasmine.clock().tick(10);
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
} catch (e) {
|
||||
// don't worry if the clock is already uninstalled
|
||||
}
|
||||
}, 100);
|
||||
});
|
||||
env.describe('beforeAll', function() {
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
env.beforeAll(function(innerDone) {
|
||||
realSetTimeout(function() {
|
||||
jasmine.clock().tick(5001);
|
||||
@@ -1663,7 +1662,6 @@ describe('Env integration', function() {
|
||||
});
|
||||
|
||||
env.describe('afterAll', function() {
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
env.afterAll(function(innerDone) {
|
||||
realSetTimeout(function() {
|
||||
jasmine.clock().tick(2001);
|
||||
@@ -1679,7 +1677,6 @@ describe('Env integration', function() {
|
||||
});
|
||||
|
||||
env.describe('beforeEach', function() {
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
env.beforeEach(function(innerDone) {
|
||||
realSetTimeout(function() {
|
||||
jasmine.clock().tick(1001);
|
||||
@@ -1695,7 +1692,6 @@ describe('Env integration', function() {
|
||||
});
|
||||
|
||||
env.describe('afterEach', function() {
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
env.afterEach(function(innerDone) {
|
||||
realSetTimeout(function() {
|
||||
jasmine.clock().tick(4001);
|
||||
@@ -1712,7 +1708,6 @@ describe('Env integration', function() {
|
||||
|
||||
env.it(
|
||||
'it times out',
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
function(innerDone) {
|
||||
realSetTimeout(function() {
|
||||
jasmine.clock().tick(6001);
|
||||
@@ -2698,7 +2693,6 @@ describe('Env integration', function() {
|
||||
env.addReporter(reporter);
|
||||
|
||||
env.describe('async suite', function() {
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
env.afterAll(function(innerDone) {
|
||||
setTimeout(function() {
|
||||
throw new Error('suite');
|
||||
@@ -2711,7 +2705,6 @@ describe('Env integration', function() {
|
||||
env.describe('suite', function() {
|
||||
env.it(
|
||||
'async spec',
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
function(innerDone) {
|
||||
setTimeout(function() {
|
||||
throw new Error('spec');
|
||||
@@ -4361,6 +4354,7 @@ describe('Env integration', function() {
|
||||
env.it('a spec', function() {
|
||||
try {
|
||||
env.throwUnless(1).toEqual(1);
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
} catch (e) {
|
||||
threw = true;
|
||||
}
|
||||
@@ -4374,6 +4368,7 @@ describe('Env integration', function() {
|
||||
env.it('a spec', function() {
|
||||
try {
|
||||
env.throwUnless(1).toEqual(2);
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
} catch (e) {}
|
||||
});
|
||||
|
||||
@@ -4417,6 +4412,7 @@ describe('Env integration', function() {
|
||||
env.it('a spec', async function() {
|
||||
try {
|
||||
await env.throwUnlessAsync(Promise.resolve()).toBeResolved();
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
} catch (e) {
|
||||
threw = true;
|
||||
}
|
||||
@@ -4430,6 +4426,7 @@ describe('Env integration', function() {
|
||||
env.it('a spec', async function() {
|
||||
try {
|
||||
await env.throwUnlessAsync(Promise.resolve()).toBeRejected();
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
} catch (e) {}
|
||||
});
|
||||
|
||||
@@ -4443,6 +4440,15 @@ describe('Env integration', function() {
|
||||
});
|
||||
});
|
||||
|
||||
it('forbids duplicates when forbidDuplicateNames is true', function() {
|
||||
env.configure({ forbidDuplicateNames: true });
|
||||
env.it('a spec');
|
||||
|
||||
expect(function() {
|
||||
env.it('a spec');
|
||||
}).toThrowError('Duplicate spec name "a spec" found in top suite');
|
||||
});
|
||||
|
||||
function browserEventMethods() {
|
||||
return {
|
||||
listeners_: { error: [], unhandledrejection: [] },
|
||||
|
||||
@@ -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$/);
|
||||
|
||||
@@ -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 });
|
||||
});
|
||||
@@ -866,7 +866,6 @@ describe('spec running', function() {
|
||||
const actions = [];
|
||||
|
||||
env.describe('Something', function() {
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
env.beforeEach(function(innerDone) {
|
||||
actions.push('beforeEach');
|
||||
}, 1);
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
);
|
||||
|
||||
@@ -766,7 +768,9 @@ describe('matchersUtil', function() {
|
||||
const a1 = new TypedArrayCtor(2);
|
||||
const a2 = new TypedArrayCtor(1);
|
||||
a1[0] = a1[1] = a2[0] = 0;
|
||||
expect(matchersUtil.equals(a1, a2)).toBe(false);
|
||||
const diffBuilder = new jasmineUnderTest.DiffBuilder();
|
||||
expect(matchersUtil.equals(a1, a2, diffBuilder)).toBe(false);
|
||||
jasmine.debugLog('Diff: ' + diffBuilder.getMessage());
|
||||
});
|
||||
|
||||
it(
|
||||
@@ -777,7 +781,9 @@ describe('matchersUtil', function() {
|
||||
const a2 = new TypedArrayCtor(1);
|
||||
a1[0] = 0;
|
||||
a2[0] = 1;
|
||||
expect(matchersUtil.equals(a1, a2)).toBe(false);
|
||||
const diffBuilder = new jasmineUnderTest.DiffBuilder();
|
||||
expect(matchersUtil.equals(a1, a2, diffBuilder)).toBe(false);
|
||||
jasmine.debugLog('Diff: ' + diffBuilder.getMessage());
|
||||
}
|
||||
);
|
||||
|
||||
@@ -824,9 +830,7 @@ describe('matchersUtil', function() {
|
||||
const matchersUtil = new jasmineUnderTest.MatchersUtil();
|
||||
const a1 = new TypedArrayCtor(2);
|
||||
const a2 = new TypedArrayCtor(2);
|
||||
// eslint-disable-next-line compat/compat
|
||||
a1[0] = a2[0] = BigInt(0);
|
||||
// eslint-disable-next-line compat/compat
|
||||
a1[1] = a2[1] = BigInt(1);
|
||||
expect(matchersUtil.equals(a1, a2)).toBe(true);
|
||||
}
|
||||
@@ -837,7 +841,6 @@ describe('matchersUtil', function() {
|
||||
const matchersUtil = new jasmineUnderTest.MatchersUtil();
|
||||
const a1 = new TypedArrayCtor(2);
|
||||
const a2 = new TypedArrayCtor(1);
|
||||
// eslint-disable-next-line compat/compat
|
||||
a1[0] = a1[1] = a2[0] = BigInt(0);
|
||||
expect(matchersUtil.equals(a1, a2)).toBe(false);
|
||||
});
|
||||
@@ -849,9 +852,7 @@ describe('matchersUtil', function() {
|
||||
const matchersUtil = new jasmineUnderTest.MatchersUtil();
|
||||
const a1 = new TypedArrayCtor(2);
|
||||
const a2 = new TypedArrayCtor(2);
|
||||
// eslint-disable-next-line compat/compat
|
||||
a1[0] = a1[1] = a2[0] = BigInt(0);
|
||||
// eslint-disable-next-line compat/compat
|
||||
a2[1] = BigInt(1);
|
||||
expect(matchersUtil.equals(a1, a2)).toBe(false);
|
||||
}
|
||||
|
||||
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);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,4 +1,3 @@
|
||||
/* eslint-disable compat/compat */
|
||||
describe('toEqual', function() {
|
||||
'use strict';
|
||||
|
||||
@@ -96,6 +95,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);
|
||||
|
||||
@@ -70,7 +70,7 @@ describe('toHaveSpyInteractions', function() {
|
||||
);
|
||||
});
|
||||
|
||||
it(`throws an error if a non-object is passed`, function() {
|
||||
it('throws an error if a non-object is passed', function() {
|
||||
let matcher = jasmineUnderTest.matchers.toHaveSpyInteractions();
|
||||
|
||||
expect(function() {
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
describe('npm package', function() {
|
||||
const path = require('path'),
|
||||
temp = require('temp').track(),
|
||||
fs = require('fs');
|
||||
const fs = require('node:fs');
|
||||
const path = require('node:path');
|
||||
const os = require('node:os');
|
||||
const { rimrafSync } = require('rimraf');
|
||||
|
||||
describe('npm package', function() {
|
||||
beforeAll(function() {
|
||||
const shell = require('shelljs'),
|
||||
pack = shell.exec('npm pack', { silent: true });
|
||||
|
||||
this.tarball = pack.stdout.split('\n')[0];
|
||||
this.tmpDir = temp.mkdirSync(); // automatically deleted on exit
|
||||
const prefix = path.join(os.tmpdir(), 'jasmine-npm-package');
|
||||
this.tmpDir = fs.mkdtempSync(prefix);
|
||||
|
||||
const untar = shell.exec(
|
||||
'tar -xzf ' + this.tarball + ' -C ' + this.tmpDir,
|
||||
@@ -41,6 +43,7 @@ describe('npm package', function() {
|
||||
|
||||
afterAll(function() {
|
||||
fs.unlinkSync(this.tarball);
|
||||
rimrafSync(this.tmpDir);
|
||||
});
|
||||
|
||||
it('has a root path', function() {
|
||||
@@ -113,7 +116,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',
|
||||
|
||||
@@ -8,6 +8,7 @@ config.clearReporters = true;
|
||||
config.jasmineCore = jasmineCore;
|
||||
|
||||
jasmineBrowser.runSpecs(config).catch(function(error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error(error);
|
||||
process.exit(1);
|
||||
});
|
||||
|
||||
@@ -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',
|
||||
@@ -37,7 +38,7 @@ module.exports = {
|
||||
name: `jasmine-core ${new Date().toISOString()}`,
|
||||
build: `Core ${process.env.CIRCLE_BUILD_NUM || 'Ran locally'}`,
|
||||
tags: ['Jasmine-Core'],
|
||||
tunnelIdentifier: process.env.SAUCE_TUNNEL_IDENTIFIER,
|
||||
tunnelName: process.env.SAUCE_TUNNEL_NAME,
|
||||
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",
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -2,7 +2,8 @@ getJasmineRequireObj().clearStack = function(j$) {
|
||||
const maxInlineCallCount = 10;
|
||||
|
||||
function browserQueueMicrotaskImpl(global) {
|
||||
const { setTimeout, queueMicrotask } = global;
|
||||
const unclampedSetTimeout = getUnclampedSetTimeout(global);
|
||||
const { queueMicrotask } = global;
|
||||
let currentCallCount = 0;
|
||||
return function clearStack(fn) {
|
||||
currentCallCount++;
|
||||
@@ -11,7 +12,7 @@ getJasmineRequireObj().clearStack = function(j$) {
|
||||
queueMicrotask(fn);
|
||||
} else {
|
||||
currentCallCount = 0;
|
||||
setTimeout(fn);
|
||||
unclampedSetTimeout(fn);
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -25,6 +26,37 @@ getJasmineRequireObj().clearStack = function(j$) {
|
||||
}
|
||||
|
||||
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 = {};
|
||||
@@ -48,17 +80,9 @@ 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);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -68,20 +92,25 @@ getJasmineRequireObj().clearStack = function(j$) {
|
||||
global.process.versions &&
|
||||
typeof global.process.versions.node === 'string';
|
||||
|
||||
const SAFARI =
|
||||
// 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/i.test(global.navigator.userAgent);
|
||||
/(^((?!chrome|android).)*safari)|(Win64; x64\) AppleWebKit\/[0-9.]+ \(KHTML, like Gecko\)$)/i.test(
|
||||
global.navigator.userAgent
|
||||
);
|
||||
|
||||
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 ||
|
||||
SAFARI_OR_WIN_WEBKIT ||
|
||||
j$.util.isUndefined(global.MessageChannel) /* tests */
|
||||
) {
|
||||
// queueMicrotask is dramatically faster than MessageChannel in Safari,
|
||||
// at least through version 16.
|
||||
// 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);
|
||||
|
||||
@@ -29,6 +29,15 @@ getJasmineRequireObj().Clock = function() {
|
||||
let installed = false;
|
||||
let delayedFunctionScheduler;
|
||||
let timer;
|
||||
// Tracks how the clock ticking behaves.
|
||||
// By default, the clock only ticks when the user manually calls a tick method.
|
||||
// There is also an 'auto' mode which will advance the clock automatically to
|
||||
// to the next task. Once enabled, there is currently no mechanism for users
|
||||
// to disable the auto ticking.
|
||||
let tickMode = {
|
||||
mode: 'manual',
|
||||
counter: 0
|
||||
};
|
||||
|
||||
this.FakeTimeout = FakeTimeout;
|
||||
|
||||
@@ -138,8 +147,98 @@ getJasmineRequireObj().Clock = function() {
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Updates the clock to automatically advance time.
|
||||
*
|
||||
* With this mode, the clock advances to the first scheduled timer and fires it, in a loop.
|
||||
* Between each timer, it will also break the event loop, allowing any scheduled promise
|
||||
callbacks to execute _before_ running the next one.
|
||||
*
|
||||
* This mode allows tests to be authored in a way that does not need to be aware of the
|
||||
* mock clock. Consequently, tests which have been authored without a mock clock installed
|
||||
* can one with auto tick enabled without any other updates to the test logic.
|
||||
*
|
||||
* In many cases, this can greatly improve test execution speed because asynchronous tasks
|
||||
* will execute as quickly as possible rather than waiting real time to complete.
|
||||
*
|
||||
* Furthermore, tests can be authored in a consistent manner. They can always be written in an asynchronous style
|
||||
* rather than having `tick` sprinkled throughout the tests with mock time in order to manually
|
||||
* advance the clock.
|
||||
*
|
||||
* When auto tick is enabled, `tick` can still be used to synchronously advance the clock if necessary.
|
||||
* @name Clock#autoTick
|
||||
* @function
|
||||
* @since 5.7.0
|
||||
*/
|
||||
this.autoTick = function() {
|
||||
if (tickMode.mode === 'auto') {
|
||||
return;
|
||||
}
|
||||
|
||||
tickMode = { mode: 'auto', counter: tickMode.counter + 1 };
|
||||
advanceUntilModeChanges();
|
||||
};
|
||||
|
||||
return this;
|
||||
|
||||
// Advances the Clock's time until the mode changes.
|
||||
//
|
||||
// The time is advanced asynchronously, giving microtasks and events a chance
|
||||
// to run before each timer runs.
|
||||
//
|
||||
// @function
|
||||
// @return {!Promise<undefined>}
|
||||
async function advanceUntilModeChanges() {
|
||||
if (!installed) {
|
||||
throw new Error(
|
||||
'Mock clock is not installed, use jasmine.clock().install()'
|
||||
);
|
||||
}
|
||||
const { counter } = tickMode;
|
||||
|
||||
while (true) {
|
||||
await newMacrotask();
|
||||
|
||||
if (
|
||||
tickMode.counter !== counter ||
|
||||
!installed ||
|
||||
delayedFunctionScheduler === null
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!delayedFunctionScheduler.isEmpty()) {
|
||||
delayedFunctionScheduler.runNextQueuedFunction(function(millis) {
|
||||
mockDate.tick(millis);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Waits until a new macro task.
|
||||
//
|
||||
// Used with autoTick(), which is meant to act when the test is waiting, we need
|
||||
// to insert ourselves in the macro task queue.
|
||||
//
|
||||
// @return {!Promise<undefined>}
|
||||
async function newMacrotask() {
|
||||
// MessageChannel ensures that setTimeout is not throttled to 4ms.
|
||||
// https://developer.mozilla.org/en-US/docs/Web/API/setTimeout#reasons_for_delays_longer_than_specified
|
||||
// https://stackblitz.com/edit/stackblitz-starters-qtlpcc
|
||||
// Note: This trick does not work in Safari, which will still throttle the setTimeout
|
||||
const channel = new MessageChannel();
|
||||
await new Promise(resolve => {
|
||||
channel.port1.onmessage = resolve;
|
||||
channel.port2.postMessage(undefined);
|
||||
});
|
||||
channel.port1.close();
|
||||
channel.port2.close();
|
||||
// setTimeout ensures that we interleave with other setTimeouts.
|
||||
await new Promise(resolve => {
|
||||
realTimingFunctions.setTimeout.call(global, resolve);
|
||||
});
|
||||
}
|
||||
|
||||
function originalTimingFunctionsIntact() {
|
||||
return (
|
||||
global.setTimeout === realTimingFunctions.setTimeout &&
|
||||
|
||||
@@ -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);
|
||||
@@ -81,6 +87,37 @@ getJasmineRequireObj().DelayedFunctionScheduler = function(j$) {
|
||||
}
|
||||
};
|
||||
|
||||
// Returns whether there are any scheduled functions.
|
||||
// Returns true if there are any scheduled functions, otherwise false.
|
||||
this.isEmpty = function() {
|
||||
return this.scheduledFunctions_.length === 0;
|
||||
};
|
||||
|
||||
// Runs the next timeout in the queue, advancing the clock.
|
||||
this.runNextQueuedFunction = function(tickDate) {
|
||||
if (this.scheduledLookup_.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const newCurrentTime = this.scheduledLookup_[0];
|
||||
if (newCurrentTime >= this.currentTime_) {
|
||||
tickDate(newCurrentTime - this.currentTime_);
|
||||
this.currentTime_ = newCurrentTime;
|
||||
}
|
||||
|
||||
const funcsAtTime = this.scheduledFunctions_[this.currentTime_];
|
||||
const fn = funcsAtTime.shift();
|
||||
if (funcsAtTime.length === 0) {
|
||||
delete this.scheduledFunctions_[this.currentTime_];
|
||||
this.scheduledLookup_.splice(0, 1);
|
||||
}
|
||||
|
||||
if (fn.recurring) {
|
||||
this.reschedule_(fn);
|
||||
}
|
||||
fn.funcToCall.apply(null, fn.params || []);
|
||||
};
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
@@ -36,6 +36,7 @@ getJasmineRequireObj().Deprecator = function(j$) {
|
||||
|
||||
Deprecator.prototype.log_ = function(runnable, deprecation, options) {
|
||||
if (j$.isError_(deprecation)) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error(deprecation);
|
||||
return;
|
||||
}
|
||||
@@ -58,6 +59,7 @@ getJasmineRequireObj().Deprecator = function(j$) {
|
||||
context += '\n' + verboseNote;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line no-console
|
||||
console.error('DEPRECATION: ' + deprecation + context);
|
||||
};
|
||||
|
||||
|
||||
@@ -138,6 +138,15 @@ getJasmineRequireObj().Env = function(j$) {
|
||||
* @default true
|
||||
*/
|
||||
autoCleanClosures: true,
|
||||
/**
|
||||
* Whether to forbid duplicate spec or suite names. If set to true, using
|
||||
* the same name multiple times in the same immediate parent suite is an
|
||||
* error.
|
||||
* @name Configuration#forbidDuplicateNames
|
||||
* @type boolean
|
||||
* @default false
|
||||
*/
|
||||
forbidDuplicateNames: false,
|
||||
/**
|
||||
* Whether or not to issue warnings for certain deprecated functionality
|
||||
* every time it's used. If not set or set to false, deprecation warnings
|
||||
@@ -186,7 +195,8 @@ getJasmineRequireObj().Env = function(j$) {
|
||||
'hideDisabled',
|
||||
'stopOnSpecFailure',
|
||||
'stopSpecOnExpectationFailure',
|
||||
'autoCleanClosures'
|
||||
'autoCleanClosures',
|
||||
'forbidDuplicateNames'
|
||||
];
|
||||
|
||||
booleanProps.forEach(function(prop) {
|
||||
@@ -272,12 +282,19 @@ getJasmineRequireObj().Env = function(j$) {
|
||||
* @extends Error
|
||||
* @description Represents a failure of an expectation evaluated with
|
||||
* {@link throwUnless}. Properties of this error are a subset of the
|
||||
* properties of {@link Expectation} and have the same values.
|
||||
* properties of {@link ExpectationResult} and have the same values.
|
||||
*
|
||||
* Note: The expected and actual properties are deprecated and may be removed
|
||||
* in a future release. In many Jasmine configurations they are passed
|
||||
* through JSON serialization and deserialization, which is inherently
|
||||
* lossy. In such cases, the expected and actual values may be placeholders
|
||||
* or approximations of the original objects.
|
||||
*
|
||||
* @property {String} matcherName - The name of the matcher that was executed for this expectation.
|
||||
* @property {String} message - The failure message for the expectation.
|
||||
* @property {Boolean} passed - Whether the expectation passed or failed.
|
||||
* @property {Object} expected - If the expectation failed, what was the expected value.
|
||||
* @property {Object} actual - If the expectation failed, what actual value was produced.
|
||||
* @property {Object} expected - Deprecated. If the expectation failed, what was the expected value.
|
||||
* @property {Object} actual - Deprecated. If the expectation failed, what actual value was produced.
|
||||
*/
|
||||
const error = new Error(result.message);
|
||||
error.passed = result.passed;
|
||||
@@ -368,7 +385,9 @@ getJasmineRequireObj().Env = function(j$) {
|
||||
|
||||
// If we get here, all results have been reported and there's nothing we
|
||||
// can do except log the result and hope the user sees it.
|
||||
// eslint-disable-next-line no-console
|
||||
console.error('Jasmine received a result after the suite finished:');
|
||||
// eslint-disable-next-line no-console
|
||||
console.error(expectationResult);
|
||||
}
|
||||
|
||||
|
||||
@@ -16,19 +16,24 @@ getJasmineRequireObj().Expectation = function(j$) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Add some context for an {@link expect}
|
||||
* Add some context to be included in matcher failures for an
|
||||
* {@link expect|expectation}, so that it can be more easily distinguished
|
||||
* from similar expectations.
|
||||
* @function
|
||||
* @name matchers#withContext
|
||||
* @since 3.3.0
|
||||
* @param {String} message - Additional context to show when the matcher fails
|
||||
* @return {matchers}
|
||||
* @example
|
||||
* expect(things[0]).withContext('thing 0').toEqual('a');
|
||||
* expect(things[1]).withContext('thing 1').toEqual('b');
|
||||
*/
|
||||
Expectation.prototype.withContext = function withContext(message) {
|
||||
return addFilter(this, new ContextAddingFilter(message));
|
||||
};
|
||||
|
||||
/**
|
||||
* Invert the matcher following this {@link expect}
|
||||
* Invert the matcher following this {@link expect|expectation}
|
||||
* @member
|
||||
* @name matchers#not
|
||||
* @since 1.3.0
|
||||
|
||||
@@ -12,6 +12,7 @@ getJasmineRequireObj().GlobalErrors = function(j$) {
|
||||
|
||||
function dispatchBrowserError(error, event) {
|
||||
if (overrideHandler) {
|
||||
// See discussion of spyOnGlobalErrorsAsync in base.js
|
||||
overrideHandler(error);
|
||||
return;
|
||||
}
|
||||
@@ -55,6 +56,7 @@ getJasmineRequireObj().GlobalErrors = function(j$) {
|
||||
const handler = handlers[handlers.length - 1];
|
||||
|
||||
if (overrideHandler) {
|
||||
// See discussion of spyOnGlobalErrorsAsync in base.js
|
||||
overrideHandler(error);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
getJasmineRequireObj().ParallelReportDispatcher = function(j$) {
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* @class ParallelReportDispatcher
|
||||
* @implements Reporter
|
||||
@@ -17,7 +19,7 @@ getJasmineRequireObj().ParallelReportDispatcher = function(j$) {
|
||||
const ReportDispatcher = deps.ReportDispatcher || j$.ReportDispatcher;
|
||||
const QueueRunner = deps.QueueRunner || j$.QueueRunner;
|
||||
const globalErrors = deps.globalErrors || new j$.GlobalErrors();
|
||||
const dispatcher = ReportDispatcher(
|
||||
const dispatcher = new ReportDispatcher(
|
||||
j$.reporterEvents,
|
||||
function(queueRunnerOptions) {
|
||||
queueRunnerOptions = {
|
||||
|
||||
@@ -58,6 +58,7 @@ getJasmineRequireObj().makePrettyPrinter = function(j$) {
|
||||
) {
|
||||
try {
|
||||
this.emitScalar(value.toString());
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
} catch (e) {
|
||||
this.emitScalar('has-invalid-toString-method');
|
||||
}
|
||||
@@ -304,6 +305,7 @@ getJasmineRequireObj().makePrettyPrinter = function(j$) {
|
||||
value.toString !== Object.prototype.toString &&
|
||||
value.toString() !== Object.prototype.toString.call(value)
|
||||
);
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
} catch (e) {
|
||||
// The custom toString() threw.
|
||||
return true;
|
||||
|
||||
@@ -22,6 +22,7 @@ getJasmineRequireObj().QueueRunner = function(j$) {
|
||||
}
|
||||
|
||||
function fallbackOnMultipleDone() {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error(
|
||||
new Error(
|
||||
"An asynchronous function called its 'done' " +
|
||||
@@ -65,8 +66,8 @@ getJasmineRequireObj().QueueRunner = function(j$) {
|
||||
}
|
||||
|
||||
QueueRunner.prototype.execute = function() {
|
||||
this.handleFinalError = error => {
|
||||
this.onException(error);
|
||||
this.handleFinalError = (error, event) => {
|
||||
this.onException(errorOrMsgForGlobalError(error, event));
|
||||
};
|
||||
this.globalErrors.pushListener(this.handleFinalError);
|
||||
this.run(0);
|
||||
@@ -96,10 +97,8 @@ getJasmineRequireObj().QueueRunner = function(j$) {
|
||||
this.recordError_(iterativeIndex);
|
||||
};
|
||||
|
||||
function handleError(error) {
|
||||
// TODO probably shouldn't next() right away here.
|
||||
// That makes debugging async failures much more confusing.
|
||||
onException(error);
|
||||
function handleError(error, event) {
|
||||
onException(errorOrMsgForGlobalError(error, event));
|
||||
}
|
||||
const cleanup = once(() => {
|
||||
if (timeoutId !== void 0) {
|
||||
@@ -137,6 +136,7 @@ getJasmineRequireObj().QueueRunner = function(j$) {
|
||||
// Any error we catch here is probably due to a bug in Jasmine,
|
||||
// and it's not likely to end up anywhere useful if we let it
|
||||
// propagate. Log it so it can at least show up when debugging.
|
||||
// eslint-disable-next-line no-console
|
||||
console.error(error);
|
||||
}
|
||||
}
|
||||
@@ -285,5 +285,16 @@ getJasmineRequireObj().QueueRunner = function(j$) {
|
||||
};
|
||||
}
|
||||
|
||||
function errorOrMsgForGlobalError(error, event) {
|
||||
// TODO: In cases where error is a string or undefined, the error message
|
||||
// that gets sent to reporters will be `${message} thrown`, which could
|
||||
// be improved to not say "thrown" when the cause wasn't necessarily
|
||||
// an exception or to provide hints about throwing Errors rather than
|
||||
// strings.
|
||||
return (
|
||||
error || (event && event.message) || 'Global error event with no message'
|
||||
);
|
||||
}
|
||||
|
||||
return QueueRunner;
|
||||
};
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
getJasmineRequireObj().ReportDispatcher = function(j$) {
|
||||
'use strict';
|
||||
|
||||
function ReportDispatcher(methods, queueRunnerFactory, onLateError) {
|
||||
const dispatchedMethods = methods || [];
|
||||
|
||||
|
||||
@@ -177,8 +177,8 @@ getJasmineRequireObj().Runner = function(j$) {
|
||||
* @property {String} incompleteCode - Machine-readable explanation of why the suite was incomplete: 'focused', 'noSpecsFound', or undefined.
|
||||
* @property {Order} order - Information about the ordering (random or not) of this execution of the suite. Note that this property is not present when Jasmine is run in parallel mode.
|
||||
* @property {Int} numWorkers - Number of parallel workers. Note that this property is only present when Jasmine is run in parallel mode.
|
||||
* @property {Expectation[]} failedExpectations - List of expectations that failed in an {@link afterAll} at the global level.
|
||||
* @property {Expectation[]} deprecationWarnings - List of deprecation warnings that occurred at the global level.
|
||||
* @property {ExpectationResult[]} failedExpectations - List of expectations that failed in an {@link afterAll} at the global level.
|
||||
* @property {ExpectationResult[]} deprecationWarnings - List of deprecation warnings that occurred at the global level.
|
||||
* @since 2.4.0
|
||||
*/
|
||||
const jasmineDoneInfo = {
|
||||
|
||||
@@ -21,11 +21,11 @@ getJasmineRequireObj().Spec = function(j$) {
|
||||
this.onStart = attrs.onStart || function() {};
|
||||
this.autoCleanClosures =
|
||||
attrs.autoCleanClosures === undefined ? true : !!attrs.autoCleanClosures;
|
||||
this.getSpecName =
|
||||
attrs.getSpecName ||
|
||||
function() {
|
||||
return '';
|
||||
};
|
||||
|
||||
this.getPath = function() {
|
||||
return attrs.getPath ? attrs.getPath(this) : [];
|
||||
};
|
||||
|
||||
this.onLateError = attrs.onLateError || function() {};
|
||||
this.catchingExceptions =
|
||||
attrs.catchingExceptions ||
|
||||
@@ -148,9 +148,12 @@ getJasmineRequireObj().Spec = function(j$) {
|
||||
* @property {String} fullName - The full description including all ancestors of this spec.
|
||||
* @property {String|null} parentSuiteId - The ID of the suite containing this spec, or null if this spec is not in a describe().
|
||||
* @property {String} filename - The name of the file the spec was defined in.
|
||||
* @property {Expectation[]} failedExpectations - The list of expectations that failed during execution of this spec.
|
||||
* @property {Expectation[]} passedExpectations - The list of expectations that passed during execution of this spec.
|
||||
* @property {Expectation[]} deprecationWarnings - The list of deprecation warnings that occurred during execution this spec.
|
||||
* Note: The value may be incorrect if zone.js is installed or
|
||||
* `it`/`fit`/`xit` have been replaced with versions that don't maintain the
|
||||
* same call stack height as the originals.
|
||||
* @property {ExpectationResult[]} failedExpectations - The list of expectations that failed during execution of this spec.
|
||||
* @property {ExpectationResult[]} passedExpectations - The list of expectations that passed during execution of this spec.
|
||||
* @property {ExpectationResult[]} deprecationWarnings - The list of deprecation warnings that occurred during execution this spec.
|
||||
* @property {String} pendingReason - If the spec is {@link pending}, this will be the reason.
|
||||
* @property {String} status - Once the spec has completed, this string represents the pass/fail status of this spec.
|
||||
* @property {number} duration - The time in ms used by the spec execution, including any before/afterEach.
|
||||
@@ -251,7 +254,7 @@ getJasmineRequireObj().Spec = function(j$) {
|
||||
};
|
||||
|
||||
Spec.prototype.getFullName = function() {
|
||||
return this.getSpecName(this);
|
||||
return this.getPath().join(' ');
|
||||
};
|
||||
|
||||
Spec.prototype.addDeprecationWarning = function(deprecation) {
|
||||
@@ -305,6 +308,10 @@ getJasmineRequireObj().Spec = function(j$) {
|
||||
* @since 2.0.0
|
||||
*/
|
||||
Object.defineProperty(Spec.prototype, 'metadata', {
|
||||
// NOTE: Although most of jasmine-core only exposes these metadata objects,
|
||||
// actual Spec instances are still passed to Configuration#specFilter. Until
|
||||
// that is fixed, it's important to make sure that all metadata properties
|
||||
// also exist in compatible form on the underlying Spec.
|
||||
get: function() {
|
||||
if (!this.metadata_) {
|
||||
this.metadata_ = {
|
||||
@@ -333,7 +340,16 @@ getJasmineRequireObj().Spec = function(j$) {
|
||||
* @returns {string}
|
||||
* @since 2.0.0
|
||||
*/
|
||||
getFullName: this.getFullName.bind(this)
|
||||
getFullName: this.getFullName.bind(this),
|
||||
|
||||
/**
|
||||
* The full path of the spec, as an array of names.
|
||||
* @name Spec#getPath
|
||||
* @function
|
||||
* @returns {Array.<string>}
|
||||
* @since 5.7.0
|
||||
*/
|
||||
getPath: this.getPath.bind(this)
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user