Compare commits
115 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9cf9b856b0 | ||
|
|
db6c142afd | ||
|
|
1e691b2470 | ||
|
|
c5555dd8cc | ||
|
|
78c14f81a8 | ||
|
|
56e2832ebe | ||
|
|
9a9d3994da | ||
|
|
ff9feb29d3 | ||
|
|
fee7e6e64e | ||
|
|
18d4d38655 | ||
|
|
53e9bc68d2 | ||
|
|
2be50e1b87 | ||
|
|
27a1257b6d | ||
|
|
75658e0566 | ||
|
|
9a67c4e24d | ||
|
|
7ba53b25f7 | ||
|
|
dbc1f9244e | ||
|
|
489b83c61b | ||
|
|
c590095662 | ||
|
|
0688db88e9 | ||
|
|
124effe04b | ||
|
|
418e9a7728 | ||
|
|
6715f24fd0 | ||
|
|
fa481b2bd1 | ||
|
|
8309416cb2 | ||
|
|
4ccc7bf3ac | ||
|
|
cca6b2aa07 | ||
|
|
78940aa0fb | ||
|
|
3d8396da0a | ||
|
|
0bf9aff195 | ||
|
|
55b2e8846f | ||
|
|
3493519c9f | ||
|
|
62b5698a99 | ||
|
|
98849882a2 | ||
|
|
6665c4e123 | ||
|
|
3698f6fb5d | ||
|
|
60f34ec087 | ||
|
|
91bd3f8201 | ||
|
|
ca4fbcbccb | ||
|
|
e1532be726 | ||
|
|
54465f6f6a | ||
|
|
fa9939ae94 | ||
|
|
7978ad9889 | ||
|
|
af4662ad31 | ||
|
|
15c38c7728 | ||
|
|
b597975c7e | ||
|
|
09ce3a30b6 | ||
|
|
3bcbc2e3af | ||
|
|
fbaba902dc | ||
|
|
bf2e8e759e | ||
|
|
50e566bd67 | ||
|
|
4b7d5e3623 | ||
|
|
6449832e7e | ||
|
|
c6266b24b7 | ||
|
|
f16b81d4ef | ||
|
|
7feec406d9 | ||
|
|
f822ffea21 | ||
|
|
db65c3b131 | ||
|
|
fd37a7eac0 | ||
|
|
12219e80c1 | ||
|
|
a980ae6bf2 | ||
|
|
56ac8f5505 | ||
|
|
3780fe0b35 | ||
|
|
164a393932 | ||
|
|
759a867094 | ||
|
|
f94d0ceda9 | ||
|
|
8d99f27be8 | ||
|
|
63774597f0 | ||
|
|
a3e1abfa12 | ||
|
|
b89a870a59 | ||
|
|
ea3fc88803 | ||
|
|
d5884e33c6 | ||
|
|
138bf9be4b | ||
|
|
98d5284c19 | ||
|
|
2299c85751 | ||
|
|
8e3ec25f6d | ||
|
|
b009cd2922 | ||
|
|
8eee6ebb91 | ||
|
|
c15a1aaa6d | ||
|
|
5b06531cac | ||
|
|
42cca93926 | ||
|
|
395ef85954 | ||
|
|
5e88fde655 | ||
|
|
bb777e93e5 | ||
|
|
9d3fb167a2 | ||
|
|
3176eaf1d8 | ||
|
|
d31a431d1f | ||
|
|
84f78c1435 | ||
|
|
ff476b1982 | ||
|
|
d53d2ff3eb | ||
|
|
adfbd00c75 | ||
|
|
495e5fcd50 | ||
|
|
bc2aa7be25 | ||
|
|
af04599bb5 | ||
|
|
21db6ec0e3 | ||
|
|
2d07b3e6d7 | ||
|
|
6891789ed2 | ||
|
|
7a3d3c9360 | ||
|
|
1b2922e008 | ||
|
|
bd8d23f2a7 | ||
|
|
de26763868 | ||
|
|
f4be08b657 | ||
|
|
50ef882a1a | ||
|
|
c1cd5c6291 | ||
|
|
63ed2b3948 | ||
|
|
0183acc682 | ||
|
|
e15819c0dd | ||
|
|
f694194b2b | ||
|
|
94c00886a6 | ||
|
|
f5915d7963 | ||
|
|
15587f3ce3 | ||
|
|
3ecddc2555 | ||
|
|
6a7c0e6368 | ||
|
|
84daa0f5dc | ||
|
|
c6b3e947e9 |
@@ -1,9 +1,13 @@
|
|||||||
# Run tests against supported Node versions, and (except for pull requests)
|
# Run tests against supported Node versions, and (except for pull requests)
|
||||||
# against supported browsers.
|
# against supported browsers that are available on Saucelabs.
|
||||||
|
|
||||||
version: 2.1
|
version: 2.1
|
||||||
|
|
||||||
executors:
|
executors:
|
||||||
|
node24:
|
||||||
|
docker:
|
||||||
|
- image: cimg/node:24.0.0
|
||||||
|
working_directory: ~/workspace
|
||||||
node22:
|
node22:
|
||||||
docker:
|
docker:
|
||||||
- image: cimg/node:22.0.0
|
- image: cimg/node:22.0.0
|
||||||
@@ -14,7 +18,7 @@ executors:
|
|||||||
working_directory: ~/workspace
|
working_directory: ~/workspace
|
||||||
node18:
|
node18:
|
||||||
docker:
|
docker:
|
||||||
- image: cimg/node:18.0.0
|
- image: cimg/node:18.20.5
|
||||||
working_directory: ~/workspace
|
working_directory: ~/workspace
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
@@ -89,7 +93,7 @@ jobs:
|
|||||||
export SAUCE_TUNNEL_NAME=$CIRCLE_WORKFLOW_JOB_ID
|
export SAUCE_TUNNEL_NAME=$CIRCLE_WORKFLOW_JOB_ID
|
||||||
scripts/start-sauce-connect
|
scripts/start-sauce-connect
|
||||||
set +o errexit
|
set +o errexit
|
||||||
scripts/run-all-browsers
|
scripts/run-sauce-browsers
|
||||||
exitcode=$?
|
exitcode=$?
|
||||||
set -o errexit
|
set -o errexit
|
||||||
scripts/stop-sauce-connect
|
scripts/stop-sauce-connect
|
||||||
@@ -100,6 +104,9 @@ workflows:
|
|||||||
|
|
||||||
push:
|
push:
|
||||||
jobs:
|
jobs:
|
||||||
|
- build:
|
||||||
|
executor: node24
|
||||||
|
name: build_node_24
|
||||||
- build:
|
- build:
|
||||||
executor: node22
|
executor: node22
|
||||||
name: build_node_22
|
name: build_node_22
|
||||||
@@ -125,10 +132,10 @@ workflows:
|
|||||||
requires:
|
requires:
|
||||||
- build_node_18
|
- build_node_18
|
||||||
- test_parallel:
|
- test_parallel:
|
||||||
executor: node18
|
executor: node24
|
||||||
name: test_parallel_node_18
|
name: test_parallel_node_24
|
||||||
requires:
|
requires:
|
||||||
- build_node_18
|
- build_node_24
|
||||||
- test_parallel:
|
- test_parallel:
|
||||||
executor: node22
|
executor: node22
|
||||||
name: test_parallel_node_22
|
name: test_parallel_node_22
|
||||||
@@ -139,6 +146,11 @@ workflows:
|
|||||||
name: test_parallel_node_20
|
name: test_parallel_node_20
|
||||||
requires:
|
requires:
|
||||||
- build_node_20
|
- build_node_20
|
||||||
|
- test_parallel:
|
||||||
|
executor: node18
|
||||||
|
name: test_parallel_node_18
|
||||||
|
requires:
|
||||||
|
- build_node_18
|
||||||
- test_browsers:
|
- test_browsers:
|
||||||
requires:
|
requires:
|
||||||
- build_node_18
|
- build_node_18
|
||||||
|
|||||||
23
.github/workflows/safari.yml
vendored
Normal file
23
.github/workflows/safari.yml
vendored
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
name: Test in latest available Safari
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
pull_request:
|
||||||
|
workflow_dispatch:
|
||||||
|
schedule:
|
||||||
|
- cron: '0 0 * * *'
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: macos-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Report Safari version
|
||||||
|
run: osascript -e 'get version of application "Safari"'
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: Use Node.js 22.x
|
||||||
|
uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: 22.x
|
||||||
|
- run: npm install
|
||||||
|
- run: npm run build
|
||||||
|
- run: JASMINE_BROWSER=safari npm run ci
|
||||||
14
README.md
14
README.md
@@ -27,13 +27,13 @@ for information on writing specs, and [the FAQ](https://jasmine.github.io/pages/
|
|||||||
Jasmine tests itself across popular browsers (Safari, Chrome, Firefox, and
|
Jasmine tests itself across popular browsers (Safari, Chrome, Firefox, and
|
||||||
Microsoft Edge) as well as Node.
|
Microsoft Edge) as well as Node.
|
||||||
|
|
||||||
| Environment | Supported versions |
|
| Environment | Supported versions |
|
||||||
|-------------------|----------------------------|
|
|-------------------|----------------------------------|
|
||||||
| Node | 18, 20, 22 |
|
| Node | 18.20.5+*, 20, 22, 24 |
|
||||||
| Safari | 15*, 16*, 17* |
|
| Safari | 16*, 17*, 26* |
|
||||||
| Chrome | Evergreen |
|
| Chrome | Evergreen |
|
||||||
| Firefox | Evergreen, 102*, 115*, 128 |
|
| Firefox | Evergreen, 102*, 115*, 128*, 140 |
|
||||||
| Edge | Evergreen |
|
| Edge | Evergreen |
|
||||||
|
|
||||||
For evergreen browsers, each version of Jasmine is tested against the version of the browser that is available to us
|
For evergreen browsers, each version of Jasmine is tested against the version of the browser that is available to us
|
||||||
at the time of release. Other browsers, as well as older & newer versions of some supported browsers, are likely to work.
|
at the time of release. Other browsers, as well as older & newer versions of some supported browsers, are likely to work.
|
||||||
|
|||||||
15
RELEASE.md
15
RELEASE.md
@@ -28,9 +28,18 @@ should also rev to that version.
|
|||||||
|
|
||||||
When ready to release - specs are all green and the stories are done:
|
When ready to release - specs are all green and the stories are done:
|
||||||
|
|
||||||
1. Update the release notes in `release_notes` - use the Anchorman gem to generate the markdown file and edit accordingly. Include a list of supported environments.
|
1. Update the release notes in `release_notes` - use the Anchorman gem to
|
||||||
1. Update the version in `package.json`
|
generate the Markdown file and edit accordingly. Include a list of supported
|
||||||
1. Run `npm run build`.
|
environments. Get that information from these places:
|
||||||
|
* For Node, see .circleci/config.yml or the README.
|
||||||
|
* For Firefox ESR and Safari <=17, see scripts/run-sauce-browsers or the README.
|
||||||
|
* For evergreen browsers, trigger a Circle CI run and check the
|
||||||
|
[Saucelabs dashboard](https://app.saucelabs.com/dashboard/tests?ownerId=90a771d55857492da3bd5251a2d92457&ownerType=user&ownerName=jasmine-js&start=last7days)
|
||||||
|
once it's finished.
|
||||||
|
* For Safari >17, trigger the [Safari action](https://github.com/jasmine/jasmine/actions/workflows/safari.yml)
|
||||||
|
and get the version from the output.
|
||||||
|
2. Update the version in `package.json`
|
||||||
|
3. Run `npm run build`.
|
||||||
|
|
||||||
### Commit and push core changes
|
### Commit and push core changes
|
||||||
|
|
||||||
|
|||||||
@@ -22,7 +22,9 @@ export default defineConfig([{
|
|||||||
...globals.node,
|
...globals.node,
|
||||||
},
|
},
|
||||||
|
|
||||||
ecmaVersion: 2018,
|
// 2022 isn't exactly right, but it's the earliest version that allows
|
||||||
|
// private properties.
|
||||||
|
ecmaVersion: 2022,
|
||||||
sourceType: "commonjs",
|
sourceType: "commonjs",
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@@ -81,6 +81,13 @@ jasmineRequire.HtmlReporter = function(j$) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @class HtmlReporter
|
||||||
|
* @classdesc Displays results and allows re-running individual specs and suites.
|
||||||
|
* @implements {Reporter}
|
||||||
|
* @param options Options object. See lib/jasmine-core/boot1.js for details.
|
||||||
|
* @since 1.2.0
|
||||||
|
*/
|
||||||
function HtmlReporter(options) {
|
function HtmlReporter(options) {
|
||||||
function config() {
|
function config() {
|
||||||
return (options.env && options.env.configuration()) || {};
|
return (options.env && options.env.configuration()) || {};
|
||||||
@@ -98,6 +105,11 @@ jasmineRequire.HtmlReporter = function(j$) {
|
|||||||
const deprecationWarnings = [];
|
const deprecationWarnings = [];
|
||||||
const failures = [];
|
const failures = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes the reporter. Should be called before {@link Env#execute}.
|
||||||
|
* @function
|
||||||
|
* @name HtmlReporter#initialize
|
||||||
|
*/
|
||||||
this.initialize = function() {
|
this.initialize = function() {
|
||||||
clearPrior();
|
clearPrior();
|
||||||
htmlReporterMain = createDom(
|
htmlReporterMain = createDom(
|
||||||
@@ -557,6 +569,11 @@ jasmineRequire.HtmlReporter = function(j$) {
|
|||||||
'a',
|
'a',
|
||||||
{ href: specHref(resultNode.result) },
|
{ href: specHref(resultNode.result) },
|
||||||
specDescription
|
specDescription
|
||||||
|
),
|
||||||
|
createDom(
|
||||||
|
'span',
|
||||||
|
{ className: 'jasmine-spec-duration' },
|
||||||
|
'(' + resultNode.result.duration + 'ms)'
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
@@ -876,6 +893,8 @@ jasmineRequire.HtmlReporter = function(j$) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
jasmineRequire.HtmlSpecFilter = function() {
|
jasmineRequire.HtmlSpecFilter = function() {
|
||||||
|
// Legacy HTML spec filter, preserved for backward compatibility with
|
||||||
|
// boot files that predate HtmlExactSpecFilterV2
|
||||||
function HtmlSpecFilter(options) {
|
function HtmlSpecFilter(options) {
|
||||||
const filterString =
|
const filterString =
|
||||||
options &&
|
options &&
|
||||||
@@ -883,6 +902,13 @@ jasmineRequire.HtmlSpecFilter = function() {
|
|||||||
options.filterString().replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&');
|
options.filterString().replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&');
|
||||||
const filterPattern = new RegExp(filterString);
|
const filterPattern = new RegExp(filterString);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines whether the spec with the specified name should be executed.
|
||||||
|
* @name HtmlSpecFilter#matches
|
||||||
|
* @function
|
||||||
|
* @param {string} specName The full name of the spec
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
this.matches = function(specName) {
|
this.matches = function(specName) {
|
||||||
return filterPattern.test(specName);
|
return filterPattern.test(specName);
|
||||||
};
|
};
|
||||||
@@ -916,38 +942,57 @@ jasmineRequire.ResultsNode = function() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
jasmineRequire.QueryString = function() {
|
jasmineRequire.QueryString = function() {
|
||||||
function QueryString(options) {
|
/**
|
||||||
this.navigateWithNewParam = function(key, value) {
|
* Reads and manipulates the query string.
|
||||||
options.getWindowLocation().search = this.fullStringWithNewParam(
|
* @since 2.0.0
|
||||||
|
*/
|
||||||
|
class QueryString {
|
||||||
|
#getWindowLocation;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param options Object with a getWindowLocation property, which should be
|
||||||
|
* a function returning the current value of window.location.
|
||||||
|
*/
|
||||||
|
constructor(options) {
|
||||||
|
this.#getWindowLocation = options.getWindowLocation;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the specified query parameter and navigates to the resulting URL.
|
||||||
|
* @param {string} key
|
||||||
|
* @param {string} value
|
||||||
|
*/
|
||||||
|
navigateWithNewParam(key, value) {
|
||||||
|
this.#getWindowLocation().search = this.fullStringWithNewParam(
|
||||||
key,
|
key,
|
||||||
value
|
value
|
||||||
);
|
);
|
||||||
};
|
|
||||||
|
|
||||||
this.fullStringWithNewParam = function(key, value) {
|
|
||||||
const paramMap = queryStringToParamMap();
|
|
||||||
paramMap[key] = value;
|
|
||||||
return toQueryString(paramMap);
|
|
||||||
};
|
|
||||||
|
|
||||||
this.getParam = function(key) {
|
|
||||||
return queryStringToParamMap()[key];
|
|
||||||
};
|
|
||||||
|
|
||||||
return this;
|
|
||||||
|
|
||||||
function toQueryString(paramMap) {
|
|
||||||
const qStrPairs = [];
|
|
||||||
for (const prop in paramMap) {
|
|
||||||
qStrPairs.push(
|
|
||||||
encodeURIComponent(prop) + '=' + encodeURIComponent(paramMap[prop])
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return '?' + qStrPairs.join('&');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function queryStringToParamMap() {
|
/**
|
||||||
const paramStr = options.getWindowLocation().search.substring(1);
|
* Returns a new URL based on the current location, with the specified
|
||||||
|
* query parameter set.
|
||||||
|
* @param {string} key
|
||||||
|
* @param {string} value
|
||||||
|
* @return {string}
|
||||||
|
*/
|
||||||
|
fullStringWithNewParam(key, value) {
|
||||||
|
const paramMap = this.#queryStringToParamMap();
|
||||||
|
paramMap[key] = value;
|
||||||
|
return toQueryString(paramMap);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the value of the specified query parameter.
|
||||||
|
* @param {string} key
|
||||||
|
* @return {string}
|
||||||
|
*/
|
||||||
|
getParam(key) {
|
||||||
|
return this.#queryStringToParamMap()[key];
|
||||||
|
}
|
||||||
|
|
||||||
|
#queryStringToParamMap() {
|
||||||
|
const paramStr = this.#getWindowLocation().search.substring(1);
|
||||||
let params = [];
|
let params = [];
|
||||||
const paramMap = {};
|
const paramMap = {};
|
||||||
|
|
||||||
@@ -967,5 +1012,15 @@ jasmineRequire.QueryString = function() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function toQueryString(paramMap) {
|
||||||
|
const qStrPairs = [];
|
||||||
|
for (const prop in paramMap) {
|
||||||
|
qStrPairs.push(
|
||||||
|
encodeURIComponent(prop) + '=' + encodeURIComponent(paramMap[prop])
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return '?' + qStrPairs.join('&');
|
||||||
|
}
|
||||||
|
|
||||||
return QueryString;
|
return QueryString;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -229,6 +229,9 @@ body {
|
|||||||
.jasmine_html-reporter .jasmine-specs li.jasmine-excluded a:before {
|
.jasmine_html-reporter .jasmine-specs li.jasmine-excluded a:before {
|
||||||
content: "• ";
|
content: "• ";
|
||||||
}
|
}
|
||||||
|
.jasmine_html-reporter .jasmine-specs li .jasmine-spec-duration {
|
||||||
|
margin-left: 1em;
|
||||||
|
}
|
||||||
.jasmine_html-reporter .jasmine-description + .jasmine-suite {
|
.jasmine_html-reporter .jasmine-description + .jasmine-suite {
|
||||||
margin-top: 0;
|
margin-top: 0;
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "jasmine-core",
|
"name": "jasmine-core",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"version": "5.7.0",
|
"version": "5.13.0",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/jasmine/jasmine.git"
|
"url": "https://github.com/jasmine/jasmine.git"
|
||||||
@@ -49,12 +49,10 @@
|
|||||||
"jasmine-browser-runner": "github:jasmine/jasmine-browser-runner",
|
"jasmine-browser-runner": "github:jasmine/jasmine-browser-runner",
|
||||||
"jsdom": "^26.0.0",
|
"jsdom": "^26.0.0",
|
||||||
"prettier": "1.17.1",
|
"prettier": "1.17.1",
|
||||||
"rimraf": "^5.0.10",
|
"sass": "^1.58.3"
|
||||||
"sass": "^1.58.3",
|
|
||||||
"shelljs": "^0.9.2"
|
|
||||||
},
|
},
|
||||||
"browserslist": [
|
"browserslist": [
|
||||||
"Safari >= 15",
|
"Safari >= 16",
|
||||||
"Firefox >= 102",
|
"Firefox >= 102",
|
||||||
"last 2 Chrome versions",
|
"last 2 Chrome versions",
|
||||||
"last 2 Edge versions"
|
"last 2 Edge versions"
|
||||||
|
|||||||
54
release_notes/5.10.0.md
Normal file
54
release_notes/5.10.0.md
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
# Jasmine Core 5.10.0 Release Notes
|
||||||
|
|
||||||
|
## New Features
|
||||||
|
|
||||||
|
* Optionally detect promise rejections that are handled after an initial
|
||||||
|
unhandled promise rejection event and don't report them as errors.
|
||||||
|
This is off by default because it comes with a performance cost. It can be
|
||||||
|
enabled by setting the `detectLateRejectionHandling` config property to true.
|
||||||
|
* Add `getSpecProperty` to retrieve data that was set with `setSpecProperty`.
|
||||||
|
* Merges [#2072](https://github.com/jasmine/jasmine/pull/2072) from @bonkevin
|
||||||
|
* Show spec duration in the HTML reporter.
|
||||||
|
* Merges [#2073](https://github.com/jasmine/jasmine/pull/2073) from @bonkevin
|
||||||
|
* Protect `GlobalErrors` against monkey patching.
|
||||||
|
|
||||||
|
All currently shipped versions of zone.js contain a monkey patch that fails
|
||||||
|
to pass constructor arguments on to `GlobalErrors`. This patch normally has
|
||||||
|
no effect because zone.js is normally installed after `GlobalErrors` is
|
||||||
|
instantiated, but it would crash Jasmine if it was applied early enough.
|
||||||
|
|
||||||
|
## Deprecations
|
||||||
|
|
||||||
|
* Issue a deprecation warning if the suite/spec order passed as a parameter to
|
||||||
|
`Env#execute` causes a suite to be re-entered.
|
||||||
|
|
||||||
|
## Changes to supported environments
|
||||||
|
|
||||||
|
* Added Firefox 140 (current ESR) to supported environments
|
||||||
|
* Demoted Firefox 128 (previous ESR) to best-effort support
|
||||||
|
|
||||||
|
## Internal improvements
|
||||||
|
|
||||||
|
* Core suite/spec execution flow has been significantly simplified.
|
||||||
|
|
||||||
|
## Supported environments
|
||||||
|
|
||||||
|
This version has been tested in the following environments.
|
||||||
|
|
||||||
|
| Environment | Supported versions |
|
||||||
|
|-------------------|--------------------------------|
|
||||||
|
| Node | 18.20.5**, 20, 22, 24 |
|
||||||
|
| Safari | 15**, 16**, 17** |
|
||||||
|
| Chrome | 139* |
|
||||||
|
| Firefox | 102**, 115**, 128**, 140, 142* |
|
||||||
|
| Edge | 139* |
|
||||||
|
|
||||||
|
\* 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)_
|
||||||
66
release_notes/5.11.0.md
Normal file
66
release_notes/5.11.0.md
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
# Jasmine Core 5.11.0 Release Notes
|
||||||
|
|
||||||
|
## New features
|
||||||
|
|
||||||
|
* `detectLateRejectionHandling` works in `beforeAll` and `afterAll` as well as
|
||||||
|
in specs.
|
||||||
|
* Clicking a link in the HTML reporter does exact filtering rather than a
|
||||||
|
substring match.
|
||||||
|
|
||||||
|
If you use jasmine-browser-runner or load boot1.js directly from jasmine-core,
|
||||||
|
you'll get the new filtering behavior automatically. Otherwise, it requires
|
||||||
|
several changes to boot1.js:
|
||||||
|
|
||||||
|
1. Add `queryString` to the options object passed to `HtmlReporter`, and
|
||||||
|
remove `filterSpecs`.
|
||||||
|
2. Instantiate a `jasmine.HtmlExactSpecFilter` instead of a
|
||||||
|
`jasmine.HtmlSpecFilter`.
|
||||||
|
2. Change the body of `config.specFilter` from
|
||||||
|
`return specFilter.matches(spec.getFullName());` to
|
||||||
|
`return specFilter.matches(spec)`
|
||||||
|
|
||||||
|
For a working example, see lib/jasmine-core/boot1.js in this package.
|
||||||
|
Old boot1.js files will still work, but you'll get the old filtering behavior.
|
||||||
|
|
||||||
|
## Bug fixes
|
||||||
|
|
||||||
|
* Fixed global error handling when the env is executed repeatedly
|
||||||
|
|
||||||
|
## Changes to supported environments
|
||||||
|
|
||||||
|
* Safari 15 is no longer supported.
|
||||||
|
|
||||||
|
## Documentation improvements
|
||||||
|
|
||||||
|
* Added API reference docs for classes used in browser boot files
|
||||||
|
* Documented the order properties of `jasmineStarted` and `jasmineDone` events
|
||||||
|
|
||||||
|
## Internal improvements
|
||||||
|
|
||||||
|
* Unified top suite and regular suite execution
|
||||||
|
* Converted `Spec`, `Suite`, and `QueryString` to ES6 classes
|
||||||
|
* Extracted configuration out of `Env`
|
||||||
|
* Updated tests to characterize suite/spec reporting more completely
|
||||||
|
* Adopted `forbidDuplicateNames: true` in jasmine-core's own tests
|
||||||
|
|
||||||
|
## Supported environments
|
||||||
|
|
||||||
|
This version has been tested in the following environments.
|
||||||
|
|
||||||
|
| Environment | Supported versions |
|
||||||
|
|-------------------|--------------------------------|
|
||||||
|
| Node | 18.20.5**, 20, 22, 24 |
|
||||||
|
| Safari | 16**, 17** |
|
||||||
|
| Chrome | 140* |
|
||||||
|
| Firefox | 102**, 115**, 128**, 140, 143* |
|
||||||
|
| Edge | 140* |
|
||||||
|
|
||||||
|
\* 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)_
|
||||||
27
release_notes/5.12.0.md
Normal file
27
release_notes/5.12.0.md
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
# Jasmine Core 5.12.0 Release Notes
|
||||||
|
|
||||||
|
This release reverts the exact spec filtering feature introduced in 5.11.0,
|
||||||
|
which broke spec filtering in Karma.
|
||||||
|
|
||||||
|
## Supported environments
|
||||||
|
|
||||||
|
This version has been tested in the following environments.
|
||||||
|
|
||||||
|
| Environment | Supported versions |
|
||||||
|
|-------------|--------------------------------|
|
||||||
|
| Node | 18.20.5**, 20, 22, 24 |
|
||||||
|
| Safari | 16**, 17** |
|
||||||
|
| Chrome | 141* |
|
||||||
|
| Firefox | 102**, 115**, 128**, 140, 143* |
|
||||||
|
| Edge | 140* |
|
||||||
|
|
||||||
|
\* 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)_
|
||||||
29
release_notes/5.12.1.md
Normal file
29
release_notes/5.12.1.md
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
Jasmine Core 5.12.1 Release Notes
|
||||||
|
|
||||||
|
## Bug fixes
|
||||||
|
|
||||||
|
* Fix custom matchers in top-level specs
|
||||||
|
* Merges [#2088](https://github.com/jasmine/jasmine/pull/2088) from @bonkevin
|
||||||
|
|
||||||
|
## Supported environments
|
||||||
|
|
||||||
|
This version has been tested in the following environments.
|
||||||
|
|
||||||
|
| Environment | Supported versions |
|
||||||
|
|-------------|--------------------------------|
|
||||||
|
| Node | 18.20.5**, 20, 22, 24 |
|
||||||
|
| Safari | 16**, 17** |
|
||||||
|
| Chrome | 141* |
|
||||||
|
| Firefox | 102**, 115**, 128**, 140, 144* |
|
||||||
|
| Edge | 141* |
|
||||||
|
|
||||||
|
\* 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)_
|
||||||
44
release_notes/5.13.0.md
Normal file
44
release_notes/5.13.0.md
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
# Jasmine Core 5.13.0 Release Notes
|
||||||
|
|
||||||
|
## Changes to supported environments
|
||||||
|
|
||||||
|
Safari 26 is now supported on a best-effort basis.
|
||||||
|
|
||||||
|
Due to the limited availability of Safari 18 and later on free CI services,
|
||||||
|
Safari support in future jasmine-core versions will be limited to:
|
||||||
|
|
||||||
|
* Best-effort support for the latest Safari version available on GitHub Actions,
|
||||||
|
which may change at any time.
|
||||||
|
* Best-effort support for Safari 16 and 17 for as long as it remains practical.
|
||||||
|
|
||||||
|
## New Features
|
||||||
|
|
||||||
|
* New `extraItStackFrames` and `extraDescribeStackFrames` config options to fix
|
||||||
|
the filename properties of reporter events in configurations that wrap
|
||||||
|
`it`/`describe`, such as zone.js. The `filename` properties of reporter events
|
||||||
|
are no longer deprecated.
|
||||||
|
* `jasmine.allOf` asymmetric equality tester
|
||||||
|
* Merges [#2087](https://github.com/jasmine/jasmine/issues/2083) from @jonahd-g
|
||||||
|
* Fixes [#2083](https://github.com/jasmine/jasmine/pull/2087)
|
||||||
|
|
||||||
|
## Supported environments
|
||||||
|
|
||||||
|
This version has been tested in the following environments.
|
||||||
|
|
||||||
|
| Environment | Supported versions |
|
||||||
|
|-------------|--------------------------------|
|
||||||
|
| Node | 18.20.5**, 20, 22, 24 |
|
||||||
|
| Safari** | 16, 17, 26.1 |
|
||||||
|
| Chrome | 142* |
|
||||||
|
| Firefox | 102**, 115**, 128**, 140, 145* |
|
||||||
|
| Edge | 142* |
|
||||||
|
|
||||||
|
\* 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)_
|
||||||
@@ -6,7 +6,6 @@
|
|||||||
to automatically tick the clock asynchronously
|
to automatically tick the clock asynchronously
|
||||||
* Merges #2042 from @atscott and @stephenfarrar
|
* Merges #2042 from @atscott and @stephenfarrar
|
||||||
* Fixes #1725
|
* Fixes #1725
|
||||||
* Fixes #1932
|
|
||||||
|
|
||||||
* Expose [spec path](https://jasmine.github.io/api/5.7/Spec.html#getPath) as an
|
* 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
|
array of names in addition to the existing concatenated name
|
||||||
@@ -54,10 +53,9 @@ This version has been tested in the following environments.
|
|||||||
|
|
||||||
\* Evergreen browser. Each version of Jasmine is tested against the latest
|
\* Evergreen browser. Each version of Jasmine is tested against the latest
|
||||||
version available at release time.<br>
|
version available at release time.<br>
|
||||||
\** Environments that are past end of life are supported on a best-effort basis.
|
\** Supported on a best-effort basis. Support for these versions may be dropped
|
||||||
They may be dropped in a future minor release of Jasmine if continued support
|
if it becomes impractical, and bugs affecting only these versions may not be
|
||||||
becomes impractical.
|
treated as release blockers.
|
||||||
|
|
||||||
|
|
||||||
------
|
------
|
||||||
|
|
||||||
|
|||||||
28
release_notes/5.7.1.md
Normal file
28
release_notes/5.7.1.md
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
# Jasmine Core 5.7.1 Release Notes
|
||||||
|
|
||||||
|
## Bug fixes
|
||||||
|
|
||||||
|
* Ensure that uninstalling the clock also stops auto tick
|
||||||
|
* Merges #2057 from @atscott
|
||||||
|
|
||||||
|
## Supported environments
|
||||||
|
|
||||||
|
This version has been tested in the following environments.
|
||||||
|
|
||||||
|
| Environment | Supported versions |
|
||||||
|
|-------------------|-------------------------|
|
||||||
|
| Node | 18**, 20, 22 |
|
||||||
|
| Safari | 15**, 16**, 17** |
|
||||||
|
| Chrome | 136* |
|
||||||
|
| Firefox | 102**, 115**, 128, 138* |
|
||||||
|
| Edge | 135* |
|
||||||
|
|
||||||
|
\* 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)_
|
||||||
44
release_notes/5.8.0.md
Normal file
44
release_notes/5.8.0.md
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
# Jasmine Core 5.8.0 Release Notes
|
||||||
|
|
||||||
|
## New Features
|
||||||
|
|
||||||
|
* Allow passing a function to `saveArgumentsByValue` to customize how arguments
|
||||||
|
are saved
|
||||||
|
* Merges [#2062](https://github.com/jasmine/jasmine/pull/2062) from @evanwaslh
|
||||||
|
* Fixes [#1886](https://github.com/jasmine/jasmine/issues/1886)
|
||||||
|
* Use custom object formatters in spy strategy mismatch errors
|
||||||
|
* Include function names in pretty printer output
|
||||||
|
* Improve performance of autoTick in Node
|
||||||
|
* Merges [#2058](https://github.com/jasmine/jasmine/pull/2058) from @atscott
|
||||||
|
|
||||||
|
## Bug Fixes
|
||||||
|
|
||||||
|
* Fix diff building when only one side has a custom object formatter
|
||||||
|
* Fixes [#2061](https://github.com/jasmine/jasmine/issues/2061)
|
||||||
|
|
||||||
|
## Documentation improvements
|
||||||
|
|
||||||
|
* Added Node 24 to supported environments
|
||||||
|
|
||||||
|
## Supported environments
|
||||||
|
|
||||||
|
This version has been tested in the following environments.
|
||||||
|
|
||||||
|
| Environment | Supported versions |
|
||||||
|
|-------------------|-------------------------|
|
||||||
|
| Node | 18**, 20, 22, 24 |
|
||||||
|
| Safari | 15**, 16**, 17** |
|
||||||
|
| Chrome | 137* |
|
||||||
|
| Firefox | 102**, 115**, 128, 139* |
|
||||||
|
| Edge | 137* |
|
||||||
|
|
||||||
|
\* 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)_
|
||||||
56
release_notes/5.9.0.md
Normal file
56
release_notes/5.9.0.md
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
# Jasmine Core 5.9.0 Release Notes
|
||||||
|
|
||||||
|
## Bug fixes
|
||||||
|
|
||||||
|
* Avoid generating mock clock timer IDs that conflict with native ones
|
||||||
|
* Fixes [#2068](https://github.com/jasmine/jasmine/issues/2068)
|
||||||
|
* Merges [#2069](https://github.com/jasmine/jasmine/pull/2069) from @atscott
|
||||||
|
|
||||||
|
## Deprecations and changes to platform support
|
||||||
|
|
||||||
|
* Node versions before 18.20.5 are no longer supported
|
||||||
|
|
||||||
|
Older 18.x versions might still work, but Jasmine is no longer tested in them
|
||||||
|
prior to release.
|
||||||
|
* Document that the filename properties of suite and spec results are deprecated
|
||||||
|
|
||||||
|
These properties are incorrect in many configurations. They'll be removed in
|
||||||
|
the next major release unless there is enough user interest in fixing them.
|
||||||
|
See <https://github.com/jasmine/jasmine/issues/2065>.
|
||||||
|
|
||||||
|
## Internal improvements
|
||||||
|
|
||||||
|
* Extensive GlobalErrors refactoring
|
||||||
|
* Removed many of the error dispatching differences between browsers and Node
|
||||||
|
* Split into portable and platform-specific parts
|
||||||
|
* Converted to ES6 classes
|
||||||
|
* Removed unnecessary errorWithStack helper
|
||||||
|
|
||||||
|
Jasmine no longer runs on platforms that create errors without stack traces.
|
||||||
|
* Removed protections against user code redefining undefined
|
||||||
|
|
||||||
|
Jasmine no longer runs on platforms that allow redefining undefined.
|
||||||
|
* Removed rimraf and shelljs dev dependencies
|
||||||
|
|
||||||
|
## Supported environments
|
||||||
|
|
||||||
|
This version has been tested in the following environments.
|
||||||
|
|
||||||
|
| Environment | Supported versions |
|
||||||
|
|-------------------|-------------------------|
|
||||||
|
| Node | 18.20.5**, 20, 22, 24 |
|
||||||
|
| Safari | 15**, 16**, 17** |
|
||||||
|
| Chrome | 138* |
|
||||||
|
| Firefox | 102**, 115**, 128, 140* |
|
||||||
|
| Edge | 138* |
|
||||||
|
|
||||||
|
\* 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)_
|
||||||
100
release_notes/6.0.0-alpha.0.md
Normal file
100
release_notes/6.0.0-alpha.0.md
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
# Jasmine Core 6.0.0-alpha.0 Release Notes
|
||||||
|
|
||||||
|
This is a pre-release, intended to offer a preview of breaking changes and to
|
||||||
|
solicit feedback.
|
||||||
|
|
||||||
|
## A Note About Pre-Release Compatibility
|
||||||
|
|
||||||
|
There may be additional breaking changes in future 6.0 pre-releases or in the
|
||||||
|
final 6.0 release. That's allowed by the semver specification, but users are
|
||||||
|
sometimes unpleasantly surprised by it.
|
||||||
|
|
||||||
|
NPM's implementation of carat version ranges assumes that subsequent
|
||||||
|
pre-releases and final releases are fully compatible with earlier pre-releases.
|
||||||
|
If your package.json contains `"jasmine-core": "^6.0.0-alpha.0`,
|
||||||
|
NPM might install any later 6.x version even though there is no guarantee of
|
||||||
|
compatibility. If that isn't ok, you should specify an exact pre-release version:
|
||||||
|
`"jasmine-core": "6.0.0-alpha.0`.
|
||||||
|
|
||||||
|
## Changes to supported environments
|
||||||
|
|
||||||
|
* Node 18 is no longer supported.
|
||||||
|
|
||||||
|
## Breaking changes
|
||||||
|
|
||||||
|
### General
|
||||||
|
|
||||||
|
* Private APIs have been removed from the `jasmine` namespace.
|
||||||
|
|
||||||
|
The purpose of this change is to reduce the risk of users inadvertently
|
||||||
|
depending on private APIs. Anything that's not covered by
|
||||||
|
[the documentation](https://jasmine.github.io/pages/docs_home.html) remains
|
||||||
|
private regardless of namespacing. Private APIs may be changed or removed in
|
||||||
|
any release. This change is being made in a major release as a courtesy to users of
|
||||||
|
libraries that depend on private APIs.
|
||||||
|
|
||||||
|
* Arguments to `setSpecProperty`/`setSuiteProperty` must be both
|
||||||
|
structured-cloneable and JSON-serializable.
|
||||||
|
* Mock clock timing functions cannot be spied on. Previously this "worked" but
|
||||||
|
prevented the mock clock from uninstalling itself.
|
||||||
|
* The default value of the `forbidDuplicateNames` config option has been
|
||||||
|
changed to true.
|
||||||
|
* The mock clock no longer supports the eval forms of `setTimeout` and
|
||||||
|
`setInterval`.
|
||||||
|
* If an execution order is passed to `Env#execute`, it must not enter any suite
|
||||||
|
more than once.
|
||||||
|
* The argument passed to spec filters is a
|
||||||
|
[spec metadata](https://jasmine.github.io/api/6.0.0-alpha.0/Spec.html)
|
||||||
|
instance, not the internal spec object.
|
||||||
|
|
||||||
|
### Changes that affect reporters
|
||||||
|
|
||||||
|
This release includes changes that are intended to streamline and clarify the
|
||||||
|
reporter interface, prevent sharing of mutable state, and prevent bugs involving
|
||||||
|
non-serializable objects. These changes should be compatible with most existing
|
||||||
|
reporters but could break reporters that manage their internal state in unusual
|
||||||
|
ways. Please [open an issue](https://github.com/jasmine/jasmine/issues/new?template=bug_report.yml)
|
||||||
|
if you find a published reporter package that works with jasmine-core 5.x but
|
||||||
|
not with this release.
|
||||||
|
|
||||||
|
* Irrelevant properties such as `status` and `failedExpectations` are omitted
|
||||||
|
from [the event passed to specStarted](https://jasmine.github.io/api/6.0.0-alpha.0/global.html#SpecStartedEvent).
|
||||||
|
* Reporter events are deep-cloned before being passed to each reporter. This
|
||||||
|
protects reporters against later mutation by jasmine-core or other reporters.
|
||||||
|
* The `expected` and `actual` properties of
|
||||||
|
[passed and failed expectations](https://jasmine.github.io/api/6.0.0-alpha.0/global.html#ExpectationResult)
|
||||||
|
have been removed.
|
||||||
|
* The [order](https://jasmine.github.io/api/6.0.0-alpha.0/global.html#Order)
|
||||||
|
property of the`jasmineStarted` and `jasmineDone` reporter events no longer
|
||||||
|
includes undocumented properties.
|
||||||
|
|
||||||
|
### Changes to Node boot functions
|
||||||
|
|
||||||
|
* [boot](https://jasmine.github.io/api/6.0.0-alpha.0/module-jasmine-core.html#.boot)
|
||||||
|
defaults to creating a new core instance each time it's called. This restores
|
||||||
|
the pre-5.0 default behavior.
|
||||||
|
* [noGlobals](https://jasmine.github.io/api/6.0.0-alpha.0/module-jasmine-core.html#.noGlobals)
|
||||||
|
no longer takes a parameter. It always returns the same object when called
|
||||||
|
repeatedly.
|
||||||
|
|
||||||
|
## Supported environments
|
||||||
|
|
||||||
|
This version has been tested in the following environments.
|
||||||
|
|
||||||
|
| Environment | Supported versions |
|
||||||
|
|-------------------|--------------------------------|
|
||||||
|
| Node | 20, 22, 24 |
|
||||||
|
| Safari | 16**, 17** |
|
||||||
|
| Chrome | 140* |
|
||||||
|
| Firefox | 102**, 115**, 128**, 140, 143* |
|
||||||
|
| Edge | 140* |
|
||||||
|
|
||||||
|
\* 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)_
|
||||||
116
release_notes/6.0.0-alpha.1.md
Normal file
116
release_notes/6.0.0-alpha.1.md
Normal file
@@ -0,0 +1,116 @@
|
|||||||
|
# Jasmine Core 6.0.0-alpha.1 Release Notes
|
||||||
|
|
||||||
|
This is a pre-release, intended to offer a preview of breaking changes and to
|
||||||
|
solicit feedback.
|
||||||
|
|
||||||
|
## A Note About Pre-Release Compatibility
|
||||||
|
|
||||||
|
There may be additional breaking changes in future 6.0 pre-releases or in the
|
||||||
|
final 6.0 release. That's allowed by the semver specification, but users are
|
||||||
|
sometimes unpleasantly surprised by it.
|
||||||
|
|
||||||
|
NPM's implementation of carat version ranges assumes that subsequent
|
||||||
|
pre-releases and final releases are fully compatible with earlier pre-releases.
|
||||||
|
If your package.json contains `"jasmine-core": "^6.0.0-alpha.1`,
|
||||||
|
NPM might install any later 6.x version even though there is no guarantee of
|
||||||
|
compatibility. If that isn't ok, you should specify an exact pre-release version:
|
||||||
|
`"jasmine-core": "6.0.0-alpha.1`.
|
||||||
|
|
||||||
|
|
||||||
|
## Breaking changes
|
||||||
|
|
||||||
|
### Changes that affect reporters
|
||||||
|
|
||||||
|
* Irrelevant properties such as `status` and `failedExpectations` are omitted
|
||||||
|
from [the event passed to suiteStarted](https://jasmine.github.io/api/6.0.0-alpha.1/global.html#SuiteStartedEvent).
|
||||||
|
|
||||||
|
This change should be compatible with most existing reporters but could break
|
||||||
|
reporters that manage their internal state in unusual ways. Please
|
||||||
|
[open an issue](https://github.com/jasmine/jasmine/issues/new?template=bug_report.yml)
|
||||||
|
if you find a published reporter package that works with jasmine-core 5.x but
|
||||||
|
not with this release.
|
||||||
|
|
||||||
|
### Changes that affect browser boot files
|
||||||
|
|
||||||
|
* The `createElement` and `createTextNode` options of `HtmlReporter` are ignored.
|
||||||
|
`HtmlReporter` now unconditionally uses `document.createElement` and
|
||||||
|
`document.createTextNode`.
|
||||||
|
|
||||||
|
### Changes that affect spec writing
|
||||||
|
|
||||||
|
* HTML reporters cache configuration throughout each run. Configuration changes
|
||||||
|
made while specs are running will not affect reporter behavior.
|
||||||
|
* Global error spies always receive a single argument. Previously, the browser
|
||||||
|
error event was passed as the second argument.
|
||||||
|
|
||||||
|
## New features
|
||||||
|
|
||||||
|
* A new `HtmlReporterV2` with several improvements over the old `HtmlReporter`:
|
||||||
|
* Clicking a spec/suite link does exact filtering rather than a substring
|
||||||
|
match.
|
||||||
|
* The old dots are replaced with a progress bar. This improves usability with
|
||||||
|
large suites and fixes an accessibility problem.
|
||||||
|
* Details of failed specs are displayed as soon as each spec finishes.
|
||||||
|
* Initialization and wire-up in boot files are much simpler.
|
||||||
|
|
||||||
|
If you're using jasmine-browser-runner or copying boot1.js from the standalone
|
||||||
|
distribution, you'll automatically get the new reporter. If you maintain your
|
||||||
|
own boot files, you'll get the old reporter unless you update your boot1.js
|
||||||
|
to match the one that's in this package.
|
||||||
|
|
||||||
|
The new reporter produces `spec` query string parameters that are different
|
||||||
|
from those created by the old reporter. If you use non-Jasmine software that
|
||||||
|
interprets the `spec` parameter, such as karma-jasmine, you may not be able to
|
||||||
|
adopt `HtmlReporterV2` unlesss it's updated.
|
||||||
|
* Use `globalThis` to determine the global object during initialization
|
||||||
|
This makes jasmine-core more tolerant of buggy bundlers or loaders that
|
||||||
|
cause `this` to be undefined in the global context.
|
||||||
|
|
||||||
|
|
||||||
|
## Deprecations
|
||||||
|
|
||||||
|
* Warn if jasmine-core is loaded as an ES module in a browser.
|
||||||
|
This is an untested and unsupported configuration that has been known to cause
|
||||||
|
problems in the past.
|
||||||
|
* Deprecated `HtmlReporter` and `HtmlSpecFilter` in favor of `HtmlReporterV2`.
|
||||||
|
|
||||||
|
|
||||||
|
## Documentation improvements
|
||||||
|
|
||||||
|
* Improved API reference documentation for APIs that are used from browser boot
|
||||||
|
files.
|
||||||
|
|
||||||
|
|
||||||
|
## Internal improvements
|
||||||
|
|
||||||
|
* Removed remaining code that supported suite re-entry.
|
||||||
|
* Encapsulated suite and spec result and status management.
|
||||||
|
* Adopted strict mode throughout the codebase.
|
||||||
|
* Decomposed `HtmlReporter` into components and converted to ES6 classes.
|
||||||
|
* Made global error handling more uniform between browsers and Node.
|
||||||
|
|
||||||
|
|
||||||
|
## Supported environments
|
||||||
|
|
||||||
|
This version has been tested in the following environments.
|
||||||
|
|
||||||
|
| Environment | Supported versions |
|
||||||
|
|-------------------|--------------------------------|
|
||||||
|
| Node | 20, 22, 24 |
|
||||||
|
| Safari | 16**, 17** |
|
||||||
|
| Chrome | 141* |
|
||||||
|
| Firefox | 102**, 115**, 128**, 140, 143* |
|
||||||
|
| Edge | 141* |
|
||||||
|
|
||||||
|
\* 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)_
|
||||||
90
release_notes/6.0.0-alpha.2.md
Normal file
90
release_notes/6.0.0-alpha.2.md
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
# Jasmine Core 6.0.0-alpha.2 Release Notes
|
||||||
|
|
||||||
|
This is a pre-release, intended to offer a preview of upcoming changes and to
|
||||||
|
solicit feedback.
|
||||||
|
|
||||||
|
## A Note About Pre-Release Compatibility
|
||||||
|
|
||||||
|
There may be additional breaking changes in future 6.0 pre-releases or in the
|
||||||
|
final 6.0 release. That's allowed by the semver specification, but users are
|
||||||
|
sometimes unpleasantly surprised by it.
|
||||||
|
|
||||||
|
NPM's implementation of carat version ranges assumes that subsequent
|
||||||
|
pre-releases and final releases are fully compatible with earlier pre-releases.
|
||||||
|
If your package.json contains `"jasmine-core": "^6.0.0-alpha.2`,
|
||||||
|
NPM might install any later 6.x version even though there is no guarantee of
|
||||||
|
compatibility. If that isn't ok, you should specify an exact pre-release version:
|
||||||
|
`"jasmine-core": "6.0.0-alpha.2`.
|
||||||
|
|
||||||
|
|
||||||
|
## Changes to supported environments
|
||||||
|
|
||||||
|
Safari 26 is now supported on a best-effort basis.†
|
||||||
|
|
||||||
|
Due to the limited availability of Safari 18 and later on free CI services,
|
||||||
|
Safari support in future jasmine-core versions will be limited to:
|
||||||
|
|
||||||
|
* Best-effort support for the latest Safari version available on GitHub Actions,
|
||||||
|
which may change at any time.
|
||||||
|
* Best-effort support for Safari 16 and 17 for as long as it remains practical.
|
||||||
|
|
||||||
|
## Bug Fixes
|
||||||
|
|
||||||
|
* Fix custom matchers in top-level specs††
|
||||||
|
* Merges [#2088](https://github.com/jasmine/jasmine/pull/2088) from @bonkevin
|
||||||
|
|
||||||
|
## New features
|
||||||
|
|
||||||
|
* Larger body font size in HTML reporters
|
||||||
|
* New Performance tab in HtmlReporterV2 shows metrics and a list of the slowest
|
||||||
|
specs.
|
||||||
|
* Experimental `safariYieldStrategy: "time"` config option, which may make
|
||||||
|
Jasmine run significantly faster in Safari and similar browsers. So far, this
|
||||||
|
option has not been tested on a wide variety of workloads. Feedback is
|
||||||
|
appreciated.
|
||||||
|
* New `extraItStackFrames` and `extraDescribeStackFrames` config options to fix
|
||||||
|
the filename properties of reporter events in configurations that wrap
|
||||||
|
`it`/`describe`, such as zone.js.†
|
||||||
|
* `jasmine.allOf asymmetric` equality tester†
|
||||||
|
* Merges [#2087](https://github.com/jasmine/jasmine/pull/2087) from @jonahd-g
|
||||||
|
* Fixes [#2083](https://github.com/jasmine/jasmine/issues/2083)
|
||||||
|
* Re-add support for partial spec name filtering via `spec` query parameter
|
||||||
|
* Fixes [#2085](https://github.com/jasmine/jasmine/issues/2085).
|
||||||
|
* Require spec/suite property keys to be strings, not just anything that's
|
||||||
|
cloneable and serializable. This matches the existing API reference
|
||||||
|
documentation.
|
||||||
|
|
||||||
|
## Documentation improvements
|
||||||
|
|
||||||
|
* Fix HtmlReporterV2 ctor example
|
||||||
|
|
||||||
|
## Internal Improvements
|
||||||
|
|
||||||
|
* Remove code to support browsers that don't have MessageChannel. Jasmine hasn't
|
||||||
|
run in any such browsers since 2.x.
|
||||||
|
|
||||||
|
† Also likely to be included in a future 5.x release.<br>
|
||||||
|
†† Also released in 5.12.1.
|
||||||
|
|
||||||
|
## Supported environments
|
||||||
|
|
||||||
|
This version has been tested in the following environments.
|
||||||
|
|
||||||
|
| Environment | Supported versions |
|
||||||
|
|-------------------|--------------------------------|
|
||||||
|
| Node | 20, 22, 24 |
|
||||||
|
| Safari | 16**, 17**, 26** |
|
||||||
|
| Chrome | 142* |
|
||||||
|
| Firefox | 102**, 115**, 128**, 140, 145* |
|
||||||
|
| Edge | 142* |
|
||||||
|
|
||||||
|
\* 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)_
|
||||||
74
release_notes/6.0.0-beta.0.md
Normal file
74
release_notes/6.0.0-beta.0.md
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
# Jasmine Core 6.0.0-beta.0 Release Notes
|
||||||
|
|
||||||
|
This is a pre-release, intended to offer a preview of upcoming changes and to
|
||||||
|
solicit feedback.
|
||||||
|
|
||||||
|
## A Note About Pre-Release Compatibility
|
||||||
|
|
||||||
|
There may be additional breaking changes in future 6.0 pre-releases or in the
|
||||||
|
final 6.0 release. That's allowed by the semver specification, but users are
|
||||||
|
sometimes unpleasantly surprised by it.
|
||||||
|
|
||||||
|
NPM's implementation of carat version ranges assumes that subsequent
|
||||||
|
pre-releases and final releases are fully compatible with earlier pre-releases.
|
||||||
|
If your package.json contains `"jasmine-core": "^6.0.0-beta.0`,
|
||||||
|
NPM might install any later 6.x version even though there is no guarantee of
|
||||||
|
compatibility. If that isn't ok, you should specify an exact pre-release version:
|
||||||
|
`"jasmine-core": "6.0.0-beta.0`.
|
||||||
|
|
||||||
|
|
||||||
|
## Breaking changes
|
||||||
|
|
||||||
|
* boot1.js no longer adds jsApiReporter to the env.
|
||||||
|
* HtmlReporterV2 initialization and boot1.js have been simplified. If you
|
||||||
|
maintain your own boot file, update it to match the boot1.js included in this
|
||||||
|
package.
|
||||||
|
|
||||||
|
|
||||||
|
## New features
|
||||||
|
|
||||||
|
* Statically exposed pretty printer as jasmine.pp().
|
||||||
|
|
||||||
|
## Bug fixes
|
||||||
|
|
||||||
|
* Fixed HtmlReporterV2 progress bar when running a subset of specs.
|
||||||
|
|
||||||
|
|
||||||
|
## Deprecations
|
||||||
|
|
||||||
|
* jsApiReporter is deprecated.
|
||||||
|
* Detect monkey patching and emit a deprecation warning.
|
||||||
|
|
||||||
|
## Documentation improvements
|
||||||
|
|
||||||
|
* Documented the set of possible spec statuses.
|
||||||
|
|
||||||
|
|
||||||
|
## Internal improvements
|
||||||
|
|
||||||
|
* Replaced isArray helper with native Array.isArray
|
||||||
|
|
||||||
|
|
||||||
|
## Supported environments
|
||||||
|
|
||||||
|
This version has been tested in the following environments.
|
||||||
|
|
||||||
|
| Environment | Supported versions |
|
||||||
|
|-------------------|--------------------------------|
|
||||||
|
| Node | 20, 22, 24 |
|
||||||
|
| Safari | 16**, 17**, 26.1** |
|
||||||
|
| Chrome | 142* |
|
||||||
|
| Firefox | 102**, 115**, 128**, 140, 145* |
|
||||||
|
| Edge | 142* |
|
||||||
|
|
||||||
|
\* 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)_
|
||||||
@@ -1,22 +1,16 @@
|
|||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
|
const os = require('os');
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
const glob = require('glob');
|
const glob = require('glob');
|
||||||
const ejs = require('ejs');
|
const ejs = require('ejs');
|
||||||
const archiver = require('archiver');
|
const archiver = require('archiver');
|
||||||
const { rimrafSync } = require('rimraf');
|
|
||||||
const buildDistribution = require('./lib/buildDistribution');
|
const buildDistribution = require('./lib/buildDistribution');
|
||||||
|
|
||||||
const tmpDir = 'dist/tmp'
|
const prefix = path.join(os.tmpdir(), 'jasmine-build-standalone');
|
||||||
|
const tmpDir = fs.mkdtempSync(prefix);
|
||||||
if (!fs.existsSync(tmpDir)) {
|
|
||||||
if (!fs.existsSync(path.dirname(tmpDir))) {
|
|
||||||
fs.mkdirSync(path.dirname(tmpDir));
|
|
||||||
}
|
|
||||||
fs.mkdirSync(tmpDir);
|
|
||||||
}
|
|
||||||
|
|
||||||
buildStandaloneDist().finally(function() {
|
buildStandaloneDist().finally(function() {
|
||||||
rimrafSync(tmpDir);
|
fs.rmSync(tmpDir, { recursive: true });
|
||||||
});
|
});
|
||||||
|
|
||||||
async function buildStandaloneDist() {
|
async function buildStandaloneDist() {
|
||||||
@@ -30,7 +24,7 @@ function compileSpecRunner(jasmineVersion) {
|
|||||||
const template = fs.readFileSync('src/SpecRunner.html.ejs',
|
const template = fs.readFileSync('src/SpecRunner.html.ejs',
|
||||||
{encoding: 'utf8'});
|
{encoding: 'utf8'});
|
||||||
const runnerHtml = ejs.render(template, { jasmineVersion });
|
const runnerHtml = ejs.render(template, { jasmineVersion });
|
||||||
fs.writeFileSync('dist/tmp/SpecRunner.html', runnerHtml,
|
fs.writeFileSync(path.join(tmpDir, 'SpecRunner.html'), runnerHtml,
|
||||||
{encoding: 'utf8'});
|
{encoding: 'utf8'});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -39,7 +33,7 @@ async function zipStandaloneDist(jasmineVersion) {
|
|||||||
{
|
{
|
||||||
src: [
|
src: [
|
||||||
'LICENSE',
|
'LICENSE',
|
||||||
'dist/tmp/SpecRunner.html',
|
path.join(tmpDir, 'SpecRunner.html'),
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,5 +1,10 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
|
|
||||||
|
# Run tests in supported browsers that are available on Saucelabs.
|
||||||
|
# Note: The latest Safari version is tested via GitHub Actions because Saucelabs
|
||||||
|
# only makes it available to paid enterprise accounts. See
|
||||||
|
# .github/workflows/safari.yml.
|
||||||
|
|
||||||
run_browser() {
|
run_browser() {
|
||||||
browser=$1
|
browser=$1
|
||||||
version=$2
|
version=$2
|
||||||
@@ -25,23 +30,23 @@ run_browser() {
|
|||||||
passfile=`mktemp -t jasmine-results.XXXXXX` || exit 1
|
passfile=`mktemp -t jasmine-results.XXXXXX` || exit 1
|
||||||
failfile=`mktemp -t jasmine-results.XXXXXX` || exit 1
|
failfile=`mktemp -t jasmine-results.XXXXXX` || exit 1
|
||||||
|
|
||||||
# As of 2023-09-30, Sauce Connect doesn't work with the latest Chrome version
|
run_browser chrome latest
|
||||||
# on the default Linux. Run on Mac OS instead. The OS specification may need to
|
|
||||||
# be updated or removed when new Chrome versions stop being available on Mac OS
|
|
||||||
# 12, although historically this has taken several major OS versions.
|
|
||||||
# See <https://saucelabs.com/products/supported-browsers-devices>.
|
|
||||||
# On Saucelabs, the test suite frequently runs ~30s slower on Mac OS than it
|
|
||||||
# does on Linux, so it's probably worth removing the OS specification once Sauce
|
|
||||||
# Connect works with Chrome latest on Linux again.
|
|
||||||
run_browser chrome latest "macOS 12"
|
|
||||||
|
|
||||||
run_browser firefox latest
|
run_browser firefox latest
|
||||||
run_browser firefox 128
|
if [ "$1" = "--not-actually-all" ]; then
|
||||||
run_browser firefox 115
|
echo "SKIPPED: firefox 140" >> "$passfile"
|
||||||
|
echo "SKIPPED: firefox 128" >> "$passfile"
|
||||||
|
echo "SKIPPED: firefox 115" >> "$passfile"
|
||||||
|
else
|
||||||
|
run_browser firefox 140
|
||||||
|
run_browser firefox 128
|
||||||
|
run_browser firefox 115
|
||||||
|
fi
|
||||||
run_browser firefox 102
|
run_browser firefox 102
|
||||||
|
|
||||||
run_browser safari 17
|
run_browser safari 17
|
||||||
run_browser safari 16
|
run_browser safari 16
|
||||||
run_browser safari 15
|
|
||||||
run_browser MicrosoftEdge latest
|
run_browser MicrosoftEdge latest
|
||||||
|
|
||||||
echo
|
echo
|
||||||
@@ -129,33 +129,33 @@ describe('AsyncExpectation', function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('prepends the context to a custom failure message from a function', function() {
|
it('prepends the context to a custom failure message from a matcher', async function() {
|
||||||
pending('should actually work, but no custom matchers for async yet');
|
|
||||||
|
|
||||||
const matchersUtil = {
|
const matchersUtil = {
|
||||||
buildFailureMessage: function() {
|
buildFailureMessage() {
|
||||||
return 'failure message';
|
return 'failure message';
|
||||||
}
|
|
||||||
},
|
},
|
||||||
addExpectationResult = jasmine.createSpy('addExpectationResult'),
|
pp(v) {
|
||||||
actual = Promise.reject(new Error('nope')),
|
return v.toString();
|
||||||
expectation = jasmineUnderTest.Expectation.asyncFactory({
|
}
|
||||||
actual: actual,
|
};
|
||||||
addExpectationResult: addExpectationResult,
|
const addExpectationResult = jasmine.createSpy('addExpectationResult');
|
||||||
matchersUtil: matchersUtil
|
const actual = Promise.reject(new Error('nope'));
|
||||||
});
|
const expectation = jasmineUnderTest.Expectation.asyncFactory({
|
||||||
|
actual: actual,
|
||||||
|
addExpectationResult: addExpectationResult,
|
||||||
|
matchersUtil: matchersUtil
|
||||||
|
});
|
||||||
|
|
||||||
return expectation
|
await expectation.withContext('Some context').toBeResolved();
|
||||||
.withContext('Some context')
|
|
||||||
.toBeResolved()
|
expect(addExpectationResult).toHaveBeenCalledWith(
|
||||||
.then(function() {
|
false,
|
||||||
expect(addExpectationResult).toHaveBeenCalledWith(
|
jasmine.objectContaining({
|
||||||
false,
|
message:
|
||||||
jasmine.objectContaining({
|
'Some context: Expected a promise to be resolved but it ' +
|
||||||
message: 'Some context: msg'
|
'was rejected with Error: nope.'
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('works with #not', function() {
|
it('works with #not', function() {
|
||||||
@@ -277,23 +277,18 @@ describe('AsyncExpectation', function() {
|
|||||||
|
|
||||||
it('reports a passing result to the spec when the comparison passes', function() {
|
it('reports a passing result to the spec when the comparison passes', function() {
|
||||||
const matchers = {
|
const matchers = {
|
||||||
toFoo: function() {
|
toFoo: function() {
|
||||||
return {
|
return {
|
||||||
compare: function() {
|
compare: function() {
|
||||||
return Promise.resolve({ pass: true });
|
return Promise.resolve({ pass: true });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
matchersUtil = {
|
const matchersUtil = {
|
||||||
buildFailureMessage: jasmine.createSpy('buildFailureMessage')
|
buildFailureMessage: jasmine.createSpy('buildFailureMessage')
|
||||||
},
|
};
|
||||||
addExpectationResult = jasmine.createSpy('addExpectationResult'),
|
const addExpectationResult = jasmine.createSpy('addExpectationResult');
|
||||||
errorWithStack = new Error('errorWithStack');
|
|
||||||
|
|
||||||
spyOn(jasmineUnderTest.util, 'errorWithStack').and.returnValue(
|
|
||||||
errorWithStack
|
|
||||||
);
|
|
||||||
|
|
||||||
const expectation = jasmineUnderTest.Expectation.asyncFactory({
|
const expectation = jasmineUnderTest.Expectation.asyncFactory({
|
||||||
customAsyncMatchers: matchers,
|
customAsyncMatchers: matchers,
|
||||||
@@ -310,7 +305,7 @@ describe('AsyncExpectation', function() {
|
|||||||
error: undefined,
|
error: undefined,
|
||||||
expected: 'hello',
|
expected: 'hello',
|
||||||
actual: 'an actual',
|
actual: 'an actual',
|
||||||
errorForStack: errorWithStack
|
errorForStack: jasmine.any(Error)
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -329,13 +324,8 @@ describe('AsyncExpectation', function() {
|
|||||||
buildFailureMessage: function() {
|
buildFailureMessage: function() {
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
addExpectationResult = jasmine.createSpy('addExpectationResult'),
|
const addExpectationResult = jasmine.createSpy('addExpectationResult');
|
||||||
errorWithStack = new Error('errorWithStack');
|
|
||||||
|
|
||||||
spyOn(jasmineUnderTest.util, 'errorWithStack').and.returnValue(
|
|
||||||
errorWithStack
|
|
||||||
);
|
|
||||||
|
|
||||||
const expectation = jasmineUnderTest.Expectation.asyncFactory({
|
const expectation = jasmineUnderTest.Expectation.asyncFactory({
|
||||||
customAsyncMatchers: matchers,
|
customAsyncMatchers: matchers,
|
||||||
@@ -352,30 +342,25 @@ describe('AsyncExpectation', function() {
|
|||||||
actual: 'an actual',
|
actual: 'an actual',
|
||||||
message: '',
|
message: '',
|
||||||
error: undefined,
|
error: undefined,
|
||||||
errorForStack: errorWithStack
|
errorForStack: jasmine.any(Error)
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('reports a failing result and a custom fail message to the spec when the comparison fails', function() {
|
it('reports a failing result and a custom fail message to the spec when the comparison fails', function() {
|
||||||
const matchers = {
|
const matchers = {
|
||||||
toFoo: function() {
|
toFoo: function() {
|
||||||
return {
|
return {
|
||||||
compare: function() {
|
compare: function() {
|
||||||
return Promise.resolve({
|
return Promise.resolve({
|
||||||
pass: false,
|
pass: false,
|
||||||
message: 'I am a custom message'
|
message: 'I am a custom message'
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
addExpectationResult = jasmine.createSpy('addExpectationResult'),
|
const addExpectationResult = jasmine.createSpy('addExpectationResult');
|
||||||
errorWithStack = new Error('errorWithStack');
|
|
||||||
|
|
||||||
spyOn(jasmineUnderTest.util, 'errorWithStack').and.returnValue(
|
|
||||||
errorWithStack
|
|
||||||
);
|
|
||||||
|
|
||||||
const expectation = jasmineUnderTest.Expectation.asyncFactory({
|
const expectation = jasmineUnderTest.Expectation.asyncFactory({
|
||||||
actual: 'an actual',
|
actual: 'an actual',
|
||||||
@@ -391,32 +376,27 @@ describe('AsyncExpectation', function() {
|
|||||||
actual: 'an actual',
|
actual: 'an actual',
|
||||||
message: 'I am a custom message',
|
message: 'I am a custom message',
|
||||||
error: undefined,
|
error: undefined,
|
||||||
errorForStack: errorWithStack
|
errorForStack: jasmine.any(Error)
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('reports a failing result with a custom fail message function to the spec when the comparison fails', function() {
|
it('reports a failing result with a custom fail message function to the spec when the comparison fails', function() {
|
||||||
const matchers = {
|
const matchers = {
|
||||||
toFoo: function() {
|
toFoo: function() {
|
||||||
return {
|
return {
|
||||||
compare: function() {
|
compare: function() {
|
||||||
return Promise.resolve({
|
return Promise.resolve({
|
||||||
pass: false,
|
pass: false,
|
||||||
message: function() {
|
message: function() {
|
||||||
return 'I am a custom message';
|
return 'I am a custom message';
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
addExpectationResult = jasmine.createSpy('addExpectationResult'),
|
const addExpectationResult = jasmine.createSpy('addExpectationResult');
|
||||||
errorWithStack = new Error('errorWithStack');
|
|
||||||
|
|
||||||
spyOn(jasmineUnderTest.util, 'errorWithStack').and.returnValue(
|
|
||||||
errorWithStack
|
|
||||||
);
|
|
||||||
|
|
||||||
const expectation = jasmineUnderTest.Expectation.asyncFactory({
|
const expectation = jasmineUnderTest.Expectation.asyncFactory({
|
||||||
customAsyncMatchers: matchers,
|
customAsyncMatchers: matchers,
|
||||||
@@ -432,28 +412,23 @@ describe('AsyncExpectation', function() {
|
|||||||
actual: 'an actual',
|
actual: 'an actual',
|
||||||
message: 'I am a custom message',
|
message: 'I am a custom message',
|
||||||
error: undefined,
|
error: undefined,
|
||||||
errorForStack: errorWithStack
|
errorForStack: jasmine.any(Error)
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('reports a passing result to the spec when the comparison fails for a negative expectation', function() {
|
it('reports a passing result to the spec when the comparison fails for a negative expectation', function() {
|
||||||
const matchers = {
|
const matchers = {
|
||||||
toFoo: function() {
|
toFoo: function() {
|
||||||
return {
|
return {
|
||||||
compare: function() {
|
compare: function() {
|
||||||
return Promise.resolve({ pass: false });
|
return Promise.resolve({ pass: false });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
addExpectationResult = jasmine.createSpy('addExpectationResult'),
|
const addExpectationResult = jasmine.createSpy('addExpectationResult');
|
||||||
actual = 'an actual',
|
const actual = 'an actual';
|
||||||
errorWithStack = new Error('errorWithStack');
|
|
||||||
|
|
||||||
spyOn(jasmineUnderTest.util, 'errorWithStack').and.returnValue(
|
|
||||||
errorWithStack
|
|
||||||
);
|
|
||||||
|
|
||||||
const expectation = jasmineUnderTest.Expectation.asyncFactory({
|
const expectation = jasmineUnderTest.Expectation.asyncFactory({
|
||||||
customAsyncMatchers: matchers,
|
customAsyncMatchers: matchers,
|
||||||
@@ -469,7 +444,7 @@ describe('AsyncExpectation', function() {
|
|||||||
error: undefined,
|
error: undefined,
|
||||||
expected: 'hello',
|
expected: 'hello',
|
||||||
actual: actual,
|
actual: actual,
|
||||||
errorForStack: errorWithStack
|
errorForStack: jasmine.any(Error)
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -488,14 +463,9 @@ describe('AsyncExpectation', function() {
|
|||||||
buildFailureMessage: function() {
|
buildFailureMessage: function() {
|
||||||
return 'default message';
|
return 'default message';
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
addExpectationResult = jasmine.createSpy('addExpectationResult'),
|
const addExpectationResult = jasmine.createSpy('addExpectationResult');
|
||||||
actual = 'an actual',
|
const actual = 'an actual';
|
||||||
errorWithStack = new Error('errorWithStack');
|
|
||||||
|
|
||||||
spyOn(jasmineUnderTest.util, 'errorWithStack').and.returnValue(
|
|
||||||
errorWithStack
|
|
||||||
);
|
|
||||||
|
|
||||||
const expectation = jasmineUnderTest.Expectation.asyncFactory({
|
const expectation = jasmineUnderTest.Expectation.asyncFactory({
|
||||||
customAsyncMatchers: matchers,
|
customAsyncMatchers: matchers,
|
||||||
@@ -512,31 +482,26 @@ describe('AsyncExpectation', function() {
|
|||||||
actual: actual,
|
actual: actual,
|
||||||
message: 'default message',
|
message: 'default message',
|
||||||
error: undefined,
|
error: undefined,
|
||||||
errorForStack: errorWithStack
|
errorForStack: jasmine.any(Error)
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('reports a failing result and a custom fail message to the spec when the comparison passes for a negative expectation', function() {
|
it('reports a failing result and a custom fail message to the spec when the comparison passes for a negative expectation', function() {
|
||||||
const matchers = {
|
const matchers = {
|
||||||
toFoo: function() {
|
toFoo: function() {
|
||||||
return {
|
return {
|
||||||
compare: function() {
|
compare: function() {
|
||||||
return Promise.resolve({
|
return Promise.resolve({
|
||||||
pass: true,
|
pass: true,
|
||||||
message: 'I am a custom message'
|
message: 'I am a custom message'
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
addExpectationResult = jasmine.createSpy('addExpectationResult'),
|
const addExpectationResult = jasmine.createSpy('addExpectationResult');
|
||||||
actual = 'an actual',
|
const actual = 'an actual';
|
||||||
errorWithStack = new Error('errorWithStack');
|
|
||||||
|
|
||||||
spyOn(jasmineUnderTest.util, 'errorWithStack').and.returnValue(
|
|
||||||
errorWithStack
|
|
||||||
);
|
|
||||||
|
|
||||||
const expectation = jasmineUnderTest.Expectation.asyncFactory({
|
const expectation = jasmineUnderTest.Expectation.asyncFactory({
|
||||||
customAsyncMatchers: matchers,
|
customAsyncMatchers: matchers,
|
||||||
@@ -552,31 +517,26 @@ describe('AsyncExpectation', function() {
|
|||||||
actual: actual,
|
actual: actual,
|
||||||
message: 'I am a custom message',
|
message: 'I am a custom message',
|
||||||
error: undefined,
|
error: undefined,
|
||||||
errorForStack: errorWithStack
|
errorForStack: jasmine.any(Error)
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it("reports a passing result to the spec when the 'not' comparison passes, given a negativeCompare", function() {
|
it("reports a passing result to the spec when the 'not' comparison passes, given a negativeCompare", function() {
|
||||||
const matchers = {
|
const matchers = {
|
||||||
toFoo: function() {
|
toFoo: function() {
|
||||||
return {
|
return {
|
||||||
compare: function() {
|
compare: function() {
|
||||||
return Promise.resolve({ pass: true });
|
return Promise.resolve({ pass: true });
|
||||||
},
|
},
|
||||||
negativeCompare: function() {
|
negativeCompare: function() {
|
||||||
return Promise.resolve({ pass: true });
|
return Promise.resolve({ pass: true });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
addExpectationResult = jasmine.createSpy('addExpectationResult'),
|
const addExpectationResult = jasmine.createSpy('addExpectationResult');
|
||||||
actual = 'an actual',
|
const actual = 'an actual';
|
||||||
errorWithStack = new Error('errorWithStack');
|
|
||||||
|
|
||||||
spyOn(jasmineUnderTest.util, 'errorWithStack').and.returnValue(
|
|
||||||
errorWithStack
|
|
||||||
);
|
|
||||||
|
|
||||||
const expectation = jasmineUnderTest.Expectation.asyncFactory({
|
const expectation = jasmineUnderTest.Expectation.asyncFactory({
|
||||||
customAsyncMatchers: matchers,
|
customAsyncMatchers: matchers,
|
||||||
@@ -592,34 +552,29 @@ describe('AsyncExpectation', function() {
|
|||||||
actual: actual,
|
actual: actual,
|
||||||
message: '',
|
message: '',
|
||||||
error: undefined,
|
error: undefined,
|
||||||
errorForStack: errorWithStack
|
errorForStack: jasmine.any(Error)
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it("reports a failing result and a custom fail message to the spec when the 'not' comparison fails, given a negativeCompare", function() {
|
it("reports a failing result and a custom fail message to the spec when the 'not' comparison fails, given a negativeCompare", function() {
|
||||||
const matchers = {
|
const matchers = {
|
||||||
toFoo: function() {
|
toFoo: function() {
|
||||||
return {
|
return {
|
||||||
compare: function() {
|
compare: function() {
|
||||||
return Promise.resolve({ pass: true });
|
return Promise.resolve({ pass: true });
|
||||||
},
|
},
|
||||||
negativeCompare: function() {
|
negativeCompare: function() {
|
||||||
return Promise.resolve({
|
return Promise.resolve({
|
||||||
pass: false,
|
pass: false,
|
||||||
message: "I'm a custom message"
|
message: "I'm a custom message"
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
addExpectationResult = jasmine.createSpy('addExpectationResult'),
|
const addExpectationResult = jasmine.createSpy('addExpectationResult');
|
||||||
actual = 'an actual',
|
const actual = 'an actual';
|
||||||
errorWithStack = new Error('errorWithStack');
|
|
||||||
|
|
||||||
spyOn(jasmineUnderTest.util, 'errorWithStack').and.returnValue(
|
|
||||||
errorWithStack
|
|
||||||
);
|
|
||||||
|
|
||||||
const expectation = jasmineUnderTest.Expectation.asyncFactory({
|
const expectation = jasmineUnderTest.Expectation.asyncFactory({
|
||||||
customAsyncMatchers: matchers,
|
customAsyncMatchers: matchers,
|
||||||
@@ -635,7 +590,7 @@ describe('AsyncExpectation', function() {
|
|||||||
actual: actual,
|
actual: actual,
|
||||||
message: "I'm a custom message",
|
message: "I'm a custom message",
|
||||||
error: undefined,
|
error: undefined,
|
||||||
errorForStack: errorWithStack
|
errorForStack: jasmine.any(Error)
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -643,24 +598,19 @@ describe('AsyncExpectation', function() {
|
|||||||
it('reports errorWithStack when a custom error message is returned', function() {
|
it('reports errorWithStack when a custom error message is returned', function() {
|
||||||
const customError = new Error('I am a custom error');
|
const customError = new Error('I am a custom error');
|
||||||
const matchers = {
|
const matchers = {
|
||||||
toFoo: function() {
|
toFoo: function() {
|
||||||
return {
|
return {
|
||||||
compare: function() {
|
compare: function() {
|
||||||
return Promise.resolve({
|
return Promise.resolve({
|
||||||
pass: false,
|
pass: false,
|
||||||
message: 'I am a custom message',
|
message: 'I am a custom message',
|
||||||
error: customError
|
error: customError
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
addExpectationResult = jasmine.createSpy('addExpectationResult'),
|
const addExpectationResult = jasmine.createSpy('addExpectationResult');
|
||||||
errorWithStack = new Error('errorWithStack');
|
|
||||||
|
|
||||||
spyOn(jasmineUnderTest.util, 'errorWithStack').and.returnValue(
|
|
||||||
errorWithStack
|
|
||||||
);
|
|
||||||
|
|
||||||
const expectation = jasmineUnderTest.Expectation.asyncFactory({
|
const expectation = jasmineUnderTest.Expectation.asyncFactory({
|
||||||
actual: 'an actual',
|
actual: 'an actual',
|
||||||
@@ -676,30 +626,25 @@ describe('AsyncExpectation', function() {
|
|||||||
actual: 'an actual',
|
actual: 'an actual',
|
||||||
message: 'I am a custom message',
|
message: 'I am a custom message',
|
||||||
error: undefined,
|
error: undefined,
|
||||||
errorForStack: errorWithStack
|
errorForStack: jasmine.any(Error)
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it("reports a custom message to the spec when a 'not' comparison fails", function() {
|
it("reports a custom message to the spec when a 'not' comparison fails", function() {
|
||||||
const matchers = {
|
const matchers = {
|
||||||
toFoo: function() {
|
toFoo: function() {
|
||||||
return {
|
return {
|
||||||
compare: function() {
|
compare: function() {
|
||||||
return Promise.resolve({
|
return Promise.resolve({
|
||||||
pass: true,
|
pass: true,
|
||||||
message: 'I am a custom message'
|
message: 'I am a custom message'
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
addExpectationResult = jasmine.createSpy('addExpectationResult'),
|
const addExpectationResult = jasmine.createSpy('addExpectationResult');
|
||||||
errorWithStack = new Error('errorWithStack');
|
|
||||||
|
|
||||||
spyOn(jasmineUnderTest.util, 'errorWithStack').and.returnValue(
|
|
||||||
errorWithStack
|
|
||||||
);
|
|
||||||
|
|
||||||
const expectation = jasmineUnderTest.Expectation.asyncFactory({
|
const expectation = jasmineUnderTest.Expectation.asyncFactory({
|
||||||
actual: 'an actual',
|
actual: 'an actual',
|
||||||
@@ -715,32 +660,27 @@ describe('AsyncExpectation', function() {
|
|||||||
actual: 'an actual',
|
actual: 'an actual',
|
||||||
message: 'I am a custom message',
|
message: 'I am a custom message',
|
||||||
error: undefined,
|
error: undefined,
|
||||||
errorForStack: errorWithStack
|
errorForStack: jasmine.any(Error)
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it("reports a custom message func to the spec when a 'not' comparison fails", function() {
|
it("reports a custom message func to the spec when a 'not' comparison fails", function() {
|
||||||
const matchers = {
|
const matchers = {
|
||||||
toFoo: function() {
|
toFoo: function() {
|
||||||
return {
|
return {
|
||||||
compare: function() {
|
compare: function() {
|
||||||
return Promise.resolve({
|
return Promise.resolve({
|
||||||
pass: true,
|
pass: true,
|
||||||
message: function() {
|
message: function() {
|
||||||
return 'I am a custom message';
|
return 'I am a custom message';
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
addExpectationResult = jasmine.createSpy('addExpectationResult'),
|
const addExpectationResult = jasmine.createSpy('addExpectationResult');
|
||||||
errorWithStack = new Error('errorWithStack');
|
|
||||||
|
|
||||||
spyOn(jasmineUnderTest.util, 'errorWithStack').and.returnValue(
|
|
||||||
errorWithStack
|
|
||||||
);
|
|
||||||
|
|
||||||
let expectation = jasmineUnderTest.Expectation.asyncFactory({
|
let expectation = jasmineUnderTest.Expectation.asyncFactory({
|
||||||
actual: 'an actual',
|
actual: 'an actual',
|
||||||
@@ -756,7 +696,7 @@ describe('AsyncExpectation', function() {
|
|||||||
actual: 'an actual',
|
actual: 'an actual',
|
||||||
message: 'I am a custom message',
|
message: 'I am a custom message',
|
||||||
error: undefined,
|
error: undefined,
|
||||||
errorForStack: errorWithStack
|
errorForStack: jasmine.any(Error)
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -134,6 +134,42 @@ describe('CallTracker', function() {
|
|||||||
expect(callTracker.mostRecent().args[1]).toEqual(arrayArg);
|
expect(callTracker.mostRecent().args[1]).toEqual(arrayArg);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('allows object arguments to be deep cloned', function() {
|
||||||
|
const callTracker = new jasmineUnderTest.CallTracker();
|
||||||
|
callTracker.saveArgumentsByValue(args => JSON.parse(JSON.stringify(args)));
|
||||||
|
|
||||||
|
const objectArg = { foo: { bar: { baz: ['qux'] } } },
|
||||||
|
arrayArg = ['foo', 'bar'];
|
||||||
|
|
||||||
|
callTracker.track({
|
||||||
|
object: {},
|
||||||
|
args: [objectArg, arrayArg, false, undefined, null, NaN, '', 0, 1.0]
|
||||||
|
});
|
||||||
|
|
||||||
|
objectArg.foo.bar.baz.push('quux');
|
||||||
|
|
||||||
|
expect(callTracker.mostRecent().args[0]).not.toBe(objectArg);
|
||||||
|
expect(callTracker.mostRecent().args[0]).not.toEqual(objectArg);
|
||||||
|
expect(callTracker.mostRecent().args[0]).toEqual({
|
||||||
|
foo: { bar: { baz: ['qux'] } }
|
||||||
|
});
|
||||||
|
expect(callTracker.mostRecent().args[1]).not.toBe(arrayArg);
|
||||||
|
expect(callTracker.mostRecent().args[1]).toEqual(arrayArg);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can take any function to transform arguments when saving by value', function() {
|
||||||
|
const callTracker = new jasmineUnderTest.CallTracker();
|
||||||
|
callTracker.saveArgumentsByValue(JSON.stringify);
|
||||||
|
|
||||||
|
const objectArg = { foo: { bar: { baz: ['qux'] } } },
|
||||||
|
arrayArg = ['foo', 'bar'],
|
||||||
|
args = [objectArg, arrayArg, false, undefined, null, NaN, '', 0, 1.0];
|
||||||
|
|
||||||
|
callTracker.track({ object: {}, args });
|
||||||
|
|
||||||
|
expect(callTracker.mostRecent().args).toEqual(JSON.stringify(args));
|
||||||
|
});
|
||||||
|
|
||||||
it('saves primitive arguments by value', function() {
|
it('saves primitive arguments by value', function() {
|
||||||
const callTracker = new jasmineUnderTest.CallTracker(),
|
const callTracker = new jasmineUnderTest.CallTracker(),
|
||||||
args = [undefined, null, false, '', /\s/, 0, 1.2, NaN];
|
args = [undefined, null, false, '', /\s/, 0, 1.2, NaN];
|
||||||
|
|||||||
@@ -242,7 +242,7 @@ describe('Clock', function() {
|
|||||||
expect(fakeGlobal.clearInterval).toBe(replacedClearInterval);
|
expect(fakeGlobal.clearInterval).toBe(replacedClearInterval);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('replaces the global timer functions on uninstall', function() {
|
it('restores the global timer functions on uninstall', function() {
|
||||||
const fakeSetTimeout = jasmine.createSpy('global setTimeout'),
|
const fakeSetTimeout = jasmine.createSpy('global setTimeout'),
|
||||||
fakeClearTimeout = jasmine.createSpy('global clearTimeout'),
|
fakeClearTimeout = jasmine.createSpy('global clearTimeout'),
|
||||||
fakeSetInterval = jasmine.createSpy('global setInterval'),
|
fakeSetInterval = jasmine.createSpy('global setInterval'),
|
||||||
@@ -408,211 +408,219 @@ describe('Clock', function() {
|
|||||||
expect(delayedFunctionScheduler.scheduleFunction).not.toHaveBeenCalled();
|
expect(delayedFunctionScheduler.scheduleFunction).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('schedules the delayed function (via setTimeout) with the fake timer', function() {
|
describe('setTimeout', function() {
|
||||||
const fakeSetTimeout = jasmine.createSpy('setTimeout'),
|
it('schedules the delayed function with the fake timer', function() {
|
||||||
scheduleFunction = jasmine.createSpy('scheduleFunction'),
|
const fakeSetTimeout = jasmine.createSpy('setTimeout'),
|
||||||
delayedFunctionScheduler = { scheduleFunction: scheduleFunction },
|
scheduleFunction = jasmine.createSpy('scheduleFunction'),
|
||||||
fakeGlobal = { setTimeout: fakeSetTimeout },
|
delayedFunctionScheduler = { scheduleFunction: scheduleFunction },
|
||||||
delayedFn = jasmine.createSpy('delayedFn'),
|
fakeGlobal = { setTimeout: fakeSetTimeout },
|
||||||
mockDate = {
|
delayedFn = jasmine.createSpy('delayedFn'),
|
||||||
install: function() {},
|
mockDate = {
|
||||||
tick: function() {},
|
install: function() {},
|
||||||
uninstall: function() {}
|
tick: function() {},
|
||||||
},
|
uninstall: function() {}
|
||||||
clock = new jasmineUnderTest.Clock(
|
|
||||||
fakeGlobal,
|
|
||||||
function() {
|
|
||||||
return delayedFunctionScheduler;
|
|
||||||
},
|
},
|
||||||
mockDate
|
clock = new jasmineUnderTest.Clock(
|
||||||
),
|
fakeGlobal,
|
||||||
timeout = new clock.FakeTimeout();
|
function() {
|
||||||
|
return delayedFunctionScheduler;
|
||||||
|
},
|
||||||
|
mockDate
|
||||||
|
),
|
||||||
|
timeout = new clock.FakeTimeout();
|
||||||
|
|
||||||
clock.install();
|
clock.install();
|
||||||
clock.setTimeout(delayedFn, 0, 'a', 'b');
|
clock.setTimeout(delayedFn, 0, 'a', 'b');
|
||||||
|
|
||||||
expect(fakeSetTimeout).not.toHaveBeenCalled();
|
expect(fakeSetTimeout).not.toHaveBeenCalled();
|
||||||
|
|
||||||
if (!NODE_JS) {
|
if (!NODE_JS) {
|
||||||
expect(delayedFunctionScheduler.scheduleFunction).toHaveBeenCalledWith(
|
expect(delayedFunctionScheduler.scheduleFunction).toHaveBeenCalledWith(
|
||||||
delayedFn,
|
delayedFn,
|
||||||
0,
|
0,
|
||||||
['a', 'b']
|
['a', 'b']
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
expect(delayedFunctionScheduler.scheduleFunction).toHaveBeenCalledWith(
|
expect(delayedFunctionScheduler.scheduleFunction).toHaveBeenCalledWith(
|
||||||
delayedFn,
|
delayedFn,
|
||||||
0,
|
0,
|
||||||
['a', 'b'],
|
['a', 'b'],
|
||||||
false,
|
false,
|
||||||
timeout
|
timeout
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns an id for the delayed function', function() {
|
||||||
|
const fakeSetTimeout = jasmine.createSpy('setTimeout'),
|
||||||
|
scheduleId = 123,
|
||||||
|
scheduleFunction = jasmine
|
||||||
|
.createSpy('scheduleFunction')
|
||||||
|
.and.returnValue(scheduleId),
|
||||||
|
delayedFunctionScheduler = { scheduleFunction: scheduleFunction },
|
||||||
|
fakeGlobal = { setTimeout: fakeSetTimeout },
|
||||||
|
delayedFn = jasmine.createSpy('delayedFn'),
|
||||||
|
mockDate = {
|
||||||
|
install: function() {},
|
||||||
|
tick: function() {},
|
||||||
|
uninstall: function() {}
|
||||||
|
},
|
||||||
|
clock = new jasmineUnderTest.Clock(
|
||||||
|
fakeGlobal,
|
||||||
|
function() {
|
||||||
|
return delayedFunctionScheduler;
|
||||||
|
},
|
||||||
|
mockDate
|
||||||
|
);
|
||||||
|
|
||||||
|
clock.install();
|
||||||
|
const timeout = clock.setTimeout(delayedFn, 0);
|
||||||
|
|
||||||
|
if (!NODE_JS) {
|
||||||
|
expect(timeout).toEqual(123);
|
||||||
|
} else {
|
||||||
|
expect(timeout.constructor.name).toEqual('FakeTimeout');
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('returns an id for the delayed function', function() {
|
describe('clearTimeout', function() {
|
||||||
const fakeSetTimeout = jasmine.createSpy('setTimeout'),
|
it('clears the scheduled function with the scheduler', function() {
|
||||||
scheduleId = 123,
|
const fakeClearTimeout = jasmine.createSpy('clearTimeout'),
|
||||||
scheduleFunction = jasmine
|
delayedFunctionScheduler = jasmine.createSpyObj(
|
||||||
.createSpy('scheduleFunction')
|
'delayedFunctionScheduler',
|
||||||
.and.returnValue(scheduleId),
|
['removeFunctionWithId']
|
||||||
delayedFunctionScheduler = { scheduleFunction: scheduleFunction },
|
),
|
||||||
fakeGlobal = { setTimeout: fakeSetTimeout },
|
fakeGlobal = { setTimeout: fakeClearTimeout },
|
||||||
delayedFn = jasmine.createSpy('delayedFn'),
|
mockDate = {
|
||||||
mockDate = {
|
install: function() {},
|
||||||
install: function() {},
|
tick: function() {},
|
||||||
tick: function() {},
|
uninstall: function() {}
|
||||||
uninstall: function() {}
|
|
||||||
},
|
|
||||||
clock = new jasmineUnderTest.Clock(
|
|
||||||
fakeGlobal,
|
|
||||||
function() {
|
|
||||||
return delayedFunctionScheduler;
|
|
||||||
},
|
},
|
||||||
mockDate
|
clock = new jasmineUnderTest.Clock(
|
||||||
);
|
fakeGlobal,
|
||||||
|
function() {
|
||||||
|
return delayedFunctionScheduler;
|
||||||
|
},
|
||||||
|
mockDate
|
||||||
|
);
|
||||||
|
|
||||||
clock.install();
|
clock.install();
|
||||||
const timeout = clock.setTimeout(delayedFn, 0);
|
clock.clearTimeout(123);
|
||||||
|
|
||||||
if (!NODE_JS) {
|
expect(fakeClearTimeout).not.toHaveBeenCalled();
|
||||||
expect(timeout).toEqual(123);
|
expect(
|
||||||
} else {
|
delayedFunctionScheduler.removeFunctionWithId
|
||||||
expect(timeout.constructor.name).toEqual('FakeTimeout');
|
).toHaveBeenCalledWith(123);
|
||||||
}
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('clears the scheduled function with the scheduler', function() {
|
describe('setInterval', function() {
|
||||||
const fakeClearTimeout = jasmine.createSpy('clearTimeout'),
|
it('schedules the delayed function with the fake timer', function() {
|
||||||
delayedFunctionScheduler = jasmine.createSpyObj(
|
const fakeSetInterval = jasmine.createSpy('setInterval'),
|
||||||
'delayedFunctionScheduler',
|
scheduleFunction = jasmine.createSpy('scheduleFunction'),
|
||||||
['removeFunctionWithId']
|
delayedFunctionScheduler = { scheduleFunction: scheduleFunction },
|
||||||
),
|
fakeGlobal = { setInterval: fakeSetInterval },
|
||||||
fakeGlobal = { setTimeout: fakeClearTimeout },
|
delayedFn = jasmine.createSpy('delayedFn'),
|
||||||
mockDate = {
|
mockDate = {
|
||||||
install: function() {},
|
install: function() {},
|
||||||
tick: function() {},
|
tick: function() {},
|
||||||
uninstall: function() {}
|
uninstall: function() {}
|
||||||
},
|
|
||||||
clock = new jasmineUnderTest.Clock(
|
|
||||||
fakeGlobal,
|
|
||||||
function() {
|
|
||||||
return delayedFunctionScheduler;
|
|
||||||
},
|
},
|
||||||
mockDate
|
clock = new jasmineUnderTest.Clock(
|
||||||
);
|
fakeGlobal,
|
||||||
|
function() {
|
||||||
|
return delayedFunctionScheduler;
|
||||||
|
},
|
||||||
|
mockDate
|
||||||
|
),
|
||||||
|
timeout = new clock.FakeTimeout();
|
||||||
|
|
||||||
clock.install();
|
clock.install();
|
||||||
clock.clearTimeout(123);
|
clock.setInterval(delayedFn, 0, 'a', 'b');
|
||||||
|
|
||||||
expect(fakeClearTimeout).not.toHaveBeenCalled();
|
expect(fakeSetInterval).not.toHaveBeenCalled();
|
||||||
expect(delayedFunctionScheduler.removeFunctionWithId).toHaveBeenCalledWith(
|
|
||||||
123
|
if (!NODE_JS) {
|
||||||
);
|
expect(delayedFunctionScheduler.scheduleFunction).toHaveBeenCalledWith(
|
||||||
|
delayedFn,
|
||||||
|
0,
|
||||||
|
['a', 'b'],
|
||||||
|
true
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
expect(delayedFunctionScheduler.scheduleFunction).toHaveBeenCalledWith(
|
||||||
|
delayedFn,
|
||||||
|
0,
|
||||||
|
['a', 'b'],
|
||||||
|
true,
|
||||||
|
timeout
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns an id for the delayed function', function() {
|
||||||
|
const fakeSetInterval = jasmine.createSpy('setInterval'),
|
||||||
|
scheduleId = 123,
|
||||||
|
scheduleFunction = jasmine
|
||||||
|
.createSpy('scheduleFunction')
|
||||||
|
.and.returnValue(scheduleId),
|
||||||
|
delayedFunctionScheduler = { scheduleFunction: scheduleFunction },
|
||||||
|
fakeGlobal = { setInterval: fakeSetInterval },
|
||||||
|
delayedFn = jasmine.createSpy('delayedFn'),
|
||||||
|
mockDate = {
|
||||||
|
install: function() {},
|
||||||
|
tick: function() {},
|
||||||
|
uninstall: function() {}
|
||||||
|
},
|
||||||
|
clock = new jasmineUnderTest.Clock(
|
||||||
|
fakeGlobal,
|
||||||
|
function() {
|
||||||
|
return delayedFunctionScheduler;
|
||||||
|
},
|
||||||
|
mockDate
|
||||||
|
);
|
||||||
|
|
||||||
|
clock.install();
|
||||||
|
const interval = clock.setInterval(delayedFn, 0);
|
||||||
|
|
||||||
|
if (!NODE_JS) {
|
||||||
|
expect(interval).toEqual(123);
|
||||||
|
} else {
|
||||||
|
expect(interval.constructor.name).toEqual('FakeTimeout');
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('schedules the delayed function with the fake timer', function() {
|
describe('clearInterval', function() {
|
||||||
const fakeSetInterval = jasmine.createSpy('setInterval'),
|
it('clears the scheduled function with the scheduler', function() {
|
||||||
scheduleFunction = jasmine.createSpy('scheduleFunction'),
|
const clearInterval = jasmine.createSpy('clearInterval'),
|
||||||
delayedFunctionScheduler = { scheduleFunction: scheduleFunction },
|
delayedFunctionScheduler = jasmine.createSpyObj(
|
||||||
fakeGlobal = { setInterval: fakeSetInterval },
|
'delayedFunctionScheduler',
|
||||||
delayedFn = jasmine.createSpy('delayedFn'),
|
['removeFunctionWithId']
|
||||||
mockDate = {
|
),
|
||||||
install: function() {},
|
fakeGlobal = { setInterval: clearInterval },
|
||||||
tick: function() {},
|
mockDate = {
|
||||||
uninstall: function() {}
|
install: function() {},
|
||||||
},
|
tick: function() {},
|
||||||
clock = new jasmineUnderTest.Clock(
|
uninstall: function() {}
|
||||||
fakeGlobal,
|
|
||||||
function() {
|
|
||||||
return delayedFunctionScheduler;
|
|
||||||
},
|
},
|
||||||
mockDate
|
clock = new jasmineUnderTest.Clock(
|
||||||
),
|
fakeGlobal,
|
||||||
timeout = new clock.FakeTimeout();
|
function() {
|
||||||
|
return delayedFunctionScheduler;
|
||||||
|
},
|
||||||
|
mockDate
|
||||||
|
);
|
||||||
|
|
||||||
clock.install();
|
clock.install();
|
||||||
clock.setInterval(delayedFn, 0, 'a', 'b');
|
clock.clearInterval(123);
|
||||||
|
|
||||||
expect(fakeSetInterval).not.toHaveBeenCalled();
|
expect(clearInterval).not.toHaveBeenCalled();
|
||||||
|
expect(
|
||||||
if (!NODE_JS) {
|
delayedFunctionScheduler.removeFunctionWithId
|
||||||
expect(delayedFunctionScheduler.scheduleFunction).toHaveBeenCalledWith(
|
).toHaveBeenCalledWith(123);
|
||||||
delayedFn,
|
});
|
||||||
0,
|
|
||||||
['a', 'b'],
|
|
||||||
true
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
expect(delayedFunctionScheduler.scheduleFunction).toHaveBeenCalledWith(
|
|
||||||
delayedFn,
|
|
||||||
0,
|
|
||||||
['a', 'b'],
|
|
||||||
true,
|
|
||||||
timeout
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
it('returns an id for the delayed function', function() {
|
|
||||||
const fakeSetInterval = jasmine.createSpy('setInterval'),
|
|
||||||
scheduleId = 123,
|
|
||||||
scheduleFunction = jasmine
|
|
||||||
.createSpy('scheduleFunction')
|
|
||||||
.and.returnValue(scheduleId),
|
|
||||||
delayedFunctionScheduler = { scheduleFunction: scheduleFunction },
|
|
||||||
fakeGlobal = { setInterval: fakeSetInterval },
|
|
||||||
delayedFn = jasmine.createSpy('delayedFn'),
|
|
||||||
mockDate = {
|
|
||||||
install: function() {},
|
|
||||||
tick: function() {},
|
|
||||||
uninstall: function() {}
|
|
||||||
},
|
|
||||||
clock = new jasmineUnderTest.Clock(
|
|
||||||
fakeGlobal,
|
|
||||||
function() {
|
|
||||||
return delayedFunctionScheduler;
|
|
||||||
},
|
|
||||||
mockDate
|
|
||||||
);
|
|
||||||
|
|
||||||
clock.install();
|
|
||||||
const interval = clock.setInterval(delayedFn, 0);
|
|
||||||
|
|
||||||
if (!NODE_JS) {
|
|
||||||
expect(interval).toEqual(123);
|
|
||||||
} else {
|
|
||||||
expect(interval.constructor.name).toEqual('FakeTimeout');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
it('clears the scheduled function with the scheduler', function() {
|
|
||||||
const clearInterval = jasmine.createSpy('clearInterval'),
|
|
||||||
delayedFunctionScheduler = jasmine.createSpyObj(
|
|
||||||
'delayedFunctionScheduler',
|
|
||||||
['removeFunctionWithId']
|
|
||||||
),
|
|
||||||
fakeGlobal = { setInterval: clearInterval },
|
|
||||||
mockDate = {
|
|
||||||
install: function() {},
|
|
||||||
tick: function() {},
|
|
||||||
uninstall: function() {}
|
|
||||||
},
|
|
||||||
clock = new jasmineUnderTest.Clock(
|
|
||||||
fakeGlobal,
|
|
||||||
function() {
|
|
||||||
return delayedFunctionScheduler;
|
|
||||||
},
|
|
||||||
mockDate
|
|
||||||
);
|
|
||||||
|
|
||||||
clock.install();
|
|
||||||
clock.clearInterval(123);
|
|
||||||
|
|
||||||
expect(clearInterval).not.toHaveBeenCalled();
|
|
||||||
expect(delayedFunctionScheduler.removeFunctionWithId).toHaveBeenCalledWith(
|
|
||||||
123
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('gives you a friendly reminder if the Clock is not installed and you tick', function() {
|
it('gives you a friendly reminder if the Clock is not installed and you tick', function() {
|
||||||
@@ -699,22 +707,39 @@ describe('Clock (acceptance)', function() {
|
|||||||
tick: function() {},
|
tick: function() {},
|
||||||
uninstall: function() {}
|
uninstall: function() {}
|
||||||
};
|
};
|
||||||
|
// window setTimeout to window to make firefox happy
|
||||||
|
const _setTimeout =
|
||||||
|
typeof window !== 'undefined' ? setTimeout.bind(window) : setTimeout;
|
||||||
|
// passing a fake global allows us to preserve the real timing functions for use in tests
|
||||||
|
const _global = { setTimeout: _setTimeout, setInterval: setInterval };
|
||||||
clock = new jasmineUnderTest.Clock(
|
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".
|
_global,
|
||||||
typeof window !== 'undefined' ? window : { setTimeout: setTimeout },
|
|
||||||
function() {
|
function() {
|
||||||
return delayedFunctionScheduler;
|
return delayedFunctionScheduler;
|
||||||
},
|
},
|
||||||
mockDate
|
mockDate
|
||||||
);
|
);
|
||||||
clock.install();
|
clock.install().autoTick();
|
||||||
clock.autoTick();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
clock.uninstall();
|
clock.uninstall();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('flushes microtask queue between macrotasks', async () => {
|
||||||
|
const log = [];
|
||||||
|
await new Promise(r => clock.setTimeout(r, 10)).then(() => {
|
||||||
|
log.push(1);
|
||||||
|
Promise.resolve().then(() => log.push(2));
|
||||||
|
Promise.resolve().then(() => log.push(3));
|
||||||
|
});
|
||||||
|
await new Promise(r => clock.setTimeout(r, 10)).then(() => {
|
||||||
|
log.push(4);
|
||||||
|
Promise.resolve().then(() => log.push(5));
|
||||||
|
});
|
||||||
|
expect(log).toEqual([1, 2, 3, 4, 5]);
|
||||||
|
});
|
||||||
|
|
||||||
it('can run setTimeouts/setIntervals asynchronously', function() {
|
it('can run setTimeouts/setIntervals asynchronously', function() {
|
||||||
const recurring = jasmine.createSpy('recurring'),
|
const recurring = jasmine.createSpy('recurring'),
|
||||||
fn1 = jasmine.createSpy('fn1'),
|
fn1 = jasmine.createSpy('fn1'),
|
||||||
@@ -776,6 +801,26 @@ describe('Clock (acceptance)', function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('aborts auto ticking when uninstalled, even if installed again synchonrously', async () => {
|
||||||
|
clock.uninstall();
|
||||||
|
clock.install();
|
||||||
|
|
||||||
|
let resolved = false;
|
||||||
|
const promise = new Promise(resolve => {
|
||||||
|
clock.setTimeout(resolve, 1);
|
||||||
|
}).then(() => {
|
||||||
|
resolved = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
// wait some real time and verify that the clock did not flush the timer above automatically
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 2));
|
||||||
|
expect(resolved).toBe(false);
|
||||||
|
|
||||||
|
// enabling auto tick again will flush the timer
|
||||||
|
clock.autoTick();
|
||||||
|
await expectAsync(promise).toBeResolved();
|
||||||
|
});
|
||||||
|
|
||||||
it('speeds up the execution of the timers in all browsers', async () => {
|
it('speeds up the execution of the timers in all browsers', async () => {
|
||||||
const startTimeMs = performance.now() / 1000;
|
const startTimeMs = performance.now() / 1000;
|
||||||
await new Promise(resolve => clock.setTimeout(resolve, 5000));
|
await new Promise(resolve => clock.setTimeout(resolve, 5000));
|
||||||
|
|||||||
158
spec/core/ConfigurationSpec.js
Normal file
158
spec/core/ConfigurationSpec.js
Normal file
@@ -0,0 +1,158 @@
|
|||||||
|
describe('Configuration', function() {
|
||||||
|
const standardBooleanKeys = [
|
||||||
|
'random',
|
||||||
|
'stopOnSpecFailure',
|
||||||
|
'stopSpecOnExpectationFailure',
|
||||||
|
'failSpecWithNoExpectations',
|
||||||
|
'hideDisabled',
|
||||||
|
'autoCleanClosures',
|
||||||
|
'forbidDuplicateNames',
|
||||||
|
'detectLateRejectionHandling'
|
||||||
|
];
|
||||||
|
const allKeys = [
|
||||||
|
...standardBooleanKeys,
|
||||||
|
'seed',
|
||||||
|
'specFilter',
|
||||||
|
'verboseDeprecations',
|
||||||
|
'extraItStackFrames',
|
||||||
|
'extraDescribeStackFrames'
|
||||||
|
];
|
||||||
|
Object.freeze(standardBooleanKeys);
|
||||||
|
Object.freeze(allKeys);
|
||||||
|
|
||||||
|
it('provides defaults', function() {
|
||||||
|
const subject = new jasmineUnderTest.Configuration();
|
||||||
|
expect(subject.random).toEqual(true);
|
||||||
|
expect(subject.seed).toBeNull();
|
||||||
|
expect(subject.stopOnSpecFailure).toEqual(false);
|
||||||
|
expect(subject.stopSpecOnExpectationFailure).toEqual(false);
|
||||||
|
expect(subject.failSpecWithNoExpectations).toEqual(false);
|
||||||
|
expect(subject.specFilter).toEqual(jasmine.any(Function));
|
||||||
|
expect(subject.specFilter()).toEqual(true);
|
||||||
|
expect(subject.hideDisabled).toEqual(false);
|
||||||
|
expect(subject.autoCleanClosures).toEqual(true);
|
||||||
|
expect(subject.forbidDuplicateNames).toEqual(false);
|
||||||
|
expect(subject.verboseDeprecations).toEqual(false);
|
||||||
|
expect(subject.detectLateRejectionHandling).toEqual(false);
|
||||||
|
expect(subject.extraItStackFrames).toEqual(0);
|
||||||
|
expect(subject.extraDescribeStackFrames).toEqual(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('copy()', function() {
|
||||||
|
it('returns a copy of the configuration as a plain old JS object', function() {
|
||||||
|
const subject = new jasmineUnderTest.Configuration();
|
||||||
|
|
||||||
|
const copy = subject.copy();
|
||||||
|
|
||||||
|
expect(copy.constructor.name).toEqual('Object');
|
||||||
|
|
||||||
|
expect(new Set(Object.keys(copy))).toEqual(new Set(allKeys));
|
||||||
|
for (const k of allKeys) {
|
||||||
|
expect(copy[k]).toEqual(subject[k]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('update()', function() {
|
||||||
|
it('does not update properties that are absent from the parameter', function() {
|
||||||
|
const subject = new jasmineUnderTest.Configuration();
|
||||||
|
const originalValues = subject.copy();
|
||||||
|
|
||||||
|
subject.update({});
|
||||||
|
expect(subject.copy()).toEqual(originalValues);
|
||||||
|
});
|
||||||
|
|
||||||
|
function booleanPropertyBehavior(key) {
|
||||||
|
it('does not update the property if the specified value is undefined', function() {
|
||||||
|
const subject = new jasmineUnderTest.Configuration();
|
||||||
|
const orig = subject[key];
|
||||||
|
|
||||||
|
subject.update({ [key]: undefined });
|
||||||
|
|
||||||
|
expect(subject[key]).toEqual(orig);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('updates the property if the specified value is not undefined', function() {
|
||||||
|
const subject = new jasmineUnderTest.Configuration();
|
||||||
|
const orig = subject[key];
|
||||||
|
|
||||||
|
subject.update({ [key]: !orig });
|
||||||
|
expect(subject[key]).toEqual(!orig);
|
||||||
|
|
||||||
|
subject.update({ [key]: orig });
|
||||||
|
expect(subject[key]).toEqual(orig);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const k of standardBooleanKeys) {
|
||||||
|
describe(k, function() {
|
||||||
|
booleanPropertyBehavior(k);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: in the next major release, treat verboseDeprecations like other booleans
|
||||||
|
it('sets verboseDeprecations when present', function() {
|
||||||
|
const subject = new jasmineUnderTest.Configuration();
|
||||||
|
const orig = subject.verboseDeprecations;
|
||||||
|
|
||||||
|
subject.update({ verboseDeprecations: !orig });
|
||||||
|
expect(subject.verboseDeprecations).toEqual(!orig);
|
||||||
|
|
||||||
|
subject.update({ verboseDeprecations: orig });
|
||||||
|
expect(subject.verboseDeprecations).toEqual(orig);
|
||||||
|
|
||||||
|
// For backwards compatibility, explicitly setting to undefined should
|
||||||
|
// work. Undefined isn't officially valid but gets treated like false.
|
||||||
|
subject.update({ verboseDeprecations: undefined });
|
||||||
|
expect(subject.verboseDeprecations).toBeUndefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('sets specFilter when truthy', function() {
|
||||||
|
const subject = new jasmineUnderTest.Configuration();
|
||||||
|
const orig = subject.specFilter;
|
||||||
|
|
||||||
|
subject.update({ specFilter: undefined });
|
||||||
|
expect(subject.specFilter).toBe(orig);
|
||||||
|
|
||||||
|
subject.update({ specFilter: false });
|
||||||
|
expect(subject.specFilter).toBe(orig);
|
||||||
|
|
||||||
|
function newSpecFilter() {}
|
||||||
|
subject.update({ specFilter: newSpecFilter });
|
||||||
|
expect(subject.specFilter).toBe(newSpecFilter);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('sets seed when not undefined', function() {
|
||||||
|
const subject = new jasmineUnderTest.Configuration();
|
||||||
|
|
||||||
|
subject.update({ seed: undefined });
|
||||||
|
expect(subject.seed).toBeNull();
|
||||||
|
|
||||||
|
subject.update({ seed: 1234 });
|
||||||
|
expect(subject.seed).toEqual(1234);
|
||||||
|
|
||||||
|
subject.update({ seed: null });
|
||||||
|
expect(subject.seed).toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('sets extraItStackFrames when not undefined', function() {
|
||||||
|
const subject = new jasmineUnderTest.Configuration();
|
||||||
|
|
||||||
|
subject.update({ extraItStackFrames: undefined });
|
||||||
|
expect(subject.extraItStackFrames).toEqual(0);
|
||||||
|
|
||||||
|
subject.update({ extraItStackFrames: 100000 });
|
||||||
|
expect(subject.extraItStackFrames).toEqual(100000);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('sets extraDescribeStackFrames when not undefined', function() {
|
||||||
|
const subject = new jasmineUnderTest.Configuration();
|
||||||
|
|
||||||
|
subject.update({ extraDescribeStackFrames: undefined });
|
||||||
|
expect(subject.extraDescribeStackFrames).toEqual(0);
|
||||||
|
|
||||||
|
subject.update({ extraDescribeStackFrames: 100000 });
|
||||||
|
expect(subject.extraDescribeStackFrames).toEqual(100000);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -86,12 +86,13 @@ describe('DelayedFunctionScheduler', function() {
|
|||||||
it('increments scheduled fns ids unless one is passed', function() {
|
it('increments scheduled fns ids unless one is passed', function() {
|
||||||
const scheduler = new jasmineUnderTest.DelayedFunctionScheduler();
|
const scheduler = new jasmineUnderTest.DelayedFunctionScheduler();
|
||||||
|
|
||||||
expect(scheduler.scheduleFunction(function() {}, 0)).toBe(1);
|
const initial = scheduler.scheduleFunction(function() {}, 0);
|
||||||
expect(scheduler.scheduleFunction(function() {}, 0)).toBe(2);
|
expect(scheduler.scheduleFunction(function() {}, 0)).toBe(initial + 1);
|
||||||
|
expect(scheduler.scheduleFunction(function() {}, 0)).toBe(initial + 2);
|
||||||
expect(scheduler.scheduleFunction(function() {}, 0, [], false, 123)).toBe(
|
expect(scheduler.scheduleFunction(function() {}, 0, [], false, 123)).toBe(
|
||||||
123
|
123
|
||||||
);
|
);
|
||||||
expect(scheduler.scheduleFunction(function() {}, 0)).toBe(3);
|
expect(scheduler.scheduleFunction(function() {}, 0)).toBe(initial + 3);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('#removeFunctionWithId removes a previously scheduled function with a given id', function() {
|
it('#removeFunctionWithId removes a previously scheduled function with a given id', function() {
|
||||||
@@ -313,6 +314,28 @@ describe('DelayedFunctionScheduler', function() {
|
|||||||
expect(tickDate).toHaveBeenCalledWith(1);
|
expect(tickDate).toHaveBeenCalledWith(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('does not conflict with native timer IDs', function() {
|
||||||
|
const NODE_JS =
|
||||||
|
typeof process !== 'undefined' &&
|
||||||
|
process.versions &&
|
||||||
|
typeof process.versions.node === 'string';
|
||||||
|
if (NODE_JS) {
|
||||||
|
pending('numeric timer ID conflicts only relevant for browsers.');
|
||||||
|
}
|
||||||
|
const nativeTimeoutId = setTimeout(function() {}, 100);
|
||||||
|
|
||||||
|
const scheduler = new jasmineUnderTest.DelayedFunctionScheduler();
|
||||||
|
const fn = jasmine.createSpy('fn');
|
||||||
|
|
||||||
|
for (let i = 0; i < nativeTimeoutId; i++) {
|
||||||
|
scheduler.scheduleFunction(fn, 0, [], false);
|
||||||
|
}
|
||||||
|
scheduler.removeFunctionWithId(nativeTimeoutId);
|
||||||
|
scheduler.tick(1);
|
||||||
|
|
||||||
|
expect(fn).toHaveBeenCalledTimes(nativeTimeoutId);
|
||||||
|
});
|
||||||
|
|
||||||
describe('ticking inside a scheduled function', function() {
|
describe('ticking inside a scheduled function', function() {
|
||||||
let clock;
|
let clock;
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
// TODO: Fix these unit tests!
|
|
||||||
describe('Env', function() {
|
describe('Env', function() {
|
||||||
let env;
|
let env;
|
||||||
beforeEach(function() {
|
beforeEach(function() {
|
||||||
@@ -95,7 +94,7 @@ describe('Env', function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('accepts its own current configureation', function() {
|
it('accepts its own current configuration', function() {
|
||||||
env.configure(env.configuration());
|
env.configure(env.configuration());
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -198,6 +197,29 @@ describe('Env', function() {
|
|||||||
expect(innerSuite.parentSuite).toBe(suite);
|
expect(innerSuite.parentSuite).toBe(suite);
|
||||||
expect(spec.getFullName()).toEqual('outer suite inner suite a spec');
|
expect(spec.getFullName()).toEqual('outer suite inner suite a spec');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('sets the caller filename correctly when extraDescribeStackFrames is not set', function() {
|
||||||
|
// IIFE is used to match the stack depth when global describe() is called
|
||||||
|
const suite = (function() {
|
||||||
|
return env[methodName]('a suite', function() {
|
||||||
|
env.it('a spec');
|
||||||
|
});
|
||||||
|
})();
|
||||||
|
expect(suite.filename).toMatch(/EnvSpec\.js$/);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('sets the caller filename correctly when extraDescribeStackFrames is set', function() {
|
||||||
|
env.configure({ extraDescribeStackFrames: 2 });
|
||||||
|
// IIFE is used to match the stack depth when global describe() is called
|
||||||
|
const suite = (function() {
|
||||||
|
return specHelpers.callerFilenameShim(function() {
|
||||||
|
return env[methodName]('a suite', function() {
|
||||||
|
env.it('a spec');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
})();
|
||||||
|
expect(suite.filename).toMatch(/EnvSpec\.js$/);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
describe('#describe', function() {
|
describe('#describe', function() {
|
||||||
@@ -300,6 +322,25 @@ describe('Env', function() {
|
|||||||
.not.toEqual('');
|
.not.toEqual('');
|
||||||
expect(spec.pend).toBeFalsy();
|
expect(spec.pend).toBeFalsy();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('sets the caller filename correctly when extraItStackFrames is not set', function() {
|
||||||
|
// IIFE is used to match the stack depth when global it() is called
|
||||||
|
const spec = (function() {
|
||||||
|
return env[methodName]('a spec', function() {});
|
||||||
|
})();
|
||||||
|
expect(spec.filename).toMatch(/EnvSpec\.js$/);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('sets the caller filename correctly when extraItStackFrames is set', function() {
|
||||||
|
env.configure({ extraItStackFrames: 2 });
|
||||||
|
// IIFE is used to match the stack depth when global it() is called
|
||||||
|
const spec = (function() {
|
||||||
|
return specHelpers.callerFilenameShim(function() {
|
||||||
|
return env[methodName]('a spec', function() {});
|
||||||
|
});
|
||||||
|
})();
|
||||||
|
expect(spec.filename).toMatch(/EnvSpec\.js$/);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
describe('#it', function() {
|
describe('#it', function() {
|
||||||
@@ -625,9 +666,12 @@ describe('Env', function() {
|
|||||||
'popListener',
|
'popListener',
|
||||||
'removeOverrideListener'
|
'removeOverrideListener'
|
||||||
]);
|
]);
|
||||||
spyOn(jasmineUnderTest, 'GlobalErrors').and.returnValue(globalErrors);
|
|
||||||
env.cleanup_();
|
env.cleanup_();
|
||||||
env = new jasmineUnderTest.Env();
|
env = new jasmineUnderTest.Env({
|
||||||
|
GlobalErrors: function() {
|
||||||
|
return globalErrors;
|
||||||
|
}
|
||||||
|
});
|
||||||
expect(globalErrors.install).toHaveBeenCalled();
|
expect(globalErrors.install).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -641,9 +685,13 @@ describe('Env', function() {
|
|||||||
'popListener',
|
'popListener',
|
||||||
'removeOverrideListener'
|
'removeOverrideListener'
|
||||||
]);
|
]);
|
||||||
spyOn(jasmineUnderTest, 'GlobalErrors').and.returnValue(globalErrors);
|
|
||||||
env.cleanup_();
|
env.cleanup_();
|
||||||
env = new jasmineUnderTest.Env({ suppressLoadErrors: true });
|
env = new jasmineUnderTest.Env({
|
||||||
|
suppressLoadErrors: true,
|
||||||
|
GlobalErrors: function() {
|
||||||
|
return globalErrors;
|
||||||
|
}
|
||||||
|
});
|
||||||
expect(globalErrors.install).not.toHaveBeenCalled();
|
expect(globalErrors.install).not.toHaveBeenCalled();
|
||||||
env.execute();
|
env.execute();
|
||||||
expect(globalErrors.install).toHaveBeenCalled();
|
expect(globalErrors.install).toHaveBeenCalled();
|
||||||
|
|||||||
@@ -1,14 +1,17 @@
|
|||||||
describe('GlobalErrors', function() {
|
describe('GlobalErrors', function() {
|
||||||
it('calls the added handler on error', function() {
|
it('calls the added handler on error', function() {
|
||||||
const fakeGlobal = browserGlobal();
|
const globals = browserGlobals();
|
||||||
const handler = jasmine.createSpy('errorHandler');
|
const handler = jasmine.createSpy('errorHandler');
|
||||||
const errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
|
const errors = new jasmineUnderTest.GlobalErrors(
|
||||||
|
globals.global,
|
||||||
|
() => ({})
|
||||||
|
);
|
||||||
|
|
||||||
errors.install();
|
errors.install();
|
||||||
errors.pushListener(handler);
|
errors.pushListener(handler);
|
||||||
|
|
||||||
const error = new Error('nope');
|
const error = new Error('nope');
|
||||||
dispatchErrorEvent(fakeGlobal, { error });
|
dispatchEvent(globals.listeners, 'error', { error });
|
||||||
|
|
||||||
expect(handler).toHaveBeenCalledWith(
|
expect(handler).toHaveBeenCalledWith(
|
||||||
jasmine.is(error),
|
jasmine.is(error),
|
||||||
@@ -17,17 +20,20 @@ describe('GlobalErrors', function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('is not affected by overriding global.onerror', function() {
|
it('is not affected by overriding global.onerror', function() {
|
||||||
const fakeGlobal = browserGlobal();
|
const globals = browserGlobals();
|
||||||
const handler = jasmine.createSpy('errorHandler');
|
const handler = jasmine.createSpy('errorHandler');
|
||||||
const errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
|
const errors = new jasmineUnderTest.GlobalErrors(
|
||||||
|
globals.global,
|
||||||
|
() => ({})
|
||||||
|
);
|
||||||
|
|
||||||
errors.install();
|
errors.install();
|
||||||
errors.pushListener(handler);
|
errors.pushListener(handler);
|
||||||
|
|
||||||
fakeGlobal.onerror = () => {};
|
globals.global.onerror = () => {};
|
||||||
|
|
||||||
const error = new Error('nope');
|
const error = new Error('nope');
|
||||||
dispatchErrorEvent(fakeGlobal, { error });
|
dispatchEvent(globals.listeners, 'error', { error });
|
||||||
|
|
||||||
expect(handler).toHaveBeenCalledWith(
|
expect(handler).toHaveBeenCalledWith(
|
||||||
jasmine.is(error),
|
jasmine.is(error),
|
||||||
@@ -36,17 +42,20 @@ describe('GlobalErrors', function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('only calls the most recent handler', function() {
|
it('only calls the most recent handler', function() {
|
||||||
const fakeGlobal = browserGlobal();
|
const globals = browserGlobals();
|
||||||
const handler1 = jasmine.createSpy('errorHandler1');
|
const handler1 = jasmine.createSpy('errorHandler1');
|
||||||
const handler2 = jasmine.createSpy('errorHandler2');
|
const handler2 = jasmine.createSpy('errorHandler2');
|
||||||
const errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
|
const errors = new jasmineUnderTest.GlobalErrors(
|
||||||
|
globals.global,
|
||||||
|
() => ({})
|
||||||
|
);
|
||||||
|
|
||||||
errors.install();
|
errors.install();
|
||||||
errors.pushListener(handler1);
|
errors.pushListener(handler1);
|
||||||
errors.pushListener(handler2);
|
errors.pushListener(handler2);
|
||||||
|
|
||||||
const error = new Error('nope');
|
const error = new Error('nope');
|
||||||
dispatchErrorEvent(fakeGlobal, { error });
|
dispatchEvent(globals.listeners, 'error', { error });
|
||||||
|
|
||||||
expect(handler1).not.toHaveBeenCalled();
|
expect(handler1).not.toHaveBeenCalled();
|
||||||
expect(handler2).toHaveBeenCalledWith(
|
expect(handler2).toHaveBeenCalledWith(
|
||||||
@@ -56,10 +65,13 @@ describe('GlobalErrors', function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('calls previous handlers when one is removed', function() {
|
it('calls previous handlers when one is removed', function() {
|
||||||
const fakeGlobal = browserGlobal();
|
const globals = browserGlobals();
|
||||||
const handler1 = jasmine.createSpy('errorHandler1');
|
const handler1 = jasmine.createSpy('errorHandler1');
|
||||||
const handler2 = jasmine.createSpy('errorHandler2');
|
const handler2 = jasmine.createSpy('errorHandler2');
|
||||||
const errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
|
const errors = new jasmineUnderTest.GlobalErrors(
|
||||||
|
globals.global,
|
||||||
|
() => ({})
|
||||||
|
);
|
||||||
|
|
||||||
errors.install();
|
errors.install();
|
||||||
errors.pushListener(handler1);
|
errors.pushListener(handler1);
|
||||||
@@ -68,7 +80,7 @@ describe('GlobalErrors', function() {
|
|||||||
errors.popListener(handler2);
|
errors.popListener(handler2);
|
||||||
|
|
||||||
const error = new Error('nope');
|
const error = new Error('nope');
|
||||||
dispatchErrorEvent(fakeGlobal, { error });
|
dispatchEvent(globals.listeners, 'error', { error });
|
||||||
|
|
||||||
expect(handler1).toHaveBeenCalledWith(
|
expect(handler1).toHaveBeenCalledWith(
|
||||||
jasmine.is(error),
|
jasmine.is(error),
|
||||||
@@ -85,26 +97,32 @@ describe('GlobalErrors', function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('uninstalls itself', function() {
|
it('uninstalls itself', function() {
|
||||||
const fakeGlobal = browserGlobal();
|
const globals = browserGlobals();
|
||||||
const errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
|
const errors = new jasmineUnderTest.GlobalErrors(
|
||||||
|
globals.global,
|
||||||
|
() => ({})
|
||||||
|
);
|
||||||
function unrelatedListener() {}
|
function unrelatedListener() {}
|
||||||
|
|
||||||
errors.install();
|
errors.install();
|
||||||
fakeGlobal.addEventListener('error', unrelatedListener);
|
globals.global.addEventListener('error', unrelatedListener);
|
||||||
errors.uninstall();
|
errors.uninstall();
|
||||||
|
|
||||||
expect(fakeGlobal.listeners_.error).toEqual([unrelatedListener]);
|
expect(globals.listeners.error).toEqual([unrelatedListener]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('rethrows the original error when there is no handler', function() {
|
it('rethrows the original error when there is no handler', function() {
|
||||||
const fakeGlobal = browserGlobal();
|
const globals = browserGlobals();
|
||||||
const errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
|
const errors = new jasmineUnderTest.GlobalErrors(
|
||||||
|
globals.global,
|
||||||
|
() => ({})
|
||||||
|
);
|
||||||
const originalError = new Error('nope');
|
const originalError = new Error('nope');
|
||||||
|
|
||||||
errors.install();
|
errors.install();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
dispatchErrorEvent(fakeGlobal, { error: originalError });
|
dispatchEvent(globals.listeners, 'error', { error: originalError });
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
expect(e).toBe(originalError);
|
expect(e).toBe(originalError);
|
||||||
}
|
}
|
||||||
@@ -113,191 +131,281 @@ describe('GlobalErrors', function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('reports uncaught exceptions in node.js', function() {
|
it('reports uncaught exceptions in node.js', function() {
|
||||||
const fakeGlobal = {
|
const globals = nodeGlobals();
|
||||||
process: {
|
const errors = new jasmineUnderTest.GlobalErrors(
|
||||||
on: jasmine.createSpy('process.on'),
|
globals.global,
|
||||||
removeListener: jasmine.createSpy('process.removeListener'),
|
() => ({})
|
||||||
listeners: jasmine
|
);
|
||||||
.createSpy('process.listeners')
|
const handler = jasmine.createSpy('errorHandler');
|
||||||
.and.returnValue(['foo']),
|
function originalHandler() {}
|
||||||
removeAllListeners: jasmine.createSpy('process.removeAllListeners')
|
globals.listeners.uncaughtException = [originalHandler];
|
||||||
}
|
|
||||||
},
|
|
||||||
handler = jasmine.createSpy('errorHandler'),
|
|
||||||
errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
|
|
||||||
|
|
||||||
errors.install();
|
errors.install();
|
||||||
expect(fakeGlobal.process.on).toHaveBeenCalledWith(
|
expect(globals.listeners.uncaughtException).toEqual([
|
||||||
'uncaughtException',
|
|
||||||
jasmine.any(Function)
|
jasmine.any(Function)
|
||||||
);
|
]);
|
||||||
expect(fakeGlobal.process.listeners).toHaveBeenCalledWith(
|
expect(globals.listeners.uncaughtException).not.toEqual([
|
||||||
'uncaughtException'
|
originalHandler()
|
||||||
);
|
]);
|
||||||
expect(fakeGlobal.process.removeAllListeners).toHaveBeenCalledWith(
|
|
||||||
'uncaughtException'
|
|
||||||
);
|
|
||||||
|
|
||||||
errors.pushListener(handler);
|
errors.pushListener(handler);
|
||||||
|
|
||||||
const addedListener = fakeGlobal.process.on.calls.argsFor(0)[1];
|
dispatchEvent(globals.listeners, 'uncaughtException', new Error('bar'));
|
||||||
addedListener(new Error('bar'));
|
|
||||||
|
|
||||||
expect(handler).toHaveBeenCalledWith(new Error('bar'));
|
expect(handler).toHaveBeenCalledWith(new Error('bar'), undefined);
|
||||||
expect(handler.calls.argsFor(0)[0].jasmineMessage).toBe(
|
expect(handler.calls.argsFor(0)[0].jasmineMessage).toBe(
|
||||||
'Uncaught exception: Error: bar'
|
'Uncaught exception: Error: bar'
|
||||||
);
|
);
|
||||||
|
|
||||||
errors.uninstall();
|
errors.uninstall();
|
||||||
|
|
||||||
expect(fakeGlobal.process.removeListener).toHaveBeenCalledWith(
|
expect(globals.listeners.uncaughtException).toEqual([originalHandler]);
|
||||||
'uncaughtException',
|
|
||||||
addedListener
|
|
||||||
);
|
|
||||||
expect(fakeGlobal.process.on).toHaveBeenCalledWith(
|
|
||||||
'uncaughtException',
|
|
||||||
'foo'
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Reporting unhandled promise rejections in node.js', function() {
|
describe('Reporting unhandled promise rejections in node.js', function() {
|
||||||
it('reports rejections with `Error` reasons', function() {
|
it('reports rejections with `Error` reasons', function() {
|
||||||
const fakeGlobal = {
|
const globals = nodeGlobals();
|
||||||
process: {
|
const errors = new jasmineUnderTest.GlobalErrors(
|
||||||
on: jasmine.createSpy('process.on'),
|
globals.global,
|
||||||
removeListener: jasmine.createSpy('process.removeListener'),
|
() => ({})
|
||||||
listeners: jasmine
|
);
|
||||||
.createSpy('process.listeners')
|
const handler = jasmine.createSpy('errorHandler');
|
||||||
.and.returnValue(['foo']),
|
function originalHandler() {}
|
||||||
removeAllListeners: jasmine.createSpy('process.removeAllListeners')
|
globals.listeners.unhandledRejection = [originalHandler];
|
||||||
}
|
|
||||||
},
|
|
||||||
handler = jasmine.createSpy('errorHandler'),
|
|
||||||
errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
|
|
||||||
|
|
||||||
errors.install();
|
errors.install();
|
||||||
expect(fakeGlobal.process.on).toHaveBeenCalledWith(
|
expect(globals.listeners.unhandledRejection).toEqual([
|
||||||
'unhandledRejection',
|
|
||||||
jasmine.any(Function)
|
jasmine.any(Function)
|
||||||
);
|
]);
|
||||||
expect(fakeGlobal.process.listeners).toHaveBeenCalledWith(
|
expect(globals.listeners.unhandledRejection).not.toEqual([
|
||||||
'unhandledRejection'
|
originalHandler()
|
||||||
);
|
]);
|
||||||
expect(fakeGlobal.process.removeAllListeners).toHaveBeenCalledWith(
|
|
||||||
'unhandledRejection'
|
|
||||||
);
|
|
||||||
|
|
||||||
errors.pushListener(handler);
|
errors.pushListener(handler);
|
||||||
|
|
||||||
const addedListener = fakeGlobal.process.on.calls.argsFor(1)[1];
|
dispatchEvent(globals.listeners, 'unhandledRejection', new Error('bar'));
|
||||||
addedListener(new Error('bar'));
|
|
||||||
|
|
||||||
expect(handler).toHaveBeenCalledWith(new Error('bar'));
|
expect(handler).toHaveBeenCalledWith(new Error('bar'), undefined);
|
||||||
expect(handler.calls.argsFor(0)[0].jasmineMessage).toBe(
|
expect(handler.calls.argsFor(0)[0].jasmineMessage).toBe(
|
||||||
'Unhandled promise rejection: Error: bar'
|
'Unhandled promise rejection: Error: bar'
|
||||||
);
|
);
|
||||||
|
|
||||||
errors.uninstall();
|
errors.uninstall();
|
||||||
|
|
||||||
expect(fakeGlobal.process.removeListener).toHaveBeenCalledWith(
|
expect(globals.listeners.unhandledRejection).toEqual([originalHandler]);
|
||||||
'unhandledRejection',
|
|
||||||
addedListener
|
|
||||||
);
|
|
||||||
expect(fakeGlobal.process.on).toHaveBeenCalledWith(
|
|
||||||
'unhandledRejection',
|
|
||||||
'foo'
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('reports rejections with non-`Error` reasons', function() {
|
it('reports rejections with non-`Error` reasons', function() {
|
||||||
const fakeGlobal = {
|
const globals = nodeGlobals();
|
||||||
process: {
|
const errors = new jasmineUnderTest.GlobalErrors(
|
||||||
on: jasmine.createSpy('process.on'),
|
globals.global,
|
||||||
removeListener: function() {},
|
() => ({})
|
||||||
listeners: function() {
|
);
|
||||||
return [];
|
const handler = jasmine.createSpy('errorHandler');
|
||||||
},
|
|
||||||
removeAllListeners: function() {}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
handler = jasmine.createSpy('errorHandler'),
|
|
||||||
errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
|
|
||||||
|
|
||||||
errors.install();
|
errors.install();
|
||||||
errors.pushListener(handler);
|
errors.pushListener(handler);
|
||||||
|
|
||||||
expect(fakeGlobal.process.on.calls.argsFor(1)[0]).toEqual(
|
dispatchEvent(globals.listeners, 'unhandledRejection', 17);
|
||||||
'unhandledRejection'
|
|
||||||
);
|
|
||||||
const addedListener = fakeGlobal.process.on.calls.argsFor(1)[1];
|
|
||||||
addedListener(17);
|
|
||||||
|
|
||||||
expect(handler).toHaveBeenCalledWith(
|
expect(handler).toHaveBeenCalledWith(
|
||||||
new Error(
|
new Error(
|
||||||
'Unhandled promise rejection: 17\n' +
|
'Unhandled promise rejection: 17\n' +
|
||||||
'(Tip: to get a useful stack trace, use ' +
|
'(Tip: to get a useful stack trace, use ' +
|
||||||
'Promise.reject(new Error(...)) instead of Promise.reject(...).)'
|
'Promise.reject(new Error(...)) instead of Promise.reject(...).)'
|
||||||
)
|
),
|
||||||
|
undefined
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('reports rejections with no reason provided', function() {
|
it('reports rejections with no reason provided', function() {
|
||||||
const fakeGlobal = {
|
const globals = nodeGlobals();
|
||||||
process: {
|
const errors = new jasmineUnderTest.GlobalErrors(
|
||||||
on: jasmine.createSpy('process.on'),
|
globals.global,
|
||||||
removeListener: function() {},
|
() => ({})
|
||||||
listeners: function() {
|
);
|
||||||
return [];
|
const handler = jasmine.createSpy('errorHandler');
|
||||||
},
|
|
||||||
removeAllListeners: function() {}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
handler = jasmine.createSpy('errorHandler'),
|
|
||||||
errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
|
|
||||||
|
|
||||||
errors.install();
|
errors.install();
|
||||||
errors.pushListener(handler);
|
errors.pushListener(handler);
|
||||||
|
|
||||||
expect(fakeGlobal.process.on.calls.argsFor(1)[0]).toEqual(
|
dispatchEvent(globals.listeners, 'unhandledRejection', undefined);
|
||||||
'unhandledRejection'
|
|
||||||
);
|
|
||||||
const addedListener = fakeGlobal.process.on.calls.argsFor(1)[1];
|
|
||||||
addedListener(undefined);
|
|
||||||
|
|
||||||
expect(handler).toHaveBeenCalledWith(
|
expect(handler).toHaveBeenCalledWith(
|
||||||
new Error(
|
new Error(
|
||||||
'Unhandled promise rejection with no error or message\n' +
|
'Unhandled promise rejection with no error or message\n' +
|
||||||
'(Tip: to get a useful stack trace, use ' +
|
'(Tip: to get a useful stack trace, use ' +
|
||||||
'Promise.reject(new Error(...)) instead of Promise.reject().)'
|
'Promise.reject(new Error(...)) instead of Promise.reject().)'
|
||||||
)
|
),
|
||||||
|
undefined
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('When detectLateRejectionHandling is true', function() {
|
||||||
|
let globals, errors;
|
||||||
|
|
||||||
|
beforeEach(function() {
|
||||||
|
globals = nodeGlobals();
|
||||||
|
errors = new jasmineUnderTest.GlobalErrors(globals.global, () => ({
|
||||||
|
detectLateRejectionHandling: true
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('subscribes and unsubscribes from the rejectionHandled event', function() {
|
||||||
|
function originalHandler() {}
|
||||||
|
globals.global.process.on('rejectionHandled', originalHandler);
|
||||||
|
errors.install();
|
||||||
|
|
||||||
|
expect(globals.listeners.rejectionHandled).toEqual([
|
||||||
|
jasmine.any(Function)
|
||||||
|
]);
|
||||||
|
expect(globals.listeners.rejectionHandled).not.toEqual([
|
||||||
|
originalHandler
|
||||||
|
]);
|
||||||
|
|
||||||
|
errors.uninstall();
|
||||||
|
expect(globals.listeners.rejectionHandled).toEqual([originalHandler]);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("When the unhandledRejection event doesn't have a promise", function() {
|
||||||
|
it('immediately reports the rejection', function() {
|
||||||
|
const handler = jasmine.createSpy('errorHandler');
|
||||||
|
|
||||||
|
errors.install();
|
||||||
|
errors.pushListener(handler);
|
||||||
|
|
||||||
|
dispatchEvent(
|
||||||
|
globals.listeners,
|
||||||
|
'unhandledRejection',
|
||||||
|
new Error('nope'),
|
||||||
|
undefined
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(handler).toHaveBeenCalledWith(new Error('nope'), undefined);
|
||||||
|
expect(handler.calls.argsFor(0)[0].jasmineMessage).toBe(
|
||||||
|
'Unhandled promise rejection: Error: nope'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('When the unhandledRejection event has a promise property', function() {
|
||||||
|
it('does not immediately report the rejection', function() {
|
||||||
|
const handler = jasmine.createSpy('errorHandler');
|
||||||
|
|
||||||
|
errors.install();
|
||||||
|
errors.pushListener(handler);
|
||||||
|
|
||||||
|
const promise = Promise.reject('nope');
|
||||||
|
promise.catch(() => {});
|
||||||
|
dispatchEvent(
|
||||||
|
globals.listeners,
|
||||||
|
'unhandledRejection',
|
||||||
|
'nope',
|
||||||
|
promise
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(handler).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('When reportUnhandledRejections is called', function() {
|
||||||
|
it('reports rejections that have not been handled', function() {
|
||||||
|
const handler = jasmine.createSpy('errorHandler');
|
||||||
|
errors.install();
|
||||||
|
errors.pushListener(handler);
|
||||||
|
|
||||||
|
const reason = new Error('nope');
|
||||||
|
const promise = Promise.reject(reason);
|
||||||
|
promise.catch(() => {});
|
||||||
|
dispatchEvent(
|
||||||
|
globals.listeners,
|
||||||
|
'unhandledRejection',
|
||||||
|
reason,
|
||||||
|
promise
|
||||||
|
);
|
||||||
|
errors.reportUnhandledRejections();
|
||||||
|
|
||||||
|
expect(handler).toHaveBeenCalledWith(new Error('nope'), undefined);
|
||||||
|
expect(handler.calls.argsFor(0)[0].jasmineMessage).toBe(
|
||||||
|
'Unhandled promise rejection: Error: nope'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does not report rejections that have been handled', function() {
|
||||||
|
const handler = jasmine.createSpy('errorHandler');
|
||||||
|
errors.install();
|
||||||
|
errors.pushListener(handler);
|
||||||
|
|
||||||
|
const reason = new Error('nope');
|
||||||
|
const promise = Promise.reject(reason);
|
||||||
|
promise.catch(() => {});
|
||||||
|
dispatchEvent(
|
||||||
|
globals.listeners,
|
||||||
|
'unhandledRejection',
|
||||||
|
reason,
|
||||||
|
promise
|
||||||
|
);
|
||||||
|
dispatchEvent(globals.listeners, 'rejectionHandled', promise);
|
||||||
|
errors.reportUnhandledRejections();
|
||||||
|
|
||||||
|
expect(handler).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does not report the same rejection on subsequent calls', function() {
|
||||||
|
const handler = jasmine.createSpy('errorHandler');
|
||||||
|
|
||||||
|
errors.install();
|
||||||
|
errors.pushListener(handler);
|
||||||
|
|
||||||
|
const promise = Promise.reject('nope');
|
||||||
|
promise.catch(() => {});
|
||||||
|
dispatchEvent(
|
||||||
|
globals.listeners,
|
||||||
|
'unhandledRejection',
|
||||||
|
'nope',
|
||||||
|
promise
|
||||||
|
);
|
||||||
|
errors.reportUnhandledRejections();
|
||||||
|
expect(handler).toHaveBeenCalled();
|
||||||
|
handler.calls.reset();
|
||||||
|
|
||||||
|
errors.reportUnhandledRejections();
|
||||||
|
expect(handler).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Reporting unhandled promise rejections in the browser', function() {
|
describe('Reporting unhandled promise rejections in the browser', function() {
|
||||||
it('subscribes and unsubscribes from the unhandledrejection event', function() {
|
it('subscribes and unsubscribes from the unhandledrejection event', function() {
|
||||||
const fakeGlobal = browserGlobal();
|
const globals = browserGlobals();
|
||||||
const errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
|
const errors = new jasmineUnderTest.GlobalErrors(
|
||||||
|
globals.global,
|
||||||
|
() => ({})
|
||||||
|
);
|
||||||
|
|
||||||
errors.install();
|
errors.install();
|
||||||
expect(fakeGlobal.listeners_.unhandledrejection).toEqual([
|
expect(globals.listeners.unhandledrejection).toEqual([
|
||||||
jasmine.any(Function)
|
jasmine.any(Function)
|
||||||
]);
|
]);
|
||||||
|
|
||||||
errors.uninstall();
|
errors.uninstall();
|
||||||
expect(fakeGlobal.listeners_.unhandledrejection).toEqual([]);
|
expect(globals.listeners.unhandledrejection).toEqual([]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('reports rejections whose reason is a string', function() {
|
it('reports rejections whose reason is a string', function() {
|
||||||
const fakeGlobal = browserGlobal();
|
const globals = browserGlobals();
|
||||||
const handler = jasmine.createSpy('errorHandler');
|
const handler = jasmine.createSpy('errorHandler');
|
||||||
const errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
|
const errors = new jasmineUnderTest.GlobalErrors(
|
||||||
|
globals.global,
|
||||||
|
() => ({})
|
||||||
|
);
|
||||||
|
|
||||||
errors.install();
|
errors.install();
|
||||||
errors.pushListener(handler);
|
errors.pushListener(handler);
|
||||||
|
|
||||||
const event = { reason: 'nope' };
|
const event = { reason: 'nope' };
|
||||||
dispatchUnhandledRejectionEvent(fakeGlobal, event);
|
dispatchEvent(globals.listeners, 'unhandledrejection', event);
|
||||||
|
|
||||||
expect(handler).toHaveBeenCalledWith(
|
expect(handler).toHaveBeenCalledWith(
|
||||||
'Unhandled promise rejection: nope',
|
'Unhandled promise rejection: nope',
|
||||||
@@ -306,16 +414,19 @@ describe('GlobalErrors', function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('reports rejections whose reason is an Error', function() {
|
it('reports rejections whose reason is an Error', function() {
|
||||||
const fakeGlobal = browserGlobal();
|
const globals = browserGlobals();
|
||||||
const handler = jasmine.createSpy('errorHandler');
|
const handler = jasmine.createSpy('errorHandler');
|
||||||
const errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
|
const errors = new jasmineUnderTest.GlobalErrors(
|
||||||
|
globals.global,
|
||||||
|
() => ({})
|
||||||
|
);
|
||||||
|
|
||||||
errors.install();
|
errors.install();
|
||||||
errors.pushListener(handler);
|
errors.pushListener(handler);
|
||||||
|
|
||||||
const reason = new Error('bar');
|
const reason = new Error('bar');
|
||||||
const event = { reason };
|
const event = { reason };
|
||||||
dispatchUnhandledRejectionEvent(fakeGlobal, event);
|
dispatchEvent(globals.listeners, 'unhandledrejection', event);
|
||||||
|
|
||||||
expect(handler).toHaveBeenCalledWith(
|
expect(handler).toHaveBeenCalledWith(
|
||||||
jasmine.objectContaining({
|
jasmine.objectContaining({
|
||||||
@@ -326,77 +437,178 @@ describe('GlobalErrors', function() {
|
|||||||
event
|
event
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('When detectLateRejectionHandling is true', function() {
|
||||||
|
let globals, errors;
|
||||||
|
|
||||||
|
beforeEach(function() {
|
||||||
|
globals = browserGlobals();
|
||||||
|
errors = new jasmineUnderTest.GlobalErrors(globals.global, () => ({
|
||||||
|
detectLateRejectionHandling: true
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('subscribes and unsubscribes from the rejectionhandled event', function() {
|
||||||
|
errors.install();
|
||||||
|
expect(globals.listeners.rejectionhandled).toEqual([
|
||||||
|
jasmine.any(Function)
|
||||||
|
]);
|
||||||
|
|
||||||
|
errors.uninstall();
|
||||||
|
expect(globals.listeners.rejectionhandled).toEqual([]);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("When the unhandledrejection event doesn't have a promise property", function() {
|
||||||
|
it('immediately reports the rejection', function() {
|
||||||
|
const handler = jasmine.createSpy('errorHandler');
|
||||||
|
|
||||||
|
errors.install();
|
||||||
|
errors.pushListener(handler);
|
||||||
|
|
||||||
|
const event = { reason: 'nope' };
|
||||||
|
dispatchEvent(globals.listeners, 'unhandledrejection', event);
|
||||||
|
|
||||||
|
expect(handler).toHaveBeenCalledWith(
|
||||||
|
'Unhandled promise rejection: nope',
|
||||||
|
event
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('When the unhandledrejection event has a promise property', function() {
|
||||||
|
it('does not immediately report the rejection', function() {
|
||||||
|
const handler = jasmine.createSpy('errorHandler');
|
||||||
|
|
||||||
|
errors.install();
|
||||||
|
errors.pushListener(handler);
|
||||||
|
|
||||||
|
const promise = Promise.reject('nope');
|
||||||
|
promise.catch(() => {});
|
||||||
|
dispatchEvent(globals.listeners, 'unhandledrejection', {
|
||||||
|
reason: 'nope',
|
||||||
|
promise
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(handler).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('When reportUnhandledRejections is called', function() {
|
||||||
|
it('reports rejections that have not been handled', function() {
|
||||||
|
const handler = jasmine.createSpy('errorHandler');
|
||||||
|
errors.install();
|
||||||
|
errors.pushListener(handler);
|
||||||
|
|
||||||
|
const promise = Promise.reject('nope');
|
||||||
|
promise.catch(() => {});
|
||||||
|
dispatchEvent(globals.listeners, 'unhandledrejection', {
|
||||||
|
reason: 'nope',
|
||||||
|
promise
|
||||||
|
});
|
||||||
|
errors.reportUnhandledRejections();
|
||||||
|
|
||||||
|
expect(handler).toHaveBeenCalledWith(
|
||||||
|
'Unhandled promise rejection: nope',
|
||||||
|
{ reason: 'nope', promise }
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does not report rejections that have been handled', function() {
|
||||||
|
const handler = jasmine.createSpy('errorHandler');
|
||||||
|
errors.install();
|
||||||
|
errors.pushListener(handler);
|
||||||
|
|
||||||
|
const promise = Promise.reject('nope');
|
||||||
|
promise.catch(() => {});
|
||||||
|
dispatchEvent(globals.listeners, 'unhandledrejection', {
|
||||||
|
reason: 'nope',
|
||||||
|
promise
|
||||||
|
});
|
||||||
|
dispatchEvent(globals.listeners, 'rejectionhandled', { promise });
|
||||||
|
errors.reportUnhandledRejections();
|
||||||
|
|
||||||
|
expect(handler).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does not report the same rejection on subsequent calls', function() {
|
||||||
|
const handler = jasmine.createSpy('errorHandler');
|
||||||
|
|
||||||
|
errors.install();
|
||||||
|
errors.pushListener(handler);
|
||||||
|
|
||||||
|
const promise = Promise.reject('nope');
|
||||||
|
promise.catch(() => {});
|
||||||
|
dispatchEvent(globals.listeners, 'unhandledrejection', {
|
||||||
|
reason: 'nope',
|
||||||
|
promise
|
||||||
|
});
|
||||||
|
errors.reportUnhandledRejections();
|
||||||
|
expect(handler).toHaveBeenCalled();
|
||||||
|
handler.calls.reset();
|
||||||
|
|
||||||
|
errors.reportUnhandledRejections();
|
||||||
|
expect(handler).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Reporting uncaught exceptions in node.js', function() {
|
describe('Reporting uncaught exceptions in node.js', function() {
|
||||||
it('prepends a descriptive message when the error is not an `Error`', function() {
|
it('prepends a descriptive message when the error is not an `Error`', function() {
|
||||||
const fakeGlobal = {
|
const globals = nodeGlobals();
|
||||||
process: {
|
const errors = new jasmineUnderTest.GlobalErrors(
|
||||||
on: jasmine.createSpy('process.on'),
|
globals.global,
|
||||||
removeListener: function() {},
|
() => ({})
|
||||||
listeners: function() {
|
);
|
||||||
return [];
|
|
||||||
},
|
|
||||||
removeAllListeners: function() {}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
const handler = jasmine.createSpy('errorHandler');
|
const handler = jasmine.createSpy('errorHandler');
|
||||||
const errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
|
|
||||||
|
|
||||||
errors.install();
|
errors.install();
|
||||||
errors.pushListener(handler);
|
errors.pushListener(handler);
|
||||||
|
|
||||||
uncaughtExceptionListener(fakeGlobal)(17);
|
dispatchEvent(globals.listeners, 'uncaughtException', 17);
|
||||||
|
|
||||||
expect(handler).toHaveBeenCalledWith(new Error('Uncaught exception: 17'));
|
expect(handler).toHaveBeenCalledWith(
|
||||||
|
new Error('Uncaught exception: 17'),
|
||||||
|
undefined
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('substitutes a descriptive message when the error is falsy', function() {
|
it('substitutes a descriptive message when the error is falsy', function() {
|
||||||
const fakeGlobal = {
|
const globals = nodeGlobals();
|
||||||
process: {
|
const errors = new jasmineUnderTest.GlobalErrors(
|
||||||
on: jasmine.createSpy('process.on'),
|
globals.global,
|
||||||
removeListener: function() {},
|
() => ({})
|
||||||
listeners: function() {
|
);
|
||||||
return [];
|
|
||||||
},
|
|
||||||
removeAllListeners: function() {}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
const handler = jasmine.createSpy('errorHandler');
|
const handler = jasmine.createSpy('errorHandler');
|
||||||
const errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
|
|
||||||
|
|
||||||
errors.install();
|
errors.install();
|
||||||
errors.pushListener(handler);
|
errors.pushListener(handler);
|
||||||
|
|
||||||
uncaughtExceptionListener(fakeGlobal)();
|
dispatchEvent(globals.listeners, 'uncaughtException', undefined);
|
||||||
|
|
||||||
expect(handler).toHaveBeenCalledWith(
|
expect(handler).toHaveBeenCalledWith(
|
||||||
new Error('Uncaught exception with no error or message')
|
new Error('Uncaught exception with no error or message'),
|
||||||
|
undefined
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
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() {
|
describe('#setOverrideListener', function() {
|
||||||
it('overrides the existing handlers in browsers until removed', function() {
|
it('overrides the existing handlers in browsers until removed', function() {
|
||||||
const fakeGlobal = browserGlobal();
|
const globals = browserGlobals();
|
||||||
const handler0 = jasmine.createSpy('handler0');
|
const handler0 = jasmine.createSpy('handler0');
|
||||||
const handler1 = jasmine.createSpy('handler1');
|
const handler1 = jasmine.createSpy('handler1');
|
||||||
const overrideHandler = jasmine.createSpy('overrideHandler');
|
const overrideHandler = jasmine.createSpy('overrideHandler');
|
||||||
const errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
|
const errors = new jasmineUnderTest.GlobalErrors(
|
||||||
|
globals.global,
|
||||||
|
() => ({})
|
||||||
|
);
|
||||||
|
|
||||||
errors.install();
|
errors.install();
|
||||||
errors.pushListener(handler0);
|
errors.pushListener(handler0);
|
||||||
errors.setOverrideListener(overrideHandler, () => {});
|
errors.setOverrideListener(overrideHandler, () => {});
|
||||||
errors.pushListener(handler1);
|
errors.pushListener(handler1);
|
||||||
dispatchErrorEvent(fakeGlobal, { error: 'foo' });
|
dispatchEvent(globals.listeners, 'error', { error: 'foo' });
|
||||||
|
|
||||||
expect(overrideHandler).toHaveBeenCalledWith('foo');
|
expect(overrideHandler).toHaveBeenCalledWith('foo');
|
||||||
expect(handler0).not.toHaveBeenCalled();
|
expect(handler0).not.toHaveBeenCalled();
|
||||||
@@ -405,55 +617,48 @@ describe('GlobalErrors', function() {
|
|||||||
errors.removeOverrideListener();
|
errors.removeOverrideListener();
|
||||||
|
|
||||||
const event = { error: 'baz' };
|
const event = { error: 'baz' };
|
||||||
dispatchErrorEvent(fakeGlobal, event);
|
dispatchEvent(globals.listeners, 'error', event);
|
||||||
expect(overrideHandler).not.toHaveBeenCalledWith('baz');
|
expect(overrideHandler).not.toHaveBeenCalledWith('baz');
|
||||||
expect(handler1).toHaveBeenCalledWith('baz', event);
|
expect(handler1).toHaveBeenCalledWith('baz', event);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('overrides the existing handlers in Node until removed', function() {
|
it('overrides the existing handlers in Node until removed', function() {
|
||||||
const globalEventListeners = {};
|
const globals = nodeGlobals();
|
||||||
const fakeGlobal = {
|
|
||||||
process: {
|
|
||||||
on: (name, listener) => (globalEventListeners[name] = listener),
|
|
||||||
removeListener: () => {},
|
|
||||||
listeners: name => globalEventListeners[name],
|
|
||||||
removeAllListeners: name => (globalEventListeners[name] = [])
|
|
||||||
}
|
|
||||||
};
|
|
||||||
const handler0 = jasmine.createSpy('handler0');
|
const handler0 = jasmine.createSpy('handler0');
|
||||||
const handler1 = jasmine.createSpy('handler1');
|
const handler1 = jasmine.createSpy('handler1');
|
||||||
const overrideHandler = jasmine.createSpy('overrideHandler');
|
const overrideHandler = jasmine.createSpy('overrideHandler');
|
||||||
const errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
|
const errors = new jasmineUnderTest.GlobalErrors(
|
||||||
|
globals.global,
|
||||||
|
() => ({})
|
||||||
|
);
|
||||||
|
|
||||||
errors.install();
|
errors.install();
|
||||||
errors.pushListener(handler0);
|
errors.pushListener(handler0);
|
||||||
errors.setOverrideListener(overrideHandler);
|
errors.setOverrideListener(overrideHandler);
|
||||||
errors.pushListener(handler1);
|
errors.pushListener(handler1);
|
||||||
|
|
||||||
globalEventListeners['uncaughtException'](new Error('foo'));
|
dispatchEvent(globals.listeners, 'uncaughtException', new Error('foo'));
|
||||||
|
|
||||||
expect(overrideHandler).toHaveBeenCalledWith(new Error('foo'));
|
expect(overrideHandler).toHaveBeenCalledWith(new Error('foo'));
|
||||||
expect(handler0).not.toHaveBeenCalled();
|
expect(handler0).not.toHaveBeenCalled();
|
||||||
expect(handler1).not.toHaveBeenCalled();
|
expect(handler1).not.toHaveBeenCalled();
|
||||||
|
|
||||||
|
overrideHandler.calls.reset();
|
||||||
errors.removeOverrideListener();
|
errors.removeOverrideListener();
|
||||||
|
|
||||||
globalEventListeners['uncaughtException'](new Error('bar'));
|
dispatchEvent(globals.listeners, 'uncaughtException', new Error('bar'));
|
||||||
expect(overrideHandler).not.toHaveBeenCalledWith(new Error('bar'));
|
expect(overrideHandler).not.toHaveBeenCalled();
|
||||||
expect(handler1).toHaveBeenCalledWith(new Error('bar'));
|
expect(handler1).toHaveBeenCalledWith(new Error('bar'), undefined);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('handles unhandled promise rejections in browsers', function() {
|
it('handles unhandled promise rejections in browsers', function() {
|
||||||
const globalEventListeners = {};
|
const globals = browserGlobals();
|
||||||
const fakeGlobal = {
|
|
||||||
addEventListener(name, listener) {
|
|
||||||
globalEventListeners[name] = listener;
|
|
||||||
},
|
|
||||||
removeEventListener() {}
|
|
||||||
};
|
|
||||||
const handler = jasmine.createSpy('handler');
|
const handler = jasmine.createSpy('handler');
|
||||||
const overrideHandler = jasmine.createSpy('overrideHandler');
|
const overrideHandler = jasmine.createSpy('overrideHandler');
|
||||||
const errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
|
const errors = new jasmineUnderTest.GlobalErrors(
|
||||||
|
globals.global,
|
||||||
|
() => ({})
|
||||||
|
);
|
||||||
|
|
||||||
errors.install();
|
errors.install();
|
||||||
errors.pushListener(handler);
|
errors.pushListener(handler);
|
||||||
@@ -461,7 +666,7 @@ describe('GlobalErrors', function() {
|
|||||||
|
|
||||||
const reason = new Error('bar');
|
const reason = new Error('bar');
|
||||||
|
|
||||||
globalEventListeners['unhandledrejection']({ reason: reason });
|
dispatchEvent(globals.listeners, 'unhandledrejection', { reason });
|
||||||
|
|
||||||
expect(overrideHandler).toHaveBeenCalledWith(
|
expect(overrideHandler).toHaveBeenCalledWith(
|
||||||
jasmine.objectContaining({
|
jasmine.objectContaining({
|
||||||
@@ -474,32 +679,21 @@ describe('GlobalErrors', function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('handles unhandled promise rejections in Node', function() {
|
it('handles unhandled promise rejections in Node', function() {
|
||||||
const globalEventListeners = {};
|
const globals = nodeGlobals();
|
||||||
const fakeGlobal = {
|
|
||||||
process: {
|
|
||||||
on(name, listener) {
|
|
||||||
globalEventListeners[name] = listener;
|
|
||||||
},
|
|
||||||
removeListener() {},
|
|
||||||
listeners(name) {
|
|
||||||
return globalEventListeners[name];
|
|
||||||
},
|
|
||||||
removeAllListeners(name) {
|
|
||||||
globalEventListeners[name] = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
const handler0 = jasmine.createSpy('handler0');
|
const handler0 = jasmine.createSpy('handler0');
|
||||||
const handler1 = jasmine.createSpy('handler1');
|
const handler1 = jasmine.createSpy('handler1');
|
||||||
const overrideHandler = jasmine.createSpy('overrideHandler');
|
const overrideHandler = jasmine.createSpy('overrideHandler');
|
||||||
const errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
|
const errors = new jasmineUnderTest.GlobalErrors(
|
||||||
|
globals.global,
|
||||||
|
() => ({})
|
||||||
|
);
|
||||||
|
|
||||||
errors.install();
|
errors.install();
|
||||||
errors.pushListener(handler0);
|
errors.pushListener(handler0);
|
||||||
errors.setOverrideListener(overrideHandler, () => {});
|
errors.setOverrideListener(overrideHandler, () => {});
|
||||||
errors.pushListener(handler1);
|
errors.pushListener(handler1);
|
||||||
|
|
||||||
globalEventListeners['unhandledRejection'](new Error('nope'));
|
dispatchEvent(globals.listeners, 'unhandledRejection', new Error('nope'));
|
||||||
|
|
||||||
expect(overrideHandler).toHaveBeenCalledWith(new Error('nope'));
|
expect(overrideHandler).toHaveBeenCalledWith(new Error('nope'));
|
||||||
expect(handler0).not.toHaveBeenCalled();
|
expect(handler0).not.toHaveBeenCalled();
|
||||||
@@ -507,7 +701,7 @@ describe('GlobalErrors', function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('throws if there is already an override handler', function() {
|
it('throws if there is already an override handler', function() {
|
||||||
const errors = new jasmineUnderTest.GlobalErrors(browserGlobal());
|
const errors = new jasmineUnderTest.GlobalErrors(browserGlobals().global);
|
||||||
|
|
||||||
errors.setOverrideListener(() => {}, () => {});
|
errors.setOverrideListener(() => {}, () => {});
|
||||||
expect(function() {
|
expect(function() {
|
||||||
@@ -519,7 +713,7 @@ describe('GlobalErrors', function() {
|
|||||||
describe('#removeOverrideListener', function() {
|
describe('#removeOverrideListener', function() {
|
||||||
it("calls the handler's onRemove callback", function() {
|
it("calls the handler's onRemove callback", function() {
|
||||||
const onRemove = jasmine.createSpy('onRemove');
|
const onRemove = jasmine.createSpy('onRemove');
|
||||||
const errors = new jasmineUnderTest.GlobalErrors(browserGlobal());
|
const errors = new jasmineUnderTest.GlobalErrors(browserGlobals().global);
|
||||||
|
|
||||||
errors.setOverrideListener(() => {}, onRemove);
|
errors.setOverrideListener(() => {}, onRemove);
|
||||||
errors.removeOverrideListener();
|
errors.removeOverrideListener();
|
||||||
@@ -528,43 +722,69 @@ describe('GlobalErrors', function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('does not throw if there is no handler', function() {
|
it('does not throw if there is no handler', function() {
|
||||||
const errors = new jasmineUnderTest.GlobalErrors(browserGlobal());
|
const errors = new jasmineUnderTest.GlobalErrors(browserGlobals().global);
|
||||||
|
|
||||||
expect(() => errors.removeOverrideListener()).not.toThrow();
|
expect(() => errors.removeOverrideListener()).not.toThrow();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
function browserGlobal() {
|
function browserGlobals() {
|
||||||
|
const listeners = {
|
||||||
|
error: [],
|
||||||
|
unhandledrejection: [],
|
||||||
|
rejectionhandled: []
|
||||||
|
};
|
||||||
return {
|
return {
|
||||||
listeners_: { error: [], unhandledrejection: [] },
|
listeners,
|
||||||
addEventListener(eventName, listener) {
|
global: {
|
||||||
this.listeners_[eventName].push(listener);
|
addEventListener(eventName, listener) {
|
||||||
},
|
listeners[eventName].push(listener);
|
||||||
removeEventListener(eventName, listener) {
|
},
|
||||||
this.listeners_[eventName] = this.listeners_[eventName].filter(
|
removeEventListener(eventName, listener) {
|
||||||
l => l !== listener
|
listeners[eventName] = listeners[eventName].filter(
|
||||||
);
|
l => l !== listener
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function dispatchErrorEvent(global, event) {
|
function nodeGlobals() {
|
||||||
expect(global.listeners_.error.length)
|
const listeners = {
|
||||||
.withContext('number of error listeners')
|
uncaughtException: [],
|
||||||
.toBeGreaterThan(0);
|
unhandledRejection: [],
|
||||||
|
rejectionHandled: []
|
||||||
for (const l of global.listeners_.error) {
|
};
|
||||||
l(event);
|
return {
|
||||||
}
|
listeners,
|
||||||
|
global: {
|
||||||
|
process: {
|
||||||
|
on(eventName, listener) {
|
||||||
|
listeners[eventName].push(listener);
|
||||||
|
},
|
||||||
|
removeListener(eventName, listener) {
|
||||||
|
listeners[eventName] = listeners[eventName].filter(
|
||||||
|
l => l !== listener
|
||||||
|
);
|
||||||
|
},
|
||||||
|
removeAllListeners(eventName) {
|
||||||
|
listeners[eventName] = [];
|
||||||
|
},
|
||||||
|
listeners(eventName) {
|
||||||
|
return listeners[eventName];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function dispatchUnhandledRejectionEvent(global, event) {
|
function dispatchEvent(listeners, eventName, ...args) {
|
||||||
expect(global.listeners_.unhandledrejection.length)
|
expect(listeners[eventName].length)
|
||||||
.withContext('number of unhandledrejection listeners')
|
.withContext(`number of ${eventName} listeners`)
|
||||||
.toBeGreaterThan(0);
|
.toBeGreaterThan(0);
|
||||||
|
|
||||||
for (const l of global.listeners_.unhandledrejection) {
|
for (const l of listeners[eventName]) {
|
||||||
l(event);
|
l.apply(null, args);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -164,7 +164,7 @@ describe('PrettyPrinter', function() {
|
|||||||
"Object({ foo: 'bar', baz: 3, nullValue: null, undefinedValue: undefined })"
|
"Object({ foo: 'bar', baz: 3, nullValue: null, undefinedValue: undefined })"
|
||||||
);
|
);
|
||||||
expect(pp({ foo: function() {}, bar: [1, 2, 3] })).toEqual(
|
expect(pp({ foo: function() {}, bar: [1, 2, 3] })).toEqual(
|
||||||
'Object({ foo: Function, bar: [ 1, 2, 3 ] })'
|
"Object({ foo: Function 'foo', bar: [ 1, 2, 3 ] })"
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -450,7 +450,7 @@ describe('PrettyPrinter', function() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
expect(pp(objFromOtherContext)).toEqual(
|
expect(pp(objFromOtherContext)).toEqual(
|
||||||
"Object({ foo: 'bar', toString: Function })"
|
"Object({ foo: 'bar', toString: Function 'toString' })"
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -477,6 +477,17 @@ describe('PrettyPrinter', function() {
|
|||||||
expect(pp(a)).toEqual('<anonymous>({ })');
|
expect(pp(a)).toEqual('<anonymous>({ })');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('stringifies functions with names', function() {
|
||||||
|
const pp = jasmineUnderTest.makePrettyPrinter();
|
||||||
|
expect(pp(foo)).toEqual("Function 'foo'");
|
||||||
|
function foo() {}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('stringifies functions without names', function() {
|
||||||
|
const pp = jasmineUnderTest.makePrettyPrinter();
|
||||||
|
expect(pp(function() {})).toEqual('Function');
|
||||||
|
});
|
||||||
|
|
||||||
it('should handle objects with null prototype', function() {
|
it('should handle objects with null prototype', function() {
|
||||||
const pp = jasmineUnderTest.makePrettyPrinter();
|
const pp = jasmineUnderTest.makePrettyPrinter();
|
||||||
const obj = Object.create(null);
|
const obj = Object.create(null);
|
||||||
|
|||||||
@@ -1,4 +1,20 @@
|
|||||||
describe('QueueRunner', function() {
|
describe('QueueRunner', function() {
|
||||||
|
it('validates that queueableFns are truthy', function() {
|
||||||
|
expect(function() {
|
||||||
|
new jasmineUnderTest.QueueRunner({
|
||||||
|
queueableFns: [undefined]
|
||||||
|
});
|
||||||
|
}).toThrowError('Received a falsy queueableFn');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('validates that queueableFns have fn properties', function() {
|
||||||
|
expect(function() {
|
||||||
|
new jasmineUnderTest.QueueRunner({
|
||||||
|
queueableFns: [{ fn: undefined }]
|
||||||
|
});
|
||||||
|
}).toThrowError('Received a queueableFn with no fn');
|
||||||
|
});
|
||||||
|
|
||||||
it("runs all the functions it's passed", function() {
|
it("runs all the functions it's passed", function() {
|
||||||
const calls = [],
|
const calls = [],
|
||||||
queueableFn1 = { fn: jasmine.createSpy('fn1') },
|
queueableFn1 = { fn: jasmine.createSpy('fn1') },
|
||||||
|
|||||||
@@ -12,10 +12,10 @@ describe('ReportDispatcher', function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('dispatches requested methods to added reporters', function() {
|
it('dispatches requested methods to added reporters', function() {
|
||||||
const queueRunnerFactory = jasmine.createSpy('queueRunner'),
|
const runQueue = jasmine.createSpy('runQueue'),
|
||||||
dispatcher = new jasmineUnderTest.ReportDispatcher(
|
dispatcher = new jasmineUnderTest.ReportDispatcher(
|
||||||
['foo', 'bar'],
|
['foo', 'bar'],
|
||||||
queueRunnerFactory
|
runQueue
|
||||||
),
|
),
|
||||||
reporter = jasmine.createSpyObj('reporter', ['foo', 'bar']),
|
reporter = jasmine.createSpyObj('reporter', ['foo', 'bar']),
|
||||||
anotherReporter = jasmine.createSpyObj('reporter', ['foo', 'bar']);
|
anotherReporter = jasmine.createSpyObj('reporter', ['foo', 'bar']);
|
||||||
@@ -25,7 +25,7 @@ describe('ReportDispatcher', function() {
|
|||||||
|
|
||||||
dispatcher.foo(123, 456);
|
dispatcher.foo(123, 456);
|
||||||
|
|
||||||
expect(queueRunnerFactory).toHaveBeenCalledWith(
|
expect(runQueue).toHaveBeenCalledWith(
|
||||||
jasmine.objectContaining({
|
jasmine.objectContaining({
|
||||||
queueableFns: [
|
queueableFns: [
|
||||||
{ fn: jasmine.any(Function) },
|
{ fn: jasmine.any(Function) },
|
||||||
@@ -35,7 +35,7 @@ describe('ReportDispatcher', function() {
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
let fns = queueRunnerFactory.calls.mostRecent().args[0].queueableFns;
|
let fns = runQueue.calls.mostRecent().args[0].queueableFns;
|
||||||
fns[0].fn();
|
fns[0].fn();
|
||||||
expect(reporter.foo).toHaveBeenCalledWith(123, 456);
|
expect(reporter.foo).toHaveBeenCalledWith(123, 456);
|
||||||
expect(reporter.foo.calls.mostRecent().object).toBe(reporter);
|
expect(reporter.foo.calls.mostRecent().object).toBe(reporter);
|
||||||
@@ -44,11 +44,11 @@ describe('ReportDispatcher', function() {
|
|||||||
expect(anotherReporter.foo).toHaveBeenCalledWith(123, 456);
|
expect(anotherReporter.foo).toHaveBeenCalledWith(123, 456);
|
||||||
expect(anotherReporter.foo.calls.mostRecent().object).toBe(anotherReporter);
|
expect(anotherReporter.foo.calls.mostRecent().object).toBe(anotherReporter);
|
||||||
|
|
||||||
queueRunnerFactory.calls.reset();
|
runQueue.calls.reset();
|
||||||
|
|
||||||
dispatcher.bar('a', 'b');
|
dispatcher.bar('a', 'b');
|
||||||
|
|
||||||
expect(queueRunnerFactory).toHaveBeenCalledWith(
|
expect(runQueue).toHaveBeenCalledWith(
|
||||||
jasmine.objectContaining({
|
jasmine.objectContaining({
|
||||||
queueableFns: [
|
queueableFns: [
|
||||||
{ fn: jasmine.any(Function) },
|
{ fn: jasmine.any(Function) },
|
||||||
@@ -58,7 +58,7 @@ describe('ReportDispatcher', function() {
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
fns = queueRunnerFactory.calls.mostRecent().args[0].queueableFns;
|
fns = runQueue.calls.mostRecent().args[0].queueableFns;
|
||||||
fns[0].fn();
|
fns[0].fn();
|
||||||
expect(reporter.bar).toHaveBeenCalledWith('a', 'b');
|
expect(reporter.bar).toHaveBeenCalledWith('a', 'b');
|
||||||
|
|
||||||
@@ -67,17 +67,14 @@ describe('ReportDispatcher', function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("does not dispatch to a reporter if the reporter doesn't accept the method", function() {
|
it("does not dispatch to a reporter if the reporter doesn't accept the method", function() {
|
||||||
const queueRunnerFactory = jasmine.createSpy('queueRunner'),
|
const runQueue = jasmine.createSpy('runQueue'),
|
||||||
dispatcher = new jasmineUnderTest.ReportDispatcher(
|
dispatcher = new jasmineUnderTest.ReportDispatcher(['foo'], runQueue),
|
||||||
['foo'],
|
|
||||||
queueRunnerFactory
|
|
||||||
),
|
|
||||||
reporter = jasmine.createSpyObj('reporter', ['baz']);
|
reporter = jasmine.createSpyObj('reporter', ['baz']);
|
||||||
|
|
||||||
dispatcher.addReporter(reporter);
|
dispatcher.addReporter(reporter);
|
||||||
|
|
||||||
dispatcher.foo(123, 456);
|
dispatcher.foo(123, 456);
|
||||||
expect(queueRunnerFactory).toHaveBeenCalledWith(
|
expect(runQueue).toHaveBeenCalledWith(
|
||||||
jasmine.objectContaining({
|
jasmine.objectContaining({
|
||||||
queueableFns: []
|
queueableFns: []
|
||||||
})
|
})
|
||||||
@@ -85,33 +82,33 @@ describe('ReportDispatcher', function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("allows providing a fallback reporter in case there's no other reporter", function() {
|
it("allows providing a fallback reporter in case there's no other reporter", function() {
|
||||||
const queueRunnerFactory = jasmine.createSpy('queueRunner'),
|
const runQueue = jasmine.createSpy('runQueue'),
|
||||||
dispatcher = new jasmineUnderTest.ReportDispatcher(
|
dispatcher = new jasmineUnderTest.ReportDispatcher(
|
||||||
['foo', 'bar'],
|
['foo', 'bar'],
|
||||||
queueRunnerFactory
|
runQueue
|
||||||
),
|
),
|
||||||
reporter = jasmine.createSpyObj('reporter', ['foo', 'bar']);
|
reporter = jasmine.createSpyObj('reporter', ['foo', 'bar']);
|
||||||
|
|
||||||
dispatcher.provideFallbackReporter(reporter);
|
dispatcher.provideFallbackReporter(reporter);
|
||||||
dispatcher.foo(123, 456);
|
dispatcher.foo(123, 456);
|
||||||
|
|
||||||
expect(queueRunnerFactory).toHaveBeenCalledWith(
|
expect(runQueue).toHaveBeenCalledWith(
|
||||||
jasmine.objectContaining({
|
jasmine.objectContaining({
|
||||||
queueableFns: [{ fn: jasmine.any(Function) }],
|
queueableFns: [{ fn: jasmine.any(Function) }],
|
||||||
isReporter: true
|
isReporter: true
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
const fns = queueRunnerFactory.calls.mostRecent().args[0].queueableFns;
|
const fns = runQueue.calls.mostRecent().args[0].queueableFns;
|
||||||
fns[0].fn();
|
fns[0].fn();
|
||||||
expect(reporter.foo).toHaveBeenCalledWith(123, 456);
|
expect(reporter.foo).toHaveBeenCalledWith(123, 456);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('does not call fallback reporting methods when another reporter is provided', function() {
|
it('does not call fallback reporting methods when another reporter is provided', function() {
|
||||||
const queueRunnerFactory = jasmine.createSpy('queueRunner'),
|
const runQueue = jasmine.createSpy('runQueue'),
|
||||||
dispatcher = new jasmineUnderTest.ReportDispatcher(
|
dispatcher = new jasmineUnderTest.ReportDispatcher(
|
||||||
['foo', 'bar'],
|
['foo', 'bar'],
|
||||||
queueRunnerFactory
|
runQueue
|
||||||
),
|
),
|
||||||
reporter = jasmine.createSpyObj('reporter', ['foo', 'bar']),
|
reporter = jasmine.createSpyObj('reporter', ['foo', 'bar']),
|
||||||
fallbackReporter = jasmine.createSpyObj('otherReporter', ['foo', 'bar']);
|
fallbackReporter = jasmine.createSpyObj('otherReporter', ['foo', 'bar']);
|
||||||
@@ -120,38 +117,38 @@ describe('ReportDispatcher', function() {
|
|||||||
dispatcher.addReporter(reporter);
|
dispatcher.addReporter(reporter);
|
||||||
dispatcher.foo(123, 456);
|
dispatcher.foo(123, 456);
|
||||||
|
|
||||||
expect(queueRunnerFactory).toHaveBeenCalledWith(
|
expect(runQueue).toHaveBeenCalledWith(
|
||||||
jasmine.objectContaining({
|
jasmine.objectContaining({
|
||||||
queueableFns: [{ fn: jasmine.any(Function) }],
|
queueableFns: [{ fn: jasmine.any(Function) }],
|
||||||
isReporter: true
|
isReporter: true
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
const fns = queueRunnerFactory.calls.mostRecent().args[0].queueableFns;
|
const fns = runQueue.calls.mostRecent().args[0].queueableFns;
|
||||||
fns[0].fn();
|
fns[0].fn();
|
||||||
expect(reporter.foo).toHaveBeenCalledWith(123, 456);
|
expect(reporter.foo).toHaveBeenCalledWith(123, 456);
|
||||||
expect(fallbackReporter.foo).not.toHaveBeenCalledWith(123, 456);
|
expect(fallbackReporter.foo).not.toHaveBeenCalledWith(123, 456);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('allows registered reporters to be cleared', function() {
|
it('allows registered reporters to be cleared', function() {
|
||||||
const queueRunnerFactory = jasmine.createSpy('queueRunner'),
|
const runQueue = jasmine.createSpy('runQueue'),
|
||||||
dispatcher = new jasmineUnderTest.ReportDispatcher(
|
dispatcher = new jasmineUnderTest.ReportDispatcher(
|
||||||
['foo', 'bar'],
|
['foo', 'bar'],
|
||||||
queueRunnerFactory
|
runQueue
|
||||||
),
|
),
|
||||||
reporter1 = jasmine.createSpyObj('reporter1', ['foo', 'bar']),
|
reporter1 = jasmine.createSpyObj('reporter1', ['foo', 'bar']),
|
||||||
reporter2 = jasmine.createSpyObj('reporter2', ['foo', 'bar']);
|
reporter2 = jasmine.createSpyObj('reporter2', ['foo', 'bar']);
|
||||||
|
|
||||||
dispatcher.addReporter(reporter1);
|
dispatcher.addReporter(reporter1);
|
||||||
dispatcher.foo(123);
|
dispatcher.foo(123);
|
||||||
expect(queueRunnerFactory).toHaveBeenCalledWith(
|
expect(runQueue).toHaveBeenCalledWith(
|
||||||
jasmine.objectContaining({
|
jasmine.objectContaining({
|
||||||
queueableFns: [{ fn: jasmine.any(Function) }],
|
queueableFns: [{ fn: jasmine.any(Function) }],
|
||||||
isReporter: true
|
isReporter: true
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
let fns = queueRunnerFactory.calls.mostRecent().args[0].queueableFns;
|
let fns = runQueue.calls.mostRecent().args[0].queueableFns;
|
||||||
fns[0].fn();
|
fns[0].fn();
|
||||||
expect(reporter1.foo).toHaveBeenCalledWith(123);
|
expect(reporter1.foo).toHaveBeenCalledWith(123);
|
||||||
|
|
||||||
@@ -159,14 +156,14 @@ describe('ReportDispatcher', function() {
|
|||||||
dispatcher.addReporter(reporter2);
|
dispatcher.addReporter(reporter2);
|
||||||
dispatcher.bar(456);
|
dispatcher.bar(456);
|
||||||
|
|
||||||
expect(queueRunnerFactory).toHaveBeenCalledWith(
|
expect(runQueue).toHaveBeenCalledWith(
|
||||||
jasmine.objectContaining({
|
jasmine.objectContaining({
|
||||||
queueableFns: [{ fn: jasmine.any(Function) }],
|
queueableFns: [{ fn: jasmine.any(Function) }],
|
||||||
isReporter: true
|
isReporter: true
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
fns = queueRunnerFactory.calls.mostRecent().args[0].queueableFns;
|
fns = runQueue.calls.mostRecent().args[0].queueableFns;
|
||||||
fns[0].fn();
|
fns[0].fn();
|
||||||
expect(reporter1.bar).not.toHaveBeenCalled();
|
expect(reporter1.bar).not.toHaveBeenCalled();
|
||||||
expect(reporter2.bar).toHaveBeenCalledWith(456);
|
expect(reporter2.bar).toHaveBeenCalledWith(456);
|
||||||
|
|||||||
630
spec/core/RunnerSpec.js
Normal file
630
spec/core/RunnerSpec.js
Normal file
@@ -0,0 +1,630 @@
|
|||||||
|
describe('Runner', function() {
|
||||||
|
describe('Integration with TreeProcessor and TreeRunner', function() {
|
||||||
|
let suiteNumber,
|
||||||
|
specNumber,
|
||||||
|
runQueue,
|
||||||
|
globalErrors,
|
||||||
|
reportDispatcher,
|
||||||
|
failSpecWithNoExpectations,
|
||||||
|
detectLateRejectionHandling;
|
||||||
|
|
||||||
|
beforeEach(function() {
|
||||||
|
suiteNumber = 0;
|
||||||
|
specNumber = 0;
|
||||||
|
runQueue = jasmine.createSpy('runQueue');
|
||||||
|
globalErrors = 'the global errors instance';
|
||||||
|
reportDispatcher = jasmine.createSpyObj(
|
||||||
|
'reportDispatcher',
|
||||||
|
jasmineUnderTest.reporterEvents
|
||||||
|
);
|
||||||
|
|
||||||
|
for (const k of jasmineUnderTest.reporterEvents) {
|
||||||
|
reportDispatcher[k].and.returnValue(Promise.resolve());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reasonable defaults, may be overridden in some cases
|
||||||
|
failSpecWithNoExpectations = false;
|
||||||
|
detectLateRejectionHandling = false;
|
||||||
|
|
||||||
|
spyOn(jasmineUnderTest.TreeRunner.prototype, '_executeSpec');
|
||||||
|
});
|
||||||
|
|
||||||
|
function StubSuite(attrs) {
|
||||||
|
attrs = attrs || {};
|
||||||
|
this.id = 'suite' + suiteNumber++;
|
||||||
|
this.children = attrs.children || [];
|
||||||
|
this.canBeReentered = function() {
|
||||||
|
return !attrs.noReenter;
|
||||||
|
};
|
||||||
|
this.markedPending = attrs.markedPending || false;
|
||||||
|
this.sharedUserContext = function() {
|
||||||
|
return attrs.userContext || {};
|
||||||
|
};
|
||||||
|
this.result = {
|
||||||
|
id: this.id,
|
||||||
|
failedExpectations: []
|
||||||
|
};
|
||||||
|
this.getResult = jasmine.createSpy('getResult');
|
||||||
|
this.beforeAllFns = attrs.beforeAllFns || [];
|
||||||
|
this.afterAllFns = attrs.afterAllFns || [];
|
||||||
|
this.cleanupBeforeAfter = function() {};
|
||||||
|
this.startTimer = function() {};
|
||||||
|
this.endTimer = function() {};
|
||||||
|
}
|
||||||
|
|
||||||
|
function StubSpec(attrs) {
|
||||||
|
attrs = attrs || {};
|
||||||
|
this.id = 'spec' + specNumber++;
|
||||||
|
this.markedPending = attrs.markedPending || false;
|
||||||
|
this.execute = jasmine.createSpy(this.id + '#execute');
|
||||||
|
this.beforeAndAfterFns = () => ({ befores: [], afters: [] });
|
||||||
|
this.userContext = () => ({});
|
||||||
|
this.getFullName = () => '';
|
||||||
|
this.queueableFn = () => {};
|
||||||
|
}
|
||||||
|
|
||||||
|
function makeRunner(topSuite) {
|
||||||
|
const defaultOptions = {
|
||||||
|
getConfig: () => ({
|
||||||
|
specFilter: () => true,
|
||||||
|
failSpecWithNoExpectations,
|
||||||
|
detectLateRejectionHandling
|
||||||
|
}),
|
||||||
|
focusedRunables: () => [],
|
||||||
|
totalSpecsDefined: () => 1,
|
||||||
|
TreeProcessor: jasmineUnderTest.TreeProcessor,
|
||||||
|
runableResources: {
|
||||||
|
initForRunable: () => {},
|
||||||
|
clearForRunable: () => {}
|
||||||
|
},
|
||||||
|
reportDispatcher,
|
||||||
|
globalErrors,
|
||||||
|
runQueue
|
||||||
|
};
|
||||||
|
return new jasmineUnderTest.Runner({
|
||||||
|
...defaultOptions,
|
||||||
|
topSuite
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function arrayNotContaining(item) {
|
||||||
|
return {
|
||||||
|
asymmetricMatch(other, matchersUtil) {
|
||||||
|
if (!jasmine.isArray_(other)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const x of other) {
|
||||||
|
if (matchersUtil.equals(x, item)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Precondition: jasmineUnderTest.TreeRunner.prototype._executeSpec is a spy
|
||||||
|
function verifyAndFinishSpec(spec, queueableFn, shouldBeExcluded) {
|
||||||
|
const ex = jasmineUnderTest.TreeRunner.prototype._executeSpec;
|
||||||
|
ex.withArgs(spec, 'onComplete').and.callThrough();
|
||||||
|
|
||||||
|
queueableFn.fn('onComplete');
|
||||||
|
expect(ex).toHaveBeenCalledWith(spec, 'onComplete');
|
||||||
|
|
||||||
|
expect(runQueue).toHaveBeenCalledWith(
|
||||||
|
jasmine.objectContaining({
|
||||||
|
isLeaf: true,
|
||||||
|
SkipPolicy: jasmineUnderTest.CompleteOnFirstErrorSkipPolicy,
|
||||||
|
queueableFns: shouldBeExcluded
|
||||||
|
? arrayNotContaining(spec.queueableFn)
|
||||||
|
: jasmine.arrayContaining([spec.queueableFn])
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
it('runs a single spec', async function() {
|
||||||
|
const spec = new StubSpec();
|
||||||
|
const topSuite = new StubSuite({
|
||||||
|
children: [spec],
|
||||||
|
userContext: { root: 'context' }
|
||||||
|
});
|
||||||
|
detectLateRejectionHandling = true;
|
||||||
|
const subject = makeRunner(topSuite);
|
||||||
|
|
||||||
|
const promise = subject.execute();
|
||||||
|
await Promise.resolve();
|
||||||
|
|
||||||
|
expect(runQueue).toHaveBeenCalledWith({
|
||||||
|
onComplete: jasmine.any(Function),
|
||||||
|
onException: jasmine.any(Function),
|
||||||
|
userContext: { root: 'context' },
|
||||||
|
queueableFns: [{ fn: jasmine.any(Function) }],
|
||||||
|
onMultipleDone: null,
|
||||||
|
SkipPolicy: jasmineUnderTest.SkipAfterBeforeAllErrorPolicy
|
||||||
|
});
|
||||||
|
|
||||||
|
const runQueueArgs = runQueue.calls.mostRecent().args[0];
|
||||||
|
verifyAndFinishSpec(spec, runQueueArgs.queueableFns[0], false);
|
||||||
|
runQueueArgs.onComplete();
|
||||||
|
await promise;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('runs an empty suite', async function() {
|
||||||
|
const suite = new StubSuite({ userContext: { for: 'suite' } });
|
||||||
|
const topSuite = new StubSuite({
|
||||||
|
children: [suite],
|
||||||
|
userContext: { for: 'topSuite' }
|
||||||
|
});
|
||||||
|
suite.parentSuite = topSuite;
|
||||||
|
const subject = makeRunner(topSuite);
|
||||||
|
|
||||||
|
const promise = subject.execute();
|
||||||
|
await Promise.resolve();
|
||||||
|
|
||||||
|
expect(runQueue).toHaveBeenCalledWith({
|
||||||
|
onComplete: jasmine.any(Function),
|
||||||
|
onException: jasmine.any(Function),
|
||||||
|
userContext: { for: 'topSuite' },
|
||||||
|
queueableFns: [{ fn: jasmine.any(Function) }],
|
||||||
|
onMultipleDone: null,
|
||||||
|
SkipPolicy: jasmineUnderTest.SkipAfterBeforeAllErrorPolicy
|
||||||
|
});
|
||||||
|
|
||||||
|
const runQueueArgs = runQueue.calls.mostRecent().args[0];
|
||||||
|
const nodeDone = jasmine.createSpy('nodeDone');
|
||||||
|
runQueueArgs.queueableFns[0].fn(nodeDone);
|
||||||
|
expect(runQueue).toHaveBeenCalledWith({
|
||||||
|
onComplete: jasmine.any(Function),
|
||||||
|
onMultipleDone: null,
|
||||||
|
queueableFns: [{ fn: jasmine.any(Function) }],
|
||||||
|
userContext: { for: 'suite' },
|
||||||
|
onException: jasmine.any(Function),
|
||||||
|
onMultipleDone: null,
|
||||||
|
SkipPolicy: jasmineUnderTest.SkipAfterBeforeAllErrorPolicy
|
||||||
|
});
|
||||||
|
|
||||||
|
runQueue.calls.mostRecent().args[0].queueableFns[0].fn('foo');
|
||||||
|
expect(reportDispatcher.suiteStarted).toHaveBeenCalledWith(suite.result);
|
||||||
|
|
||||||
|
suite.getResult.and.returnValue({ my: 'result' });
|
||||||
|
|
||||||
|
runQueue.calls.mostRecent().args[0].onComplete();
|
||||||
|
expect(reportDispatcher.suiteDone).toHaveBeenCalledWith({ my: 'result' });
|
||||||
|
|
||||||
|
runQueueArgs.onComplete();
|
||||||
|
await promise;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('runs a non-empty suite', async function() {
|
||||||
|
const spec1 = new StubSpec();
|
||||||
|
const spec2 = new StubSpec();
|
||||||
|
const suite = new StubSuite({ children: [spec1, spec2] });
|
||||||
|
const topSuite = new StubSuite({ children: [suite] });
|
||||||
|
const subject = makeRunner(topSuite);
|
||||||
|
|
||||||
|
const promise = subject.execute();
|
||||||
|
await Promise.resolve();
|
||||||
|
expect(runQueue).toHaveBeenCalledTimes(1);
|
||||||
|
let queueableFns = runQueue.calls.mostRecent().args[0].queueableFns;
|
||||||
|
queueableFns[0].fn(function() {});
|
||||||
|
|
||||||
|
expect(runQueue).toHaveBeenCalledTimes(2);
|
||||||
|
queueableFns = runQueue.calls.mostRecent().args[0].queueableFns;
|
||||||
|
expect(queueableFns.length).toBe(3);
|
||||||
|
|
||||||
|
verifyAndFinishSpec(spec1, queueableFns[1], false);
|
||||||
|
verifyAndFinishSpec(spec2, queueableFns[2], false);
|
||||||
|
await expectAsync(promise).toBePending();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('"runs" an excluded suite', async function() {
|
||||||
|
const spec = new StubSpec();
|
||||||
|
const parent = new StubSuite({ children: [spec] });
|
||||||
|
const topSuite = new StubSuite({ children: [parent] });
|
||||||
|
parent.parentSuite = topSuite;
|
||||||
|
const subject = makeRunner(topSuite);
|
||||||
|
|
||||||
|
// Empty list of runable IDs excludes everything
|
||||||
|
const promise = subject.execute([]);
|
||||||
|
await Promise.resolve();
|
||||||
|
expect(runQueue).toHaveBeenCalledTimes(1);
|
||||||
|
let queueableFns = runQueue.calls.mostRecent().args[0].queueableFns;
|
||||||
|
queueableFns[0].fn(function() {});
|
||||||
|
|
||||||
|
queueableFns = runQueue.calls.mostRecent().args[0].queueableFns;
|
||||||
|
expect(queueableFns.length).toBe(2);
|
||||||
|
|
||||||
|
queueableFns[0].fn();
|
||||||
|
expect(reportDispatcher.suiteStarted).toHaveBeenCalledWith(parent.result);
|
||||||
|
|
||||||
|
verifyAndFinishSpec(spec, queueableFns[1], true);
|
||||||
|
|
||||||
|
parent.getResult.and.returnValue(parent.result);
|
||||||
|
runQueue.calls.argsFor(1)[0].onComplete();
|
||||||
|
expect(reportDispatcher.suiteDone).toHaveBeenCalledWith(parent.result);
|
||||||
|
await expectAsync(promise).toBePending();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('handles the failSpecWithNoExpectations option', async function() {
|
||||||
|
failSpecWithNoExpectations = true;
|
||||||
|
const spec = new StubSpec();
|
||||||
|
const parent = new StubSuite({ children: [spec] });
|
||||||
|
const topSuite = new StubSuite({ children: [parent] });
|
||||||
|
parent.parentSuite = topSuite;
|
||||||
|
const subject = makeRunner(topSuite);
|
||||||
|
|
||||||
|
const promise = subject.execute();
|
||||||
|
await Promise.resolve();
|
||||||
|
expect(runQueue).toHaveBeenCalledTimes(1);
|
||||||
|
let queueableFns = runQueue.calls.mostRecent().args[0].queueableFns;
|
||||||
|
queueableFns[0].fn(function() {});
|
||||||
|
|
||||||
|
queueableFns = runQueue.calls.mostRecent().args[0].queueableFns;
|
||||||
|
expect(queueableFns.length).toBe(2);
|
||||||
|
|
||||||
|
queueableFns[1].fn('foo');
|
||||||
|
expect(
|
||||||
|
jasmineUnderTest.TreeRunner.prototype._executeSpec
|
||||||
|
).toHaveBeenCalledWith(spec, 'foo');
|
||||||
|
|
||||||
|
await expectAsync(promise).toBePending();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('runs beforeAlls and afterAlls for a suite with children', async function() {
|
||||||
|
const spec = new StubSpec();
|
||||||
|
const target = new StubSuite({
|
||||||
|
children: [spec],
|
||||||
|
beforeAllFns: [
|
||||||
|
{ fn: 'beforeAll1', timeout: 1 },
|
||||||
|
{ fn: 'beforeAll2', timeout: 2 }
|
||||||
|
],
|
||||||
|
afterAllFns: [
|
||||||
|
{ fn: 'afterAll1', timeout: 3 },
|
||||||
|
{ fn: 'afterAll2', timeout: 4 }
|
||||||
|
]
|
||||||
|
});
|
||||||
|
const topSuite = new StubSuite({ children: [target] });
|
||||||
|
const subject = makeRunner(topSuite);
|
||||||
|
|
||||||
|
const promise = subject.execute();
|
||||||
|
await Promise.resolve();
|
||||||
|
expect(runQueue).toHaveBeenCalledTimes(1);
|
||||||
|
const queueableFns = runQueue.calls.mostRecent().args[0].queueableFns;
|
||||||
|
queueableFns[0].fn(function() {});
|
||||||
|
|
||||||
|
expect(runQueue.calls.mostRecent().args[0].queueableFns).toEqual([
|
||||||
|
{ fn: jasmine.any(Function) },
|
||||||
|
{ fn: 'beforeAll1', timeout: 1 },
|
||||||
|
{ fn: 'beforeAll2', timeout: 2 },
|
||||||
|
{ fn: jasmine.any(Function) },
|
||||||
|
{ fn: 'afterAll1', timeout: 3 },
|
||||||
|
{ fn: 'afterAll2', timeout: 4 }
|
||||||
|
]);
|
||||||
|
|
||||||
|
await expectAsync(promise).toBePending();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does not run beforeAlls or afterAlls for a suite with no children', async function() {
|
||||||
|
const target = new StubSuite({
|
||||||
|
beforeAllFns: [{ fn: 'before' }],
|
||||||
|
afterAllFns: [{ fn: 'after' }]
|
||||||
|
});
|
||||||
|
const topSuite = new StubSuite({ children: [target] });
|
||||||
|
const subject = makeRunner(topSuite);
|
||||||
|
|
||||||
|
const promise = subject.execute();
|
||||||
|
await Promise.resolve();
|
||||||
|
expect(runQueue).toHaveBeenCalledTimes(1);
|
||||||
|
const queueableFns = runQueue.calls.mostRecent().args[0].queueableFns;
|
||||||
|
queueableFns[0].fn(function() {});
|
||||||
|
|
||||||
|
expect(runQueue.calls.mostRecent().args[0].queueableFns.length).toEqual(
|
||||||
|
1
|
||||||
|
);
|
||||||
|
|
||||||
|
await expectAsync(promise).toBePending();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does not run beforeAlls or afterAlls for a suite with only pending children', async function() {
|
||||||
|
const spec = new StubSpec({ markedPending: true });
|
||||||
|
const target = new StubSuite({
|
||||||
|
children: [spec],
|
||||||
|
beforeAllFns: [{ fn: 'before' }],
|
||||||
|
afterAllFns: [{ fn: 'after' }]
|
||||||
|
});
|
||||||
|
const topSuite = new StubSuite({ children: [target] });
|
||||||
|
const subject = makeRunner(topSuite);
|
||||||
|
|
||||||
|
const promise = subject.execute();
|
||||||
|
await Promise.resolve();
|
||||||
|
expect(runQueue).toHaveBeenCalledTimes(1);
|
||||||
|
const queueableFns = runQueue.calls.mostRecent().args[0].queueableFns;
|
||||||
|
queueableFns[0].fn(function() {});
|
||||||
|
|
||||||
|
expect(runQueue.calls.mostRecent().args[0].queueableFns.length).toEqual(
|
||||||
|
2
|
||||||
|
);
|
||||||
|
|
||||||
|
await expectAsync(promise).toBePending();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('runs specs in the order specified', async function() {
|
||||||
|
const specs = [new StubSpec(), new StubSpec()];
|
||||||
|
const topSuite = new StubSuite({ children: specs });
|
||||||
|
const subject = makeRunner(topSuite);
|
||||||
|
|
||||||
|
const promise = subject.execute([specs[1].id, specs[0].id]);
|
||||||
|
await Promise.resolve();
|
||||||
|
expect(runQueue).toHaveBeenCalledTimes(1);
|
||||||
|
const queueableFns = runQueue.calls.mostRecent().args[0].queueableFns;
|
||||||
|
queueableFns[0].fn('done');
|
||||||
|
|
||||||
|
expect(
|
||||||
|
jasmineUnderTest.TreeRunner.prototype._executeSpec
|
||||||
|
).not.toHaveBeenCalledWith(specs[0], jasmine.anything());
|
||||||
|
expect(
|
||||||
|
jasmineUnderTest.TreeRunner.prototype._executeSpec
|
||||||
|
).toHaveBeenCalledWith(specs[1], 'done');
|
||||||
|
|
||||||
|
queueableFns[1].fn('done');
|
||||||
|
|
||||||
|
expect(
|
||||||
|
jasmineUnderTest.TreeRunner.prototype._executeSpec
|
||||||
|
).toHaveBeenCalledWith(specs[0], 'done');
|
||||||
|
|
||||||
|
await expectAsync(promise).toBePending();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('runs specified specs before non-specified specs within a suite', async function() {
|
||||||
|
const specified = new StubSpec();
|
||||||
|
const nonSpecified = new StubSpec();
|
||||||
|
const topSuite = new StubSuite({ children: [nonSpecified, specified] });
|
||||||
|
const subject = makeRunner(topSuite);
|
||||||
|
|
||||||
|
const promise = subject.execute([specified.id]);
|
||||||
|
await Promise.resolve();
|
||||||
|
expect(runQueue).toHaveBeenCalledTimes(1);
|
||||||
|
const queueableFns = runQueue.calls.mostRecent().args[0].queueableFns;
|
||||||
|
queueableFns[0].fn('done');
|
||||||
|
|
||||||
|
expect(
|
||||||
|
jasmineUnderTest.TreeRunner.prototype._executeSpec
|
||||||
|
).not.toHaveBeenCalledWith(nonSpecified, jasmine.anything());
|
||||||
|
expect(
|
||||||
|
jasmineUnderTest.TreeRunner.prototype._executeSpec
|
||||||
|
).toHaveBeenCalledWith(specified, 'done');
|
||||||
|
|
||||||
|
queueableFns[1].fn('done');
|
||||||
|
|
||||||
|
expect(
|
||||||
|
jasmineUnderTest.TreeRunner.prototype._executeSpec
|
||||||
|
).toHaveBeenCalledWith(nonSpecified, 'done');
|
||||||
|
|
||||||
|
await expectAsync(promise).toBePending();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('runs suites and specs with a specified order', async function() {
|
||||||
|
const specifiedSpec = new StubSpec();
|
||||||
|
const nonSpecifiedSpec = new StubSpec();
|
||||||
|
const specifiedSuite = new StubSuite({ children: [nonSpecifiedSpec] });
|
||||||
|
const topSuite = new StubSuite({
|
||||||
|
children: [specifiedSpec, specifiedSuite]
|
||||||
|
});
|
||||||
|
const subject = makeRunner(topSuite);
|
||||||
|
|
||||||
|
const promise = subject.execute([specifiedSuite.id, specifiedSpec.id]);
|
||||||
|
await Promise.resolve();
|
||||||
|
expect(runQueue).toHaveBeenCalledTimes(1);
|
||||||
|
const queueableFns = runQueue.calls.mostRecent().args[0].queueableFns;
|
||||||
|
queueableFns[0].fn();
|
||||||
|
|
||||||
|
expect(specifiedSpec.execute).not.toHaveBeenCalled();
|
||||||
|
const nodeQueueableFns = runQueue.calls.mostRecent().args[0].queueableFns;
|
||||||
|
nodeQueueableFns[1].fn('done');
|
||||||
|
expect(
|
||||||
|
jasmineUnderTest.TreeRunner.prototype._executeSpec
|
||||||
|
).toHaveBeenCalledWith(nonSpecifiedSpec, 'done');
|
||||||
|
|
||||||
|
queueableFns[1].fn('done');
|
||||||
|
expect(
|
||||||
|
jasmineUnderTest.TreeRunner.prototype._executeSpec
|
||||||
|
).toHaveBeenCalledWith(specifiedSpec, 'done');
|
||||||
|
|
||||||
|
await expectAsync(promise).toBePending();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('runs suites and specs in the order they were declared', async function() {
|
||||||
|
const spec1 = new StubSpec();
|
||||||
|
const spec2 = new StubSpec();
|
||||||
|
const spec3 = new StubSpec();
|
||||||
|
const parent = new StubSuite({ children: [spec2, spec3] });
|
||||||
|
const topSuite = new StubSuite({ children: [spec1, parent] });
|
||||||
|
const subject = makeRunner(topSuite);
|
||||||
|
|
||||||
|
const promise = subject.execute();
|
||||||
|
await Promise.resolve();
|
||||||
|
expect(runQueue).toHaveBeenCalledTimes(1);
|
||||||
|
const queueableFns = runQueue.calls.mostRecent().args[0].queueableFns;
|
||||||
|
expect(queueableFns.length).toBe(2);
|
||||||
|
|
||||||
|
queueableFns[0].fn('done');
|
||||||
|
expect(
|
||||||
|
jasmineUnderTest.TreeRunner.prototype._executeSpec
|
||||||
|
).toHaveBeenCalledWith(spec1, 'done');
|
||||||
|
|
||||||
|
queueableFns[1].fn();
|
||||||
|
const childFns = runQueue.calls.mostRecent().args[0].queueableFns;
|
||||||
|
expect(childFns.length).toBe(3);
|
||||||
|
childFns[1].fn('done');
|
||||||
|
expect(
|
||||||
|
jasmineUnderTest.TreeRunner.prototype._executeSpec
|
||||||
|
).toHaveBeenCalledWith(spec2, 'done');
|
||||||
|
|
||||||
|
childFns[2].fn('done');
|
||||||
|
expect(
|
||||||
|
jasmineUnderTest.TreeRunner.prototype._executeSpec
|
||||||
|
).toHaveBeenCalledWith(spec3, 'done');
|
||||||
|
|
||||||
|
await expectAsync(promise).toBePending();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('runs a suite multiple times if the order specified leaves and re-enters it', async function() {
|
||||||
|
const spec1 = new StubSpec();
|
||||||
|
const spec2 = new StubSpec();
|
||||||
|
const spec3 = new StubSpec();
|
||||||
|
const spec4 = new StubSpec();
|
||||||
|
const spec5 = new StubSpec();
|
||||||
|
const reentered = new StubSuite({ children: [spec1, spec2, spec3] });
|
||||||
|
const topSuite = new StubSuite({ children: [reentered, spec4, spec5] });
|
||||||
|
const subject = makeRunner(topSuite);
|
||||||
|
|
||||||
|
spyOn(jasmineUnderTest.getEnv(), 'deprecated');
|
||||||
|
const promise = subject.execute([
|
||||||
|
spec1.id,
|
||||||
|
spec4.id,
|
||||||
|
spec2.id,
|
||||||
|
spec5.id,
|
||||||
|
spec3.id
|
||||||
|
]);
|
||||||
|
await Promise.resolve();
|
||||||
|
expect(runQueue).toHaveBeenCalledTimes(1);
|
||||||
|
const queueableFns = runQueue.calls.mostRecent().args[0].queueableFns;
|
||||||
|
|
||||||
|
queueableFns[0].fn();
|
||||||
|
expect(runQueue.calls.mostRecent().args[0].queueableFns.length).toBe(2);
|
||||||
|
runQueue.calls.mostRecent().args[0].queueableFns[1].fn('done');
|
||||||
|
expect(
|
||||||
|
jasmineUnderTest.TreeRunner.prototype._executeSpec
|
||||||
|
).toHaveBeenCalledWith(spec1, 'done');
|
||||||
|
|
||||||
|
queueableFns[1].fn('done');
|
||||||
|
expect(
|
||||||
|
jasmineUnderTest.TreeRunner.prototype._executeSpec
|
||||||
|
).toHaveBeenCalledWith(spec4, 'done');
|
||||||
|
|
||||||
|
queueableFns[2].fn();
|
||||||
|
expect(runQueue.calls.count()).toBe(3);
|
||||||
|
expect(runQueue.calls.mostRecent().args[0].queueableFns.length).toBe(2);
|
||||||
|
runQueue.calls.mostRecent().args[0].queueableFns[1].fn('done');
|
||||||
|
expect(
|
||||||
|
jasmineUnderTest.TreeRunner.prototype._executeSpec
|
||||||
|
).toHaveBeenCalledWith(spec2, 'done');
|
||||||
|
|
||||||
|
queueableFns[3].fn('done');
|
||||||
|
expect(
|
||||||
|
jasmineUnderTest.TreeRunner.prototype._executeSpec
|
||||||
|
).toHaveBeenCalledWith(spec5, 'done');
|
||||||
|
|
||||||
|
queueableFns[4].fn();
|
||||||
|
expect(runQueue.calls.count()).toBe(4);
|
||||||
|
expect(runQueue.calls.mostRecent().args[0].queueableFns.length).toBe(2);
|
||||||
|
runQueue.calls.mostRecent().args[0].queueableFns[1].fn('done');
|
||||||
|
expect(
|
||||||
|
jasmineUnderTest.TreeRunner.prototype._executeSpec
|
||||||
|
).toHaveBeenCalledWith(spec3, 'done');
|
||||||
|
|
||||||
|
await expectAsync(promise).toBePending();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('runs a parent of a suite with multiple segments correctly', async function() {
|
||||||
|
const spec1 = new StubSpec();
|
||||||
|
const spec2 = new StubSpec();
|
||||||
|
const spec3 = new StubSpec();
|
||||||
|
const spec4 = new StubSpec();
|
||||||
|
const spec5 = new StubSpec();
|
||||||
|
const parent = new StubSuite({ children: [spec1, spec2, spec3] });
|
||||||
|
const grandparent = new StubSuite({ children: [parent] });
|
||||||
|
const topSuite = new StubSuite({ children: [grandparent, spec4, spec5] });
|
||||||
|
const subject = makeRunner(topSuite);
|
||||||
|
|
||||||
|
spyOn(jasmineUnderTest.getEnv(), 'deprecated');
|
||||||
|
const promise = subject.execute([
|
||||||
|
spec1.id,
|
||||||
|
spec4.id,
|
||||||
|
spec2.id,
|
||||||
|
spec5.id,
|
||||||
|
spec3.id
|
||||||
|
]);
|
||||||
|
await Promise.resolve();
|
||||||
|
expect(runQueue).toHaveBeenCalledTimes(1);
|
||||||
|
const queueableFns = runQueue.calls.mostRecent().args[0].queueableFns;
|
||||||
|
expect(queueableFns.length).toBe(5);
|
||||||
|
|
||||||
|
queueableFns[0].fn();
|
||||||
|
expect(runQueue.calls.count()).toBe(2);
|
||||||
|
expect(runQueue.calls.mostRecent().args[0].queueableFns.length).toBe(2);
|
||||||
|
|
||||||
|
runQueue.calls.mostRecent().args[0].queueableFns[1].fn();
|
||||||
|
expect(runQueue.calls.count()).toBe(3);
|
||||||
|
|
||||||
|
runQueue.calls.mostRecent().args[0].queueableFns[1].fn('done');
|
||||||
|
expect(
|
||||||
|
jasmineUnderTest.TreeRunner.prototype._executeSpec
|
||||||
|
).toHaveBeenCalledWith(spec1, 'done');
|
||||||
|
|
||||||
|
queueableFns[1].fn('done');
|
||||||
|
expect(
|
||||||
|
jasmineUnderTest.TreeRunner.prototype._executeSpec
|
||||||
|
).toHaveBeenCalledWith(spec4, 'done');
|
||||||
|
|
||||||
|
queueableFns[2].fn();
|
||||||
|
expect(runQueue.calls.count()).toBe(4);
|
||||||
|
expect(runQueue.calls.mostRecent().args[0].queueableFns.length).toBe(2);
|
||||||
|
|
||||||
|
runQueue.calls.mostRecent().args[0].queueableFns[1].fn();
|
||||||
|
expect(runQueue.calls.count()).toBe(5);
|
||||||
|
|
||||||
|
runQueue.calls.mostRecent().args[0].queueableFns[1].fn('done');
|
||||||
|
expect(
|
||||||
|
jasmineUnderTest.TreeRunner.prototype._executeSpec
|
||||||
|
).toHaveBeenCalledWith(spec2, 'done');
|
||||||
|
|
||||||
|
queueableFns[3].fn('done');
|
||||||
|
expect(
|
||||||
|
jasmineUnderTest.TreeRunner.prototype._executeSpec
|
||||||
|
).toHaveBeenCalledWith(spec5, 'done');
|
||||||
|
|
||||||
|
queueableFns[4].fn();
|
||||||
|
expect(runQueue.calls.count()).toBe(6);
|
||||||
|
expect(runQueue.calls.mostRecent().args[0].queueableFns.length).toBe(2);
|
||||||
|
|
||||||
|
runQueue.calls.mostRecent().args[0].queueableFns[1].fn();
|
||||||
|
expect(runQueue.calls.count()).toBe(7);
|
||||||
|
|
||||||
|
runQueue.calls.mostRecent().args[0].queueableFns[1].fn('done');
|
||||||
|
expect(
|
||||||
|
jasmineUnderTest.TreeRunner.prototype._executeSpec
|
||||||
|
).toHaveBeenCalledWith(spec3, 'done');
|
||||||
|
|
||||||
|
await expectAsync(promise).toBePending();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('runs large segments of nodes in the order they were declared', async function() {
|
||||||
|
const specs = [];
|
||||||
|
|
||||||
|
for (let i = 0; i < 11; i++) {
|
||||||
|
specs.push(new StubSpec());
|
||||||
|
}
|
||||||
|
|
||||||
|
const topSuite = new StubSuite({ children: specs });
|
||||||
|
const subject = makeRunner(topSuite);
|
||||||
|
|
||||||
|
const promise = subject.execute();
|
||||||
|
await Promise.resolve();
|
||||||
|
expect(runQueue).toHaveBeenCalledTimes(1);
|
||||||
|
const queueableFns = runQueue.calls.mostRecent().args[0].queueableFns;
|
||||||
|
expect(queueableFns.length).toBe(11);
|
||||||
|
|
||||||
|
for (let i = 0; i < 11; i++) {
|
||||||
|
queueableFns[i].fn('done');
|
||||||
|
expect(
|
||||||
|
jasmineUnderTest.TreeRunner.prototype._executeSpec
|
||||||
|
).toHaveBeenCalledWith(specs[i], 'done');
|
||||||
|
}
|
||||||
|
|
||||||
|
await expectAsync(promise).toBePending();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -33,357 +33,167 @@ describe('Spec', function() {
|
|||||||
expect(jasmineUnderTest.Spec.isPendingSpecException(void 0)).toBe(false);
|
expect(jasmineUnderTest.Spec.isPendingSpecException(void 0)).toBe(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('delegates execution to a QueueRunner', function() {
|
describe('#executionFinished', function() {
|
||||||
const fakeQueueRunner = jasmine.createSpy('fakeQueueRunner'),
|
it('removes the fn if autoCleanClosures is true', function() {
|
||||||
spec = new jasmineUnderTest.Spec({
|
const spec = new jasmineUnderTest.Spec({
|
||||||
description: 'my test',
|
queueableFn: { fn: () => {} },
|
||||||
id: 'some-id',
|
autoCleanClosures: true
|
||||||
queueableFn: { fn: function() {} }
|
|
||||||
});
|
});
|
||||||
|
|
||||||
spec.execute(fakeQueueRunner);
|
spec.executionFinished();
|
||||||
|
expect(spec.queueableFn.fn).toBeFalsy();
|
||||||
expect(fakeQueueRunner).toHaveBeenCalled();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should call the start callback on execution', function() {
|
|
||||||
const fakeQueueRunner = jasmine.createSpy('fakeQueueRunner'),
|
|
||||||
startCallback = jasmine.createSpy('startCallback'),
|
|
||||||
spec = new jasmineUnderTest.Spec({
|
|
||||||
id: 123,
|
|
||||||
description: 'foo bar',
|
|
||||||
queueableFn: { fn: function() {} },
|
|
||||||
onStart: startCallback
|
|
||||||
});
|
|
||||||
|
|
||||||
spec.execute(fakeQueueRunner);
|
|
||||||
|
|
||||||
fakeQueueRunner.calls.mostRecent().args[0].queueableFns[0].fn();
|
|
||||||
expect(startCallback).toHaveBeenCalled();
|
|
||||||
expect(startCallback.calls.first().object).toEqual(spec);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should call the start callback on execution but before any befores are called', function() {
|
|
||||||
const fakeQueueRunner = jasmine.createSpy('fakeQueueRunner');
|
|
||||||
let beforesWereCalled = false;
|
|
||||||
const startCallback = jasmine
|
|
||||||
.createSpy('start-callback')
|
|
||||||
.and.callFake(function() {
|
|
||||||
expect(beforesWereCalled).toBe(false);
|
|
||||||
});
|
|
||||||
const spec = new jasmineUnderTest.Spec({
|
|
||||||
queueableFn: { fn: function() {} },
|
|
||||||
beforeFns: function() {
|
|
||||||
return [
|
|
||||||
function() {
|
|
||||||
beforesWereCalled = true;
|
|
||||||
}
|
|
||||||
];
|
|
||||||
},
|
|
||||||
onStart: startCallback
|
|
||||||
});
|
});
|
||||||
|
|
||||||
spec.execute(fakeQueueRunner);
|
it('removes the fn after execution if autoCleanClosures is undefined', function() {
|
||||||
|
const spec = new jasmineUnderTest.Spec({
|
||||||
fakeQueueRunner.calls.mostRecent().args[0].queueableFns[0].fn();
|
queueableFn: { fn: () => {} },
|
||||||
expect(startCallback).toHaveBeenCalled();
|
autoCleanClosures: undefined
|
||||||
});
|
|
||||||
|
|
||||||
it('provides all before fns and after fns to be run', function() {
|
|
||||||
const fakeQueueRunner = jasmine.createSpy('fakeQueueRunner'),
|
|
||||||
before = jasmine.createSpy('before'),
|
|
||||||
after = jasmine.createSpy('after'),
|
|
||||||
queueableFn = {
|
|
||||||
fn: jasmine.createSpy('test body').and.callFake(function() {
|
|
||||||
expect(before).toHaveBeenCalled();
|
|
||||||
expect(after).not.toHaveBeenCalled();
|
|
||||||
})
|
|
||||||
},
|
|
||||||
spec = new jasmineUnderTest.Spec({
|
|
||||||
queueableFn: queueableFn,
|
|
||||||
beforeAndAfterFns: function() {
|
|
||||||
return { befores: [before], afters: [after] };
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
spec.execute(fakeQueueRunner);
|
spec.executionFinished();
|
||||||
|
expect(spec.queueableFn.fn).toBeFalsy();
|
||||||
const options = fakeQueueRunner.calls.mostRecent().args[0];
|
|
||||||
expect(options.queueableFns).toEqual([
|
|
||||||
{ fn: jasmine.any(Function) },
|
|
||||||
before,
|
|
||||||
queueableFn,
|
|
||||||
after,
|
|
||||||
{
|
|
||||||
fn: jasmine.any(Function),
|
|
||||||
type: 'specCleanup'
|
|
||||||
}
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("tells the queue runner that it's a leaf node", function() {
|
|
||||||
const fakeQueueRunner = jasmine.createSpy('fakeQueueRunner'),
|
|
||||||
spec = new jasmineUnderTest.Spec({
|
|
||||||
queueableFn: { fn: function() {} },
|
|
||||||
beforeAndAfterFns: function() {
|
|
||||||
return { befores: [], afters: [] };
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
spec.execute(fakeQueueRunner);
|
|
||||||
|
|
||||||
expect(fakeQueueRunner).toHaveBeenCalledWith(
|
|
||||||
jasmine.objectContaining({
|
|
||||||
isLeaf: true
|
|
||||||
})
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('is marked pending if created without a function body', function() {
|
|
||||||
const startCallback = jasmine.createSpy('startCallback'),
|
|
||||||
resultCallback = jasmine.createSpy('resultCallback'),
|
|
||||||
spec = new jasmineUnderTest.Spec({
|
|
||||||
onStart: startCallback,
|
|
||||||
queueableFn: { fn: null },
|
|
||||||
resultCallback: resultCallback
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(spec.status()).toBe('pending');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('can be excluded at execution time by a parent', function() {
|
|
||||||
const fakeQueueRunner = jasmine.createSpy('fakeQueueRunner'),
|
|
||||||
startCallback = jasmine.createSpy('startCallback'),
|
|
||||||
specBody = jasmine.createSpy('specBody'),
|
|
||||||
resultCallback = jasmine.createSpy('resultCallback'),
|
|
||||||
spec = new jasmineUnderTest.Spec({
|
|
||||||
onStart: startCallback,
|
|
||||||
queueableFn: { fn: specBody },
|
|
||||||
resultCallback: resultCallback
|
|
||||||
});
|
|
||||||
|
|
||||||
spec.execute(fakeQueueRunner, 'cally-back', true);
|
|
||||||
|
|
||||||
expect(fakeQueueRunner).toHaveBeenCalledWith(
|
|
||||||
jasmine.objectContaining({
|
|
||||||
onComplete: jasmine.any(Function),
|
|
||||||
queueableFns: [
|
|
||||||
{ fn: jasmine.any(Function) },
|
|
||||||
{
|
|
||||||
fn: jasmine.any(Function),
|
|
||||||
type: 'specCleanup'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
})
|
|
||||||
);
|
|
||||||
expect(specBody).not.toHaveBeenCalled();
|
|
||||||
|
|
||||||
const args = fakeQueueRunner.calls.mostRecent().args[0];
|
|
||||||
args.queueableFns[0].fn();
|
|
||||||
expect(startCallback).toHaveBeenCalled();
|
|
||||||
args.queueableFns[args.queueableFns.length - 1].fn();
|
|
||||||
expect(resultCallback).toHaveBeenCalled();
|
|
||||||
|
|
||||||
expect(spec.result.status).toBe('excluded');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('can be marked pending, but still calls callbacks when executed', function() {
|
|
||||||
const fakeQueueRunner = jasmine.createSpy('fakeQueueRunner'),
|
|
||||||
startCallback = jasmine.createSpy('startCallback'),
|
|
||||||
resultCallback = jasmine.createSpy('resultCallback'),
|
|
||||||
spec = new jasmineUnderTest.Spec({
|
|
||||||
onStart: startCallback,
|
|
||||||
resultCallback: resultCallback,
|
|
||||||
description: 'with a spec',
|
|
||||||
parentSuiteId: 'suite1',
|
|
||||||
filename: 'someSpecFile.js',
|
|
||||||
getPath: function() {
|
|
||||||
return ['a suite', 'with a spec'];
|
|
||||||
},
|
|
||||||
queueableFn: { fn: null }
|
|
||||||
});
|
|
||||||
|
|
||||||
spec.pend();
|
|
||||||
|
|
||||||
expect(spec.status()).toBe('pending');
|
|
||||||
|
|
||||||
spec.execute(fakeQueueRunner);
|
|
||||||
|
|
||||||
expect(fakeQueueRunner).toHaveBeenCalled();
|
|
||||||
|
|
||||||
const args = fakeQueueRunner.calls.mostRecent().args[0];
|
|
||||||
args.queueableFns[0].fn();
|
|
||||||
expect(startCallback).toHaveBeenCalled();
|
|
||||||
args.queueableFns[1].fn('things');
|
|
||||||
expect(resultCallback).toHaveBeenCalledWith(
|
|
||||||
{
|
|
||||||
id: spec.id,
|
|
||||||
status: 'pending',
|
|
||||||
description: 'with a spec',
|
|
||||||
fullName: 'a suite with a spec',
|
|
||||||
parentSuiteId: 'suite1',
|
|
||||||
filename: 'someSpecFile.js',
|
|
||||||
failedExpectations: [],
|
|
||||||
passedExpectations: [],
|
|
||||||
deprecationWarnings: [],
|
|
||||||
pendingReason: '',
|
|
||||||
duration: jasmine.any(Number),
|
|
||||||
properties: null,
|
|
||||||
debugLogs: null
|
|
||||||
},
|
|
||||||
'things'
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should call the done callback on execution complete', function() {
|
|
||||||
const done = jasmine.createSpy('done callback'),
|
|
||||||
spec = new jasmineUnderTest.Spec({
|
|
||||||
queueableFn: { fn: function() {} },
|
|
||||||
catchExceptions: function() {
|
|
||||||
return false;
|
|
||||||
},
|
|
||||||
resultCallback: function() {}
|
|
||||||
});
|
|
||||||
|
|
||||||
spec.execute(attrs => attrs.onComplete(), done);
|
|
||||||
|
|
||||||
expect(done).toHaveBeenCalled();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should call the done callback with an error if the spec is failed', function() {
|
|
||||||
const done = jasmine.createSpy('done callback'),
|
|
||||||
spec = new jasmineUnderTest.Spec({
|
|
||||||
queueableFn: { fn: function() {} },
|
|
||||||
catchExceptions: function() {
|
|
||||||
return false;
|
|
||||||
},
|
|
||||||
resultCallback: function() {}
|
|
||||||
});
|
|
||||||
|
|
||||||
function queueRunnerFactory(attrs) {
|
|
||||||
spec.result.status = 'failed';
|
|
||||||
attrs.onComplete();
|
|
||||||
}
|
|
||||||
spec.execute(queueRunnerFactory, done);
|
|
||||||
|
|
||||||
expect(done).toHaveBeenCalledWith(
|
|
||||||
jasmine.any(jasmineUnderTest.StopExecutionError)
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should report the duration of the test', function() {
|
|
||||||
const timer = jasmine.createSpyObj('timer', {
|
|
||||||
start: null,
|
|
||||||
elapsed: 77000
|
|
||||||
});
|
|
||||||
let duration = undefined;
|
|
||||||
const spec = new jasmineUnderTest.Spec({
|
|
||||||
queueableFn: { fn: jasmine.createSpy('spec body') },
|
|
||||||
catchExceptions: function() {
|
|
||||||
return false;
|
|
||||||
},
|
|
||||||
resultCallback: function(result) {
|
|
||||||
duration = result.duration;
|
|
||||||
},
|
|
||||||
timer: timer
|
|
||||||
});
|
});
|
||||||
|
|
||||||
function queueRunnerFactory(config) {
|
it('does not remove the fn after execution if autoCleanClosures is false', function() {
|
||||||
config.queueableFns.forEach(function(qf) {
|
function originalFn() {}
|
||||||
qf.fn();
|
const spec = new jasmineUnderTest.Spec({
|
||||||
|
queueableFn: { fn: originalFn },
|
||||||
|
autoCleanClosures: false
|
||||||
});
|
});
|
||||||
config.onComplete();
|
|
||||||
}
|
|
||||||
|
|
||||||
spec.execute(queueRunnerFactory, function() {});
|
spec.executionFinished();
|
||||||
expect(duration).toBe(77000);
|
expect(spec.queueableFn.fn).toBe(originalFn);
|
||||||
});
|
|
||||||
|
|
||||||
it('should report properties set during the test', function() {
|
|
||||||
const done = jasmine.createSpy('done callback'),
|
|
||||||
spec = new jasmineUnderTest.Spec({
|
|
||||||
queueableFn: { fn: jasmine.createSpy('spec body') },
|
|
||||||
catchExceptions: function() {
|
|
||||||
return false;
|
|
||||||
},
|
|
||||||
resultCallback: function() {}
|
|
||||||
});
|
|
||||||
spec.setSpecProperty('a', 4);
|
|
||||||
spec.execute(attrs => attrs.onComplete(), done);
|
|
||||||
expect(spec.result.properties).toEqual({ a: 4 });
|
|
||||||
});
|
|
||||||
|
|
||||||
it('#status returns passing by default', function() {
|
|
||||||
const spec = new jasmineUnderTest.Spec({
|
|
||||||
queueableFn: { fn: jasmine.createSpy('spec body') }
|
|
||||||
});
|
});
|
||||||
expect(spec.status()).toBe('passed');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('#status returns passed if all expectations in the spec have passed', function() {
|
describe('#getSpecProperty', function() {
|
||||||
const spec = new jasmineUnderTest.Spec({
|
it('get the property value', function() {
|
||||||
queueableFn: { fn: jasmine.createSpy('spec body') }
|
const spec = new jasmineUnderTest.Spec({
|
||||||
});
|
queueableFn: { fn: () => {} }
|
||||||
spec.addExpectationResult(true, {});
|
|
||||||
expect(spec.status()).toBe('passed');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('#status returns failed if any expectations in the spec have failed', function() {
|
|
||||||
const spec = new jasmineUnderTest.Spec({
|
|
||||||
queueableFn: { fn: jasmine.createSpy('spec body') }
|
|
||||||
});
|
|
||||||
spec.addExpectationResult(true, {});
|
|
||||||
spec.addExpectationResult(false, {});
|
|
||||||
expect(spec.status()).toBe('failed');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('keeps track of passed and failed expectations', function() {
|
|
||||||
const fakeQueueRunner = jasmine.createSpy('queueRunner'),
|
|
||||||
resultCallback = jasmine.createSpy('resultCallback'),
|
|
||||||
spec = new jasmineUnderTest.Spec({
|
|
||||||
queueableFn: { fn: jasmine.createSpy('spec body') },
|
|
||||||
resultCallback: resultCallback
|
|
||||||
});
|
|
||||||
spec.addExpectationResult(true, { message: 'expectation1' });
|
|
||||||
spec.addExpectationResult(false, { message: 'expectation2' });
|
|
||||||
|
|
||||||
spec.execute(fakeQueueRunner);
|
|
||||||
|
|
||||||
const fns = fakeQueueRunner.calls.mostRecent().args[0].queueableFns;
|
|
||||||
fns[fns.length - 1].fn();
|
|
||||||
|
|
||||||
expect(resultCallback.calls.first().args[0].passedExpectations).toEqual([
|
|
||||||
jasmine.objectContaining({ message: 'expectation1' })
|
|
||||||
]);
|
|
||||||
expect(resultCallback.calls.first().args[0].failedExpectations).toEqual([
|
|
||||||
jasmine.objectContaining({ message: 'expectation2' })
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("throws an ExpectationFailed error upon receiving a failed expectation when 'throwOnExpectationFailure' is set", function() {
|
|
||||||
const fakeQueueRunner = jasmine.createSpy('queueRunner'),
|
|
||||||
resultCallback = jasmine.createSpy('resultCallback'),
|
|
||||||
spec = new jasmineUnderTest.Spec({
|
|
||||||
queueableFn: { fn: function() {} },
|
|
||||||
resultCallback: resultCallback,
|
|
||||||
throwOnExpectationFailure: true
|
|
||||||
});
|
});
|
||||||
|
|
||||||
spec.addExpectationResult(true, { message: 'passed' });
|
spec.setSpecProperty('a', 4);
|
||||||
expect(function() {
|
expect(spec.getSpecProperty('a')).toBe(4);
|
||||||
spec.addExpectationResult(false, { message: 'failed' });
|
});
|
||||||
}).toThrowError(jasmineUnderTest.errors.ExpectationFailed);
|
});
|
||||||
|
|
||||||
spec.execute(fakeQueueRunner);
|
describe('#setSpecProperty', function() {
|
||||||
|
it('adds the property to the result', function() {
|
||||||
|
const spec = new jasmineUnderTest.Spec({
|
||||||
|
queueableFn: { fn: () => {} }
|
||||||
|
});
|
||||||
|
|
||||||
const fns = fakeQueueRunner.calls.mostRecent().args[0].queueableFns;
|
spec.setSpecProperty('a', 4);
|
||||||
fns[fns.length - 1].fn();
|
|
||||||
expect(resultCallback.calls.first().args[0].passedExpectations).toEqual([
|
expect(spec.result.properties).toEqual({ a: 4 });
|
||||||
jasmine.objectContaining({ message: 'passed' })
|
});
|
||||||
]);
|
|
||||||
expect(resultCallback.calls.first().args[0].failedExpectations).toEqual([
|
it('replace the property result when it was previously set', function() {
|
||||||
jasmine.objectContaining({ message: 'failed' })
|
const spec = new jasmineUnderTest.Spec({
|
||||||
]);
|
queueableFn: { fn: () => {} }
|
||||||
|
});
|
||||||
|
|
||||||
|
spec.setSpecProperty('a', 'original-value');
|
||||||
|
spec.setSpecProperty('b', 'original-value');
|
||||||
|
spec.setSpecProperty('a', 'new-value');
|
||||||
|
|
||||||
|
expect(spec.result.properties).toEqual({
|
||||||
|
a: 'new-value',
|
||||||
|
b: 'original-value'
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('status', function() {
|
||||||
|
it('is "passed" by default', function() {
|
||||||
|
const spec = new jasmineUnderTest.Spec({
|
||||||
|
queueableFn: { fn: () => {} }
|
||||||
|
});
|
||||||
|
expect(spec.getResult().status).toBe('passed');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('is "passed" if all expectations passed', function() {
|
||||||
|
const spec = new jasmineUnderTest.Spec({
|
||||||
|
queueableFn: { fn: () => {} }
|
||||||
|
});
|
||||||
|
|
||||||
|
spec.addExpectationResult(true, {});
|
||||||
|
|
||||||
|
expect(spec.getResult().status).toBe('passed');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('is "failed" if any expectation failed', function() {
|
||||||
|
const spec = new jasmineUnderTest.Spec({
|
||||||
|
queueableFn: { fn: () => {} }
|
||||||
|
});
|
||||||
|
|
||||||
|
spec.addExpectationResult(true, {});
|
||||||
|
spec.addExpectationResult(false, {});
|
||||||
|
|
||||||
|
expect(spec.getResult().status).toBe('failed');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('is "pending" if created without a function body', function() {
|
||||||
|
const startCallback = jasmine.createSpy('startCallback'),
|
||||||
|
resultCallback = jasmine.createSpy('resultCallback'),
|
||||||
|
spec = new jasmineUnderTest.Spec({
|
||||||
|
onStart: startCallback,
|
||||||
|
queueableFn: { fn: null },
|
||||||
|
resultCallback: resultCallback
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(spec.getResult().status).toBe('pending');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('#addExpectationResult', function() {
|
||||||
|
it('keeps track of passed and failed expectations', function() {
|
||||||
|
const spec = new jasmineUnderTest.Spec({
|
||||||
|
queueableFn: { fn: () => {} }
|
||||||
|
});
|
||||||
|
|
||||||
|
spec.addExpectationResult(true, { message: 'expectation1' });
|
||||||
|
spec.addExpectationResult(false, { message: 'expectation2' });
|
||||||
|
|
||||||
|
expect(spec.result.passedExpectations).toEqual([
|
||||||
|
jasmine.objectContaining({ message: 'expectation1' })
|
||||||
|
]);
|
||||||
|
expect(spec.result.failedExpectations).toEqual([
|
||||||
|
jasmine.objectContaining({ message: 'expectation2' })
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("when 'throwOnExpectationFailure' is set", function() {
|
||||||
|
it('throws an ExpectationFailed error', function() {
|
||||||
|
const spec = new jasmineUnderTest.Spec({
|
||||||
|
queueableFn: { fn: () => {} },
|
||||||
|
throwOnExpectationFailure: true
|
||||||
|
});
|
||||||
|
|
||||||
|
spec.addExpectationResult(true, { message: 'passed' });
|
||||||
|
expect(function() {
|
||||||
|
spec.addExpectationResult(false, { message: 'failed' });
|
||||||
|
}).toThrowError(jasmineUnderTest.errors.ExpectationFailed);
|
||||||
|
|
||||||
|
expect(spec.result.failedExpectations).toEqual([
|
||||||
|
jasmine.objectContaining({ message: 'failed' })
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("when 'throwOnExpectationFailure' is not set", function() {
|
||||||
|
it('does not throw', function() {
|
||||||
|
const spec = new jasmineUnderTest.Spec({
|
||||||
|
queueableFn: { fn: () => {} }
|
||||||
|
});
|
||||||
|
|
||||||
|
spec.addExpectationResult(false, { message: 'failed' });
|
||||||
|
|
||||||
|
expect(spec.result.failedExpectations).toEqual([
|
||||||
|
jasmine.objectContaining({ message: 'failed' })
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('forwards late expectation failures to onLateError', function() {
|
it('forwards late expectation failures to onLateError', function() {
|
||||||
@@ -514,125 +324,47 @@ describe('Spec', function() {
|
|||||||
expect(spec.metadata.getPath()).toEqual(['expected val']);
|
expect(spec.metadata.getPath()).toEqual(['expected val']);
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('when a spec is marked pending during execution', function() {
|
describe('#handleException', function() {
|
||||||
it('should mark the spec as pending', function() {
|
it('records a failure', function() {
|
||||||
const fakeQueueRunner = function(opts) {
|
const spec = new jasmineUnderTest.Spec({
|
||||||
opts.onException(
|
queueableFn: {}
|
||||||
new Error(jasmineUnderTest.Spec.pendingSpecExceptionMessage)
|
|
||||||
);
|
|
||||||
},
|
|
||||||
spec = new jasmineUnderTest.Spec({
|
|
||||||
description: 'my test',
|
|
||||||
id: 'some-id',
|
|
||||||
queueableFn: { fn: function() {} }
|
|
||||||
});
|
|
||||||
|
|
||||||
spec.execute(fakeQueueRunner);
|
|
||||||
|
|
||||||
expect(spec.status()).toEqual('pending');
|
|
||||||
expect(spec.result.pendingReason).toEqual('');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should set the pendingReason', function() {
|
|
||||||
const fakeQueueRunner = function(opts) {
|
|
||||||
opts.onException(
|
|
||||||
new Error(
|
|
||||||
jasmineUnderTest.Spec.pendingSpecExceptionMessage +
|
|
||||||
'custom message'
|
|
||||||
)
|
|
||||||
);
|
|
||||||
},
|
|
||||||
spec = new jasmineUnderTest.Spec({
|
|
||||||
description: 'my test',
|
|
||||||
id: 'some-id',
|
|
||||||
queueableFn: { fn: function() {} }
|
|
||||||
});
|
|
||||||
|
|
||||||
spec.execute(fakeQueueRunner);
|
|
||||||
|
|
||||||
expect(spec.status()).toEqual('pending');
|
|
||||||
expect(spec.result.pendingReason).toEqual('custom message');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should log a failure when handling an exception', function() {
|
|
||||||
const fakeQueueRunner = jasmine.createSpy('queueRunner'),
|
|
||||||
resultCallback = jasmine.createSpy('resultCallback'),
|
|
||||||
spec = new jasmineUnderTest.Spec({
|
|
||||||
queueableFn: { fn: function() {} },
|
|
||||||
resultCallback: resultCallback
|
|
||||||
});
|
});
|
||||||
|
|
||||||
spec.handleException('foo');
|
spec.handleException('foo');
|
||||||
spec.execute(fakeQueueRunner);
|
|
||||||
|
|
||||||
const args = fakeQueueRunner.calls.mostRecent().args[0];
|
expect(spec.result.failedExpectations).toEqual([
|
||||||
args.queueableFns[args.queueableFns.length - 1].fn();
|
{
|
||||||
expect(resultCallback.calls.first().args[0].failedExpectations).toEqual([
|
message: 'foo thrown',
|
||||||
{
|
matcherName: '',
|
||||||
message: 'foo thrown',
|
passed: false,
|
||||||
matcherName: '',
|
expected: '',
|
||||||
passed: false,
|
actual: '',
|
||||||
expected: '',
|
stack: null
|
||||||
actual: '',
|
|
||||||
stack: null
|
|
||||||
}
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should not log an additional failure when handling an ExpectationFailed error', function() {
|
|
||||||
const fakeQueueRunner = jasmine.createSpy('queueRunner'),
|
|
||||||
resultCallback = jasmine.createSpy('resultCallback'),
|
|
||||||
spec = new jasmineUnderTest.Spec({
|
|
||||||
queueableFn: { fn: function() {} },
|
|
||||||
resultCallback: resultCallback
|
|
||||||
});
|
|
||||||
|
|
||||||
spec.handleException(new jasmineUnderTest.errors.ExpectationFailed());
|
|
||||||
spec.execute(fakeQueueRunner);
|
|
||||||
|
|
||||||
const args = fakeQueueRunner.calls.mostRecent().args[0];
|
|
||||||
args.queueableFns[args.queueableFns.length - 1].fn();
|
|
||||||
expect(resultCallback.calls.first().args[0].failedExpectations).toEqual([]);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('treats multiple done calls as late errors', function() {
|
|
||||||
const queueRunnerFactory = jasmine.createSpy('queueRunnerFactory'),
|
|
||||||
onLateError = jasmine.createSpy('onLateError'),
|
|
||||||
spec = new jasmineUnderTest.Spec({
|
|
||||||
onLateError: onLateError,
|
|
||||||
queueableFn: { fn: function() {} },
|
|
||||||
getPath: function() {
|
|
||||||
return ['a spec'];
|
|
||||||
}
|
}
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does not record an additional failure when the error is ExpectationFailed', function() {
|
||||||
|
const spec = new jasmineUnderTest.Spec({
|
||||||
|
queueableFn: {}
|
||||||
});
|
});
|
||||||
|
|
||||||
spec.execute(queueRunnerFactory);
|
spec.handleException(new jasmineUnderTest.errors.ExpectationFailed());
|
||||||
|
|
||||||
expect(queueRunnerFactory).toHaveBeenCalled();
|
expect(spec.result.failedExpectations).toEqual([]);
|
||||||
queueRunnerFactory.calls.argsFor(0)[0].onMultipleDone();
|
});
|
||||||
|
|
||||||
expect(onLateError).toHaveBeenCalledTimes(1);
|
|
||||||
expect(onLateError.calls.argsFor(0)[0]).toBeInstanceOf(Error);
|
|
||||||
expect(onLateError.calls.argsFor(0)[0].message).toEqual(
|
|
||||||
'An asynchronous spec, beforeEach, or afterEach function called its ' +
|
|
||||||
"'done' callback more than once.\n(in spec: a spec)"
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('#trace', function() {
|
describe('#debugLog', function() {
|
||||||
it('adds the messages to the result', function() {
|
it('adds the messages to the result', function() {
|
||||||
const timer = jasmine.createSpyObj('timer', ['start', 'elapsed']),
|
const timer = jasmine.createSpyObj('timer', ['start', 'elapsed']);
|
||||||
spec = new jasmineUnderTest.Spec({
|
const spec = new jasmineUnderTest.Spec({
|
||||||
queueableFn: {
|
queueableFn: { fn: () => {} },
|
||||||
fn: function() {}
|
timer: timer
|
||||||
},
|
});
|
||||||
timer: timer
|
const t1 = 123;
|
||||||
}),
|
const t2 = 456;
|
||||||
t1 = 123,
|
|
||||||
t2 = 456;
|
|
||||||
|
|
||||||
spec.execute(() => {});
|
|
||||||
expect(spec.result.debugLogs).toBeNull();
|
expect(spec.result.debugLogs).toBeNull();
|
||||||
timer.elapsed.and.returnValue(t1);
|
timer.elapsed.and.returnValue(t1);
|
||||||
spec.debugLog('msg 1');
|
spec.debugLog('msg 1');
|
||||||
@@ -648,84 +380,36 @@ describe('Spec', function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('When the spec passes', function() {
|
describe('When the spec passes', function() {
|
||||||
it('omits the messages from the reported result', function() {
|
it('removes the logs from the result', function() {
|
||||||
const resultCallback = jasmine.createSpy('resultCallback'),
|
const spec = new jasmineUnderTest.Spec({
|
||||||
spec = new jasmineUnderTest.Spec({
|
queueableFn: { fn: () => {} }
|
||||||
queueableFn: {
|
});
|
||||||
fn: function() {}
|
|
||||||
},
|
|
||||||
resultCallback: resultCallback
|
|
||||||
});
|
|
||||||
|
|
||||||
function queueRunnerFactory(config) {
|
spec.debugLog('msg');
|
||||||
spec.debugLog('msg');
|
spec.executionFinished();
|
||||||
for (const fn of config.queueableFns) {
|
|
||||||
fn.fn();
|
|
||||||
}
|
|
||||||
config.onComplete(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
spec.execute(queueRunnerFactory, function() {});
|
|
||||||
expect(resultCallback).toHaveBeenCalledWith(
|
|
||||||
jasmine.objectContaining({ debugLogs: null }),
|
|
||||||
undefined
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('removes the messages to save memory', function() {
|
|
||||||
const resultCallback = jasmine.createSpy('resultCallback'),
|
|
||||||
spec = new jasmineUnderTest.Spec({
|
|
||||||
queueableFn: {
|
|
||||||
fn: function() {}
|
|
||||||
},
|
|
||||||
resultCallback: resultCallback
|
|
||||||
});
|
|
||||||
|
|
||||||
function queueRunnerFactory(config) {
|
|
||||||
spec.debugLog('msg');
|
|
||||||
for (const fn of config.queueableFns) {
|
|
||||||
fn.fn();
|
|
||||||
}
|
|
||||||
config.onComplete(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
spec.execute(queueRunnerFactory, function() {});
|
|
||||||
expect(resultCallback).toHaveBeenCalled();
|
|
||||||
expect(spec.result.debugLogs).toBeNull();
|
expect(spec.result.debugLogs).toBeNull();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('When the spec fails', function() {
|
describe('When the spec fails', function() {
|
||||||
it('includes the messages in the reported result', function() {
|
it('includes the messages in the result', function() {
|
||||||
const resultCallback = jasmine.createSpy('resultCallback'),
|
const timer = jasmine.createSpyObj('timer', ['start', 'elapsed']);
|
||||||
timer = jasmine.createSpyObj('timer', ['start', 'elapsed']),
|
const spec = new jasmineUnderTest.Spec({
|
||||||
spec = new jasmineUnderTest.Spec({
|
queueableFn: { fn: () => {} },
|
||||||
queueableFn: {
|
timer: timer
|
||||||
fn: function() {}
|
});
|
||||||
},
|
const timestamp = 12345;
|
||||||
resultCallback: resultCallback,
|
|
||||||
timer: timer
|
|
||||||
}),
|
|
||||||
timestamp = 12345;
|
|
||||||
|
|
||||||
timer.elapsed.and.returnValue(timestamp);
|
timer.elapsed.and.returnValue(timestamp);
|
||||||
|
|
||||||
function queueRunnerFactory(config) {
|
spec.debugLog('msg');
|
||||||
spec.debugLog('msg');
|
spec.handleException(new Error('nope'));
|
||||||
spec.handleException(new Error('nope'));
|
spec.executionFinished();
|
||||||
for (const fn of config.queueableFns) {
|
|
||||||
fn.fn();
|
|
||||||
}
|
|
||||||
config.onComplete(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
spec.execute(queueRunnerFactory, function() {});
|
expect(spec.result.debugLogs).toEqual([
|
||||||
expect(resultCallback).toHaveBeenCalledWith(
|
{ message: 'msg', timestamp: timestamp }
|
||||||
jasmine.objectContaining({
|
]);
|
||||||
debugLogs: [{ message: 'msg', timestamp: timestamp }]
|
|
||||||
}),
|
|
||||||
undefined
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -153,7 +153,7 @@ describe('Spies', function() {
|
|||||||
it('should throw if you do not pass an array or object argument', function() {
|
it('should throw if you do not pass an array or object argument', function() {
|
||||||
expect(function() {
|
expect(function() {
|
||||||
env.createSpyObj('BaseName');
|
env.createSpyObj('BaseName');
|
||||||
}).toThrow(
|
}).toThrowError(
|
||||||
'createSpyObj requires a non-empty array or object of method names to create spies for'
|
'createSpyObj requires a non-empty array or object of method names to create spies for'
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@@ -161,7 +161,7 @@ describe('Spies', function() {
|
|||||||
it('should throw if you pass an empty array argument', function() {
|
it('should throw if you pass an empty array argument', function() {
|
||||||
expect(function() {
|
expect(function() {
|
||||||
env.createSpyObj('BaseName', []);
|
env.createSpyObj('BaseName', []);
|
||||||
}).toThrow(
|
}).toThrowError(
|
||||||
'createSpyObj requires a non-empty array or object of method names to create spies for'
|
'createSpyObj requires a non-empty array or object of method names to create spies for'
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@@ -169,7 +169,7 @@ describe('Spies', function() {
|
|||||||
it('should throw if you pass an empty object argument', function() {
|
it('should throw if you pass an empty object argument', function() {
|
||||||
expect(function() {
|
expect(function() {
|
||||||
env.createSpyObj('BaseName', {});
|
env.createSpyObj('BaseName', {});
|
||||||
}).toThrow(
|
}).toThrowError(
|
||||||
'createSpyObj requires a non-empty array or object of method names to create spies for'
|
'createSpyObj requires a non-empty array or object of method names to create spies for'
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -115,7 +115,7 @@ describe('Suite', function() {
|
|||||||
const suite = new jasmineUnderTest.Suite({});
|
const suite = new jasmineUnderTest.Suite({});
|
||||||
|
|
||||||
suite.addExpectationResult(false, {});
|
suite.addExpectationResult(false, {});
|
||||||
expect(suite.status()).toBe('failed');
|
expect(suite.getResult().status).toBe('failed');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('retrieves a result with updated status', function() {
|
it('retrieves a result with updated status', function() {
|
||||||
@@ -140,7 +140,7 @@ describe('Suite', function() {
|
|||||||
suite.addExpectationResult(false, { message: 'failed' });
|
suite.addExpectationResult(false, { message: 'failed' });
|
||||||
}).toThrowError(jasmineUnderTest.errors.ExpectationFailed);
|
}).toThrowError(jasmineUnderTest.errors.ExpectationFailed);
|
||||||
|
|
||||||
expect(suite.status()).toBe('failed');
|
expect(suite.getResult().status).toBe('failed');
|
||||||
expect(suite.result.failedExpectations).toEqual([
|
expect(suite.result.failedExpectations).toEqual([
|
||||||
jasmine.objectContaining({ message: 'failed' })
|
jasmine.objectContaining({ message: 'failed' })
|
||||||
]);
|
]);
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
717
spec/core/TreeRunnerSpec.js
Normal file
717
spec/core/TreeRunnerSpec.js
Normal file
@@ -0,0 +1,717 @@
|
|||||||
|
describe('TreeRunner', function() {
|
||||||
|
describe('spec execution', function() {
|
||||||
|
it('starts the timer, reports the spec started, and updates run state at the start of the queue', async function() {
|
||||||
|
const timer = jasmine.createSpyObj('timer', ['start']);
|
||||||
|
const spec = new jasmineUnderTest.Spec({
|
||||||
|
id: 'spec1',
|
||||||
|
queueableFn: {},
|
||||||
|
timer
|
||||||
|
});
|
||||||
|
const {
|
||||||
|
runQueue,
|
||||||
|
currentRunableTracker,
|
||||||
|
runableResources,
|
||||||
|
reportDispatcher,
|
||||||
|
suiteRunQueueArgs,
|
||||||
|
executePromise
|
||||||
|
} = runSingleSpecSuite(spec);
|
||||||
|
suiteRunQueueArgs.queueableFns[0].fn();
|
||||||
|
|
||||||
|
expect(runQueue).toHaveBeenCalledTimes(1);
|
||||||
|
const specRunQueueArgs = runQueue.calls.mostRecent().args[0];
|
||||||
|
const next = jasmine.createSpy('next');
|
||||||
|
specRunQueueArgs.queueableFns[0].fn(next);
|
||||||
|
|
||||||
|
expect(timer.start).toHaveBeenCalled();
|
||||||
|
expect(currentRunableTracker.currentRunable()).toBe(spec);
|
||||||
|
expect(runableResources.initForRunable).toHaveBeenCalledWith(
|
||||||
|
spec.id,
|
||||||
|
spec.parentSuiteId
|
||||||
|
);
|
||||||
|
expect(reportDispatcher.specStarted).toHaveBeenCalledWith(spec.result);
|
||||||
|
await Promise.resolve();
|
||||||
|
expect(reportDispatcher.specStarted).toHaveBeenCalledBefore(next);
|
||||||
|
await expectAsync(executePromise).toBePending();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('stops the timer, updates run state, and reports the spec done at the end of the queue', async function() {
|
||||||
|
const timer = jasmine.createSpyObj('timer', ['start', 'elapsed']);
|
||||||
|
const spec = new jasmineUnderTest.Spec({
|
||||||
|
id: 'spec1',
|
||||||
|
queueableFn: {},
|
||||||
|
timer
|
||||||
|
});
|
||||||
|
const {
|
||||||
|
runQueue,
|
||||||
|
currentRunableTracker,
|
||||||
|
runableResources,
|
||||||
|
reportDispatcher,
|
||||||
|
suiteRunQueueArgs,
|
||||||
|
executePromise
|
||||||
|
} = runSingleSpecSuite(spec);
|
||||||
|
|
||||||
|
suiteRunQueueArgs.queueableFns[0].fn();
|
||||||
|
|
||||||
|
expect(runQueue).toHaveBeenCalledTimes(1);
|
||||||
|
const specRunQueueArgs = runQueue.calls.mostRecent().args[0];
|
||||||
|
const next = jasmine.createSpy('next');
|
||||||
|
timer.elapsed.and.returnValue('the elapsed time');
|
||||||
|
currentRunableTracker.setCurrentSpec(spec);
|
||||||
|
specRunQueueArgs.queueableFns[1].fn(next);
|
||||||
|
|
||||||
|
expect(currentRunableTracker.currentSpec()).toBeFalsy();
|
||||||
|
expect(runableResources.clearForRunable).toHaveBeenCalledWith(spec.id);
|
||||||
|
expect(reportDispatcher.specDone).toHaveBeenCalledWith(spec.result);
|
||||||
|
expect(spec.result.duration).toEqual('the elapsed time');
|
||||||
|
expect(spec.reportedDone).toEqual(true);
|
||||||
|
await Promise.resolve();
|
||||||
|
await Promise.resolve();
|
||||||
|
await Promise.resolve();
|
||||||
|
expect(reportDispatcher.specDone).toHaveBeenCalledBefore(next);
|
||||||
|
await expectAsync(executePromise).toBePending();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('runs before and after fns', function() {
|
||||||
|
const before = { fn: jasmine.createSpy('before') };
|
||||||
|
const after = { fn: jasmine.createSpy('after') };
|
||||||
|
const queueableFn = {
|
||||||
|
fn: jasmine.createSpy('test body').and.callFake(function() {
|
||||||
|
expect(before).toHaveBeenCalled();
|
||||||
|
expect(after).not.toHaveBeenCalled();
|
||||||
|
})
|
||||||
|
};
|
||||||
|
const spec = new jasmineUnderTest.Spec({
|
||||||
|
queueableFn: queueableFn,
|
||||||
|
beforeAndAfterFns: function() {
|
||||||
|
return { befores: [before], afters: [after] };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const { runQueue, suiteRunQueueArgs } = runSingleSpecSuite(spec);
|
||||||
|
suiteRunQueueArgs.queueableFns[0].fn();
|
||||||
|
expect(runQueue).toHaveBeenCalledTimes(1);
|
||||||
|
const specRunQueueArgs = runQueue.calls.mostRecent().args[0];
|
||||||
|
|
||||||
|
expect(specRunQueueArgs.queueableFns[1]).toEqual(before);
|
||||||
|
expect(specRunQueueArgs.queueableFns[2]).toEqual(queueableFn);
|
||||||
|
expect(specRunQueueArgs.queueableFns[3]).toEqual(after);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('marks specs pending at runtime', function() {
|
||||||
|
let spec;
|
||||||
|
const queueableFn = {
|
||||||
|
fn() {
|
||||||
|
spec.pend();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
spec = new jasmineUnderTest.Spec({ queueableFn });
|
||||||
|
|
||||||
|
const { runQueue, suiteRunQueueArgs } = runSingleSpecSuite(spec);
|
||||||
|
suiteRunQueueArgs.queueableFns[0].fn();
|
||||||
|
expect(runQueue).toHaveBeenCalledTimes(1);
|
||||||
|
const specRunQueueArgs = runQueue.calls.mostRecent().args[0];
|
||||||
|
|
||||||
|
expect(specRunQueueArgs.queueableFns[1]).toEqual(queueableFn);
|
||||||
|
queueableFn.fn();
|
||||||
|
|
||||||
|
expect(spec.getResult().status).toEqual('pending');
|
||||||
|
expect(spec.getResult().pendingReason).toEqual('');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('marks specs pending at runtime with a message', function() {
|
||||||
|
let spec;
|
||||||
|
const queueableFn = {
|
||||||
|
fn() {
|
||||||
|
spec.pend('some reason');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
spec = new jasmineUnderTest.Spec({ queueableFn });
|
||||||
|
|
||||||
|
const { runQueue, suiteRunQueueArgs } = runSingleSpecSuite(spec);
|
||||||
|
suiteRunQueueArgs.queueableFns[0].fn();
|
||||||
|
expect(runQueue).toHaveBeenCalledTimes(1);
|
||||||
|
const specRunQueueArgs = runQueue.calls.mostRecent().args[0];
|
||||||
|
|
||||||
|
expect(specRunQueueArgs.queueableFns[1]).toEqual(queueableFn);
|
||||||
|
queueableFn.fn();
|
||||||
|
|
||||||
|
expect(spec.getResult().status).toEqual('pending');
|
||||||
|
expect(spec.getResult().pendingReason).toEqual('some reason');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('passes failSpecWithNoExp to Spec#executionFinished', async function() {
|
||||||
|
const spec = new jasmineUnderTest.Spec({
|
||||||
|
id: 'spec1',
|
||||||
|
queueableFn: {}
|
||||||
|
});
|
||||||
|
spyOn(spec, 'executionFinished');
|
||||||
|
const {
|
||||||
|
runQueue,
|
||||||
|
suiteRunQueueArgs,
|
||||||
|
executePromise
|
||||||
|
} = runSingleSpecSuite(spec, { failSpecWithNoExpectations: true });
|
||||||
|
|
||||||
|
suiteRunQueueArgs.queueableFns[0].fn();
|
||||||
|
|
||||||
|
expect(runQueue).toHaveBeenCalledTimes(1);
|
||||||
|
const specRunQueueArgs = runQueue.calls.mostRecent().args[0];
|
||||||
|
expect(specRunQueueArgs.queueableFns[1].type).toEqual('specCleanup');
|
||||||
|
specRunQueueArgs.queueableFns[1].fn();
|
||||||
|
|
||||||
|
expect(spec.executionFinished).toHaveBeenCalledWith(false, true);
|
||||||
|
await expectAsync(executePromise).toBePending();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Suite execution', function() {
|
||||||
|
it('reports the duration of the suite', async function() {
|
||||||
|
const timer = jasmine.createSpyObj('timer', ['start', 'elapsed']);
|
||||||
|
const topSuite = new jasmineUnderTest.Suite({ id: 'topSuite' });
|
||||||
|
const suite = new jasmineUnderTest.Suite({
|
||||||
|
id: 'suite1',
|
||||||
|
parentSuite: topSuite,
|
||||||
|
timer
|
||||||
|
});
|
||||||
|
topSuite.addChild(suite);
|
||||||
|
const executionTree = {
|
||||||
|
topSuite,
|
||||||
|
childrenOfTopSuite() {
|
||||||
|
return [{ suite }];
|
||||||
|
},
|
||||||
|
childrenOfSuiteSegment() {
|
||||||
|
return [];
|
||||||
|
},
|
||||||
|
isExcluded() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const runQueue = jasmine.createSpy('runQueue');
|
||||||
|
const reportDispatcher = mockReportDispatcher();
|
||||||
|
const subject = new jasmineUnderTest.TreeRunner({
|
||||||
|
executionTree,
|
||||||
|
runQueue,
|
||||||
|
globalErrors: mockGlobalErrors(),
|
||||||
|
runableResources: mockRunableResources(),
|
||||||
|
reportDispatcher,
|
||||||
|
currentRunableTracker: new jasmineUnderTest.CurrentRunableTracker(),
|
||||||
|
getConfig() {
|
||||||
|
return {};
|
||||||
|
},
|
||||||
|
reportChildrenOfBeforeAllFailure() {}
|
||||||
|
});
|
||||||
|
|
||||||
|
const executePromise = subject.execute();
|
||||||
|
expect(runQueue).toHaveBeenCalledTimes(1);
|
||||||
|
const topSuiteRunQueueOpts = runQueue.calls.mostRecent().args[0];
|
||||||
|
runQueue.calls.reset();
|
||||||
|
topSuiteRunQueueOpts.queueableFns[0].fn(function() {});
|
||||||
|
|
||||||
|
expect(runQueue).toHaveBeenCalledTimes(1);
|
||||||
|
expect(timer.start).not.toHaveBeenCalled();
|
||||||
|
const suiteRunQueueOpts = runQueue.calls.mostRecent().args[0];
|
||||||
|
suiteRunQueueOpts.queueableFns[0].fn();
|
||||||
|
expect(timer.start).toHaveBeenCalled();
|
||||||
|
expect(timer.elapsed).not.toHaveBeenCalled();
|
||||||
|
|
||||||
|
timer.elapsed.and.returnValue('the duration');
|
||||||
|
suiteRunQueueOpts.onComplete();
|
||||||
|
expect(timer.elapsed).toHaveBeenCalled();
|
||||||
|
const result = suite.getResult();
|
||||||
|
expect(result.duration).toEqual('the duration');
|
||||||
|
expect(reportDispatcher.suiteDone).toHaveBeenCalledWith(result);
|
||||||
|
|
||||||
|
await expectAsync(executePromise).toBePending();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns false if a suite failed', async function() {
|
||||||
|
const topSuite = new jasmineUnderTest.Suite({ id: 'topSuite' });
|
||||||
|
const failingSuite = new jasmineUnderTest.Suite({
|
||||||
|
id: 'failingSuite',
|
||||||
|
parentSuite: topSuite
|
||||||
|
});
|
||||||
|
const passingSuite = new jasmineUnderTest.Suite({
|
||||||
|
id: 'passingSuite',
|
||||||
|
parentSuite: topSuite
|
||||||
|
});
|
||||||
|
const executionTree = {
|
||||||
|
topSuite,
|
||||||
|
childrenOfTopSuite() {
|
||||||
|
return [{ suite: failingSuite }, { suite: passingSuite }];
|
||||||
|
},
|
||||||
|
childrenOfSuiteSegment() {
|
||||||
|
return [];
|
||||||
|
},
|
||||||
|
isExcluded() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const runQueue = jasmine.createSpy('runQueue');
|
||||||
|
const reportDispatcher = mockReportDispatcher();
|
||||||
|
const subject = new jasmineUnderTest.TreeRunner({
|
||||||
|
executionTree,
|
||||||
|
runQueue,
|
||||||
|
globalErrors: mockGlobalErrors(),
|
||||||
|
runableResources: mockRunableResources(),
|
||||||
|
reportDispatcher,
|
||||||
|
currentRunableTracker: new jasmineUnderTest.CurrentRunableTracker(),
|
||||||
|
getConfig() {
|
||||||
|
return {};
|
||||||
|
},
|
||||||
|
reportChildrenOfBeforeAllFailure() {}
|
||||||
|
});
|
||||||
|
|
||||||
|
const executePromise = subject.execute();
|
||||||
|
expect(runQueue).toHaveBeenCalledTimes(1);
|
||||||
|
const topSuiteRunQueueOpts = runQueue.calls.mostRecent().args[0];
|
||||||
|
runQueue.calls.reset();
|
||||||
|
topSuiteRunQueueOpts.queueableFns[0].fn(function() {});
|
||||||
|
|
||||||
|
// Fail the first suite.
|
||||||
|
expect(runQueue).toHaveBeenCalledTimes(1);
|
||||||
|
const failingSuiteRunQueueOpts = runQueue.calls.mostRecent().args[0];
|
||||||
|
runQueue.calls.reset();
|
||||||
|
failingSuiteRunQueueOpts.queueableFns[0].fn();
|
||||||
|
failingSuite.addExpectationResult(false, {});
|
||||||
|
failingSuiteRunQueueOpts.onComplete();
|
||||||
|
|
||||||
|
// Passing the second suite should not reset the overall result.
|
||||||
|
topSuiteRunQueueOpts.queueableFns[1].fn(function() {});
|
||||||
|
expect(runQueue).toHaveBeenCalledTimes(1);
|
||||||
|
const passingSuiteRunQueueOpts = runQueue.calls.mostRecent().args[0];
|
||||||
|
passingSuiteRunQueueOpts.queueableFns[0].fn();
|
||||||
|
passingSuiteRunQueueOpts.onComplete();
|
||||||
|
|
||||||
|
topSuiteRunQueueOpts.onComplete();
|
||||||
|
|
||||||
|
const result = await executePromise;
|
||||||
|
expect(result.hasFailures).toEqual(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('reports children when there is a beforeAll failure', async function() {
|
||||||
|
const topSuite = new jasmineUnderTest.Suite({ id: 'topSuite' });
|
||||||
|
const suite = new jasmineUnderTest.Suite({
|
||||||
|
id: 'suite',
|
||||||
|
parentSuite: topSuite
|
||||||
|
});
|
||||||
|
suite.beforeAll({ fn() {} });
|
||||||
|
const spec = new jasmineUnderTest.Spec({
|
||||||
|
id: 'spec',
|
||||||
|
parentSuite: suite,
|
||||||
|
queueableFn: { fn() {} }
|
||||||
|
});
|
||||||
|
suite.addChild(spec);
|
||||||
|
topSuite.addChild(suite);
|
||||||
|
const executionTree = {
|
||||||
|
topSuite,
|
||||||
|
childrenOfTopSuite() {
|
||||||
|
return [{ suite }];
|
||||||
|
},
|
||||||
|
childrenOfSuiteSegment() {
|
||||||
|
return [{ spec }];
|
||||||
|
},
|
||||||
|
isExcluded() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const runQueue = jasmine.createSpy('runQueue');
|
||||||
|
const reportDispatcher = mockReportDispatcher();
|
||||||
|
const reportChildrenOfBeforeAllFailure = jasmine
|
||||||
|
.createSpy('reportChildrenOfBeforeAllFailure')
|
||||||
|
.and.returnValue(Promise.resolve());
|
||||||
|
const subject = new jasmineUnderTest.TreeRunner({
|
||||||
|
executionTree,
|
||||||
|
runQueue,
|
||||||
|
globalErrors: mockGlobalErrors(),
|
||||||
|
runableResources: mockRunableResources(),
|
||||||
|
reportDispatcher,
|
||||||
|
currentRunableTracker: new jasmineUnderTest.CurrentRunableTracker(),
|
||||||
|
reportChildrenOfBeforeAllFailure,
|
||||||
|
getConfig() {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const executePromise = subject.execute();
|
||||||
|
expect(runQueue).toHaveBeenCalledTimes(1);
|
||||||
|
const topSuiteRunQueueOpts = runQueue.calls.mostRecent().args[0];
|
||||||
|
runQueue.calls.reset();
|
||||||
|
topSuiteRunQueueOpts.queueableFns[0].fn(function() {});
|
||||||
|
|
||||||
|
expect(runQueue).toHaveBeenCalledTimes(1);
|
||||||
|
const suiteRunQueueOpts = runQueue.calls.mostRecent().args[0];
|
||||||
|
suiteRunQueueOpts.queueableFns[0].fn();
|
||||||
|
suite.hadBeforeAllFailure = true;
|
||||||
|
suiteRunQueueOpts.onComplete();
|
||||||
|
|
||||||
|
while (reportDispatcher.suiteDone.calls.count() === 0) {
|
||||||
|
await Promise.resolve();
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(reportDispatcher.specDone).toHaveBeenCalledBefore(
|
||||||
|
reportDispatcher.suiteDone
|
||||||
|
);
|
||||||
|
await expectAsync(executePromise).toBePending();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('throws if the wrong suite is completed', async function() {
|
||||||
|
const topSuite = new jasmineUnderTest.Suite({ id: 'topSuite' });
|
||||||
|
const suite = new jasmineUnderTest.Suite({
|
||||||
|
id: 'suite',
|
||||||
|
parentSuite: topSuite
|
||||||
|
});
|
||||||
|
const spec = new jasmineUnderTest.Spec({
|
||||||
|
id: 'spec',
|
||||||
|
parentSuite: suite,
|
||||||
|
queueableFn: { fn() {} }
|
||||||
|
});
|
||||||
|
const executionTree = {
|
||||||
|
topSuite,
|
||||||
|
childrenOfTopSuite() {
|
||||||
|
return [{ suite }];
|
||||||
|
},
|
||||||
|
childrenOfSuiteSegment() {
|
||||||
|
return [{ spec }];
|
||||||
|
},
|
||||||
|
isExcluded() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const runQueue = jasmine.createSpy('runQueue');
|
||||||
|
const reportDispatcher = mockReportDispatcher();
|
||||||
|
const subject = new jasmineUnderTest.TreeRunner({
|
||||||
|
executionTree,
|
||||||
|
runQueue,
|
||||||
|
globalErrors: mockGlobalErrors(),
|
||||||
|
runableResources: mockRunableResources(),
|
||||||
|
reportDispatcher,
|
||||||
|
currentRunableTracker: new jasmineUnderTest.CurrentRunableTracker(),
|
||||||
|
getConfig() {
|
||||||
|
return {};
|
||||||
|
},
|
||||||
|
reportChildrenOfBeforeAllFailure() {}
|
||||||
|
});
|
||||||
|
|
||||||
|
const executePromise = subject.execute();
|
||||||
|
expect(runQueue).toHaveBeenCalledTimes(1);
|
||||||
|
const topSuiteRunQueueOpts = runQueue.calls.mostRecent().args[0];
|
||||||
|
runQueue.calls.reset();
|
||||||
|
topSuiteRunQueueOpts.queueableFns[0].fn(function() {});
|
||||||
|
|
||||||
|
expect(runQueue).toHaveBeenCalledTimes(1);
|
||||||
|
const suiteRunQueueOpts = runQueue.calls.mostRecent().args[0];
|
||||||
|
|
||||||
|
// Complete the suite without starting it
|
||||||
|
expect(function() {
|
||||||
|
suiteRunQueueOpts.onComplete();
|
||||||
|
}).toThrowError('Tried to complete the wrong suite');
|
||||||
|
|
||||||
|
await expectAsync(executePromise).toBePending();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does not remove before and after fns from the top suite', async function() {
|
||||||
|
const topSuite = new jasmineUnderTest.Suite({ id: 'topSuite' });
|
||||||
|
spyOn(topSuite, 'cleanupBeforeAfter');
|
||||||
|
const executionTree = {
|
||||||
|
topSuite,
|
||||||
|
childrenOfTopSuite() {
|
||||||
|
return [];
|
||||||
|
},
|
||||||
|
isExcluded() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const runQueue = jasmine.createSpy('runQueue');
|
||||||
|
const subject = new jasmineUnderTest.TreeRunner({
|
||||||
|
executionTree,
|
||||||
|
runQueue,
|
||||||
|
globalErrors: mockGlobalErrors(),
|
||||||
|
runableResources: mockRunableResources(),
|
||||||
|
reportDispatcher: mockReportDispatcher(),
|
||||||
|
currentRunableTracker: new jasmineUnderTest.CurrentRunableTracker(),
|
||||||
|
getConfig() {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const executePromise = subject.execute();
|
||||||
|
expect(runQueue).toHaveBeenCalledTimes(1);
|
||||||
|
const topSuiteRunQueueOpts = runQueue.calls.mostRecent().args[0];
|
||||||
|
runQueue.calls.reset();
|
||||||
|
|
||||||
|
for (const qfn of topSuiteRunQueueOpts.queueableFns) {
|
||||||
|
qfn.fn();
|
||||||
|
}
|
||||||
|
topSuiteRunQueueOpts.onComplete();
|
||||||
|
|
||||||
|
await expectAsync(executePromise).toBeResolved();
|
||||||
|
expect(topSuite.cleanupBeforeAfter).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Late promise rejection handling', function() {
|
||||||
|
it('works for specs when the detectLateRejectionHandling param is true', function() {
|
||||||
|
const before = jasmine.createSpy('before');
|
||||||
|
const after = jasmine.createSpy('after');
|
||||||
|
const queueableFn = {
|
||||||
|
fn: jasmine.createSpy('test body').and.callFake(function() {
|
||||||
|
expect(before).toHaveBeenCalled();
|
||||||
|
expect(after).not.toHaveBeenCalled();
|
||||||
|
})
|
||||||
|
};
|
||||||
|
const spec = new jasmineUnderTest.Spec({
|
||||||
|
queueableFn,
|
||||||
|
beforeAndAfterFns: function() {
|
||||||
|
return { befores: [before], afters: [after] };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const {
|
||||||
|
runQueue,
|
||||||
|
setTimeout,
|
||||||
|
suiteRunQueueArgs,
|
||||||
|
globalErrors
|
||||||
|
} = runSingleSpecSuite(spec, { detectLateRejectionHandling: true });
|
||||||
|
|
||||||
|
suiteRunQueueArgs.queueableFns[0].fn();
|
||||||
|
expect(runQueue).toHaveBeenCalledTimes(1);
|
||||||
|
const specRunQueueOpts = runQueue.calls.mostRecent().args[0];
|
||||||
|
|
||||||
|
expect(specRunQueueOpts.queueableFns).toEqual([
|
||||||
|
{ fn: jasmine.any(Function) },
|
||||||
|
before,
|
||||||
|
queueableFn,
|
||||||
|
after,
|
||||||
|
{ fn: jasmine.any(Function) },
|
||||||
|
{
|
||||||
|
fn: jasmine.any(Function),
|
||||||
|
type: 'specCleanup'
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
|
||||||
|
const done = jasmine.createSpy('done');
|
||||||
|
specRunQueueOpts.queueableFns[4].fn(done);
|
||||||
|
expect(globalErrors.reportUnhandledRejections).not.toHaveBeenCalled();
|
||||||
|
expect(done).not.toHaveBeenCalled();
|
||||||
|
|
||||||
|
expect(setTimeout).toHaveBeenCalledOnceWith(jasmine.any(Function));
|
||||||
|
setTimeout.calls.argsFor(0)[0]();
|
||||||
|
expect(globalErrors.reportUnhandledRejections).toHaveBeenCalled();
|
||||||
|
expect(globalErrors.reportUnhandledRejections).toHaveBeenCalledBefore(
|
||||||
|
done
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('works for beforeAll when the detectLateRejectionHandling param is true', async function() {
|
||||||
|
const topSuite = new jasmineUnderTest.Suite({ id: 'topSuite' });
|
||||||
|
const suite = new jasmineUnderTest.Suite({
|
||||||
|
id: 'suite',
|
||||||
|
parentSuite: topSuite
|
||||||
|
});
|
||||||
|
suite.beforeAll(function() {});
|
||||||
|
const spec = new jasmineUnderTest.Spec({
|
||||||
|
queueableFn: { fn() {} },
|
||||||
|
parentSuite: suite
|
||||||
|
});
|
||||||
|
const executionTree = {
|
||||||
|
topSuite,
|
||||||
|
childrenOfTopSuite() {
|
||||||
|
return [{ suite }];
|
||||||
|
},
|
||||||
|
childrenOfSuiteSegment() {
|
||||||
|
return [{ spec }];
|
||||||
|
},
|
||||||
|
isExcluded() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const runQueue = jasmine.createSpy('runQueue');
|
||||||
|
const reportDispatcher = mockReportDispatcher();
|
||||||
|
const globalErrors = mockGlobalErrors();
|
||||||
|
const setTimeout = jasmine.createSpy('setTimeout');
|
||||||
|
const subject = new jasmineUnderTest.TreeRunner({
|
||||||
|
executionTree,
|
||||||
|
runQueue,
|
||||||
|
globalErrors,
|
||||||
|
runableResources: mockRunableResources(),
|
||||||
|
reportDispatcher,
|
||||||
|
setTimeout,
|
||||||
|
currentRunableTracker: new jasmineUnderTest.CurrentRunableTracker(),
|
||||||
|
getConfig() {
|
||||||
|
return { detectLateRejectionHandling: true };
|
||||||
|
},
|
||||||
|
reportChildrenOfBeforeAllFailure() {}
|
||||||
|
});
|
||||||
|
|
||||||
|
const executePromise = subject.execute();
|
||||||
|
expect(runQueue).toHaveBeenCalledTimes(1);
|
||||||
|
const topSuiteRunQueueOpts = runQueue.calls.mostRecent().args[0];
|
||||||
|
runQueue.calls.reset();
|
||||||
|
topSuiteRunQueueOpts.queueableFns[0].fn(function() {});
|
||||||
|
|
||||||
|
expect(runQueue).toHaveBeenCalledTimes(1);
|
||||||
|
const suiteRunQueueOpts = runQueue.calls.mostRecent().args[0];
|
||||||
|
expect(suiteRunQueueOpts.queueableFns).toEqual([
|
||||||
|
{ fn: jasmine.any(Function) }, // onStart
|
||||||
|
jasmine.objectContaining({ type: 'beforeAll' }),
|
||||||
|
{ fn: jasmine.any(Function) }, // detect late rejection handling
|
||||||
|
{ fn: jasmine.any(Function) } // spec
|
||||||
|
]);
|
||||||
|
suiteRunQueueOpts.queueableFns[0].fn();
|
||||||
|
const done = jasmine.createSpy('done');
|
||||||
|
suiteRunQueueOpts.queueableFns[2].fn(done);
|
||||||
|
expect(globalErrors.reportUnhandledRejections).not.toHaveBeenCalled();
|
||||||
|
|
||||||
|
expect(setTimeout).toHaveBeenCalledOnceWith(jasmine.any(Function));
|
||||||
|
setTimeout.calls.argsFor(0)[0]();
|
||||||
|
expect(globalErrors.reportUnhandledRejections).toHaveBeenCalledBefore(
|
||||||
|
done
|
||||||
|
);
|
||||||
|
|
||||||
|
await expectAsync(executePromise).toBePending();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('works for afterAll when the detectLateRejectionHandling param is true', async function() {
|
||||||
|
const topSuite = new jasmineUnderTest.Suite({ id: 'topSuite' });
|
||||||
|
const suite = new jasmineUnderTest.Suite({
|
||||||
|
id: 'suite',
|
||||||
|
parentSuite: topSuite
|
||||||
|
});
|
||||||
|
suite.afterAll(function() {});
|
||||||
|
const spec = new jasmineUnderTest.Spec({
|
||||||
|
queueableFn: { fn() {} },
|
||||||
|
parentSuite: suite
|
||||||
|
});
|
||||||
|
const executionTree = {
|
||||||
|
topSuite,
|
||||||
|
childrenOfTopSuite() {
|
||||||
|
return [{ suite }];
|
||||||
|
},
|
||||||
|
childrenOfSuiteSegment() {
|
||||||
|
return [{ spec }];
|
||||||
|
},
|
||||||
|
isExcluded() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const runQueue = jasmine.createSpy('runQueue');
|
||||||
|
const reportDispatcher = mockReportDispatcher();
|
||||||
|
const globalErrors = mockGlobalErrors();
|
||||||
|
const setTimeout = jasmine.createSpy('setTimeout');
|
||||||
|
const subject = new jasmineUnderTest.TreeRunner({
|
||||||
|
executionTree,
|
||||||
|
runQueue,
|
||||||
|
globalErrors,
|
||||||
|
runableResources: mockRunableResources(),
|
||||||
|
reportDispatcher,
|
||||||
|
setTimeout,
|
||||||
|
currentRunableTracker: new jasmineUnderTest.CurrentRunableTracker(),
|
||||||
|
getConfig() {
|
||||||
|
return { detectLateRejectionHandling: true };
|
||||||
|
},
|
||||||
|
reportChildrenOfBeforeAllFailure() {}
|
||||||
|
});
|
||||||
|
|
||||||
|
const executePromise = subject.execute();
|
||||||
|
expect(runQueue).toHaveBeenCalledTimes(1);
|
||||||
|
const topSuiteRunQueueOpts = runQueue.calls.mostRecent().args[0];
|
||||||
|
runQueue.calls.reset();
|
||||||
|
topSuiteRunQueueOpts.queueableFns[0].fn(function() {});
|
||||||
|
|
||||||
|
expect(runQueue).toHaveBeenCalledTimes(1);
|
||||||
|
const suiteRunQueueOpts = runQueue.calls.mostRecent().args[0];
|
||||||
|
expect(suiteRunQueueOpts.queueableFns).toEqual([
|
||||||
|
{ fn: jasmine.any(Function) }, // onStart
|
||||||
|
{ fn: jasmine.any(Function) }, // spec
|
||||||
|
jasmine.objectContaining({ type: 'afterAll' }),
|
||||||
|
{ fn: jasmine.any(Function) } // detect late rejection handling
|
||||||
|
]);
|
||||||
|
suiteRunQueueOpts.queueableFns[0].fn();
|
||||||
|
const done = jasmine.createSpy('done');
|
||||||
|
suiteRunQueueOpts.queueableFns[3].fn(done);
|
||||||
|
expect(globalErrors.reportUnhandledRejections).not.toHaveBeenCalled();
|
||||||
|
|
||||||
|
expect(setTimeout).toHaveBeenCalledOnceWith(jasmine.any(Function));
|
||||||
|
setTimeout.calls.argsFor(0)[0]();
|
||||||
|
expect(globalErrors.reportUnhandledRejections).toHaveBeenCalledBefore(
|
||||||
|
done
|
||||||
|
);
|
||||||
|
|
||||||
|
await expectAsync(executePromise).toBePending();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
function runSingleSpecSuite(spec, optionalConfig) {
|
||||||
|
const topSuiteId = 'suite1';
|
||||||
|
spec.parentSuiteId = topSuiteId;
|
||||||
|
const topSuite = new jasmineUnderTest.Suite({ id: topSuiteId });
|
||||||
|
topSuite.addChild(spec);
|
||||||
|
const executionTree = {
|
||||||
|
topSuite,
|
||||||
|
childrenOfTopSuite() {
|
||||||
|
return [{ spec }];
|
||||||
|
},
|
||||||
|
isExcluded() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const runQueue = jasmine.createSpy('runQueue');
|
||||||
|
const reportDispatcher = mockReportDispatcher();
|
||||||
|
const runableResources = mockRunableResources();
|
||||||
|
const globalErrors = mockGlobalErrors();
|
||||||
|
const setTimeout = jasmine.createSpy('setTimeout');
|
||||||
|
const currentRunableTracker = new jasmineUnderTest.CurrentRunableTracker();
|
||||||
|
const subject = new jasmineUnderTest.TreeRunner({
|
||||||
|
executionTree,
|
||||||
|
runQueue,
|
||||||
|
globalErrors,
|
||||||
|
setTimeout,
|
||||||
|
runableResources,
|
||||||
|
reportDispatcher,
|
||||||
|
currentRunableTracker,
|
||||||
|
getConfig() {
|
||||||
|
return optionalConfig || {};
|
||||||
|
},
|
||||||
|
reportChildrenOfBeforeAllFailure() {}
|
||||||
|
});
|
||||||
|
|
||||||
|
const executePromise = subject.execute();
|
||||||
|
expect(runQueue).toHaveBeenCalledTimes(1);
|
||||||
|
const suiteRunQueueArgs = runQueue.calls.mostRecent().args[0];
|
||||||
|
runQueue.calls.reset();
|
||||||
|
|
||||||
|
return {
|
||||||
|
runQueue,
|
||||||
|
globalErrors,
|
||||||
|
setTimeout,
|
||||||
|
currentRunableTracker,
|
||||||
|
runableResources,
|
||||||
|
reportDispatcher,
|
||||||
|
suiteRunQueueArgs,
|
||||||
|
executePromise
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function mockReportDispatcher() {
|
||||||
|
const reportDispatcher = jasmine.createSpyObj(
|
||||||
|
'reportDispatcher',
|
||||||
|
jasmineUnderTest.reporterEvents
|
||||||
|
);
|
||||||
|
|
||||||
|
for (const k of jasmineUnderTest.reporterEvents) {
|
||||||
|
reportDispatcher[k].and.returnValue(Promise.resolve());
|
||||||
|
}
|
||||||
|
|
||||||
|
return reportDispatcher;
|
||||||
|
}
|
||||||
|
|
||||||
|
function mockRunableResources() {
|
||||||
|
return jasmine.createSpyObj('runableResources', [
|
||||||
|
'initForRunable',
|
||||||
|
'clearForRunable'
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function mockGlobalErrors() {
|
||||||
|
return jasmine.createSpyObj('globalErrors', ['reportUnhandledRejections']);
|
||||||
|
}
|
||||||
|
});
|
||||||
@@ -128,17 +128,6 @@ describe('util', function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('isUndefined', function() {
|
|
||||||
it('reports if a variable is defined', function() {
|
|
||||||
let a;
|
|
||||||
expect(jasmineUnderTest.util.isUndefined(a)).toBe(true);
|
|
||||||
expect(jasmineUnderTest.util.isUndefined(undefined)).toBe(true);
|
|
||||||
|
|
||||||
const defined = 'diz be undefined yo';
|
|
||||||
expect(jasmineUnderTest.util.isUndefined(defined)).toBe(false);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('cloneArgs', function() {
|
describe('cloneArgs', function() {
|
||||||
it('clones primitives as-is', function() {
|
it('clones primitives as-is', function() {
|
||||||
expect(jasmineUnderTest.util.cloneArgs([true, false])).toEqual([
|
expect(jasmineUnderTest.util.cloneArgs([true, false])).toEqual([
|
||||||
|
|||||||
63
spec/core/asymmetric_equality/AllOfSpec.js
Normal file
63
spec/core/asymmetric_equality/AllOfSpec.js
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
describe('AllOf', function() {
|
||||||
|
it('matches a single value', function() {
|
||||||
|
const matchersUtil = new jasmineUnderTest.MatchersUtil();
|
||||||
|
const allOf = new jasmineUnderTest.AllOf('foo');
|
||||||
|
|
||||||
|
expect(allOf.asymmetricMatch('foo', matchersUtil)).toBeTrue();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('matches a single matcher', function() {
|
||||||
|
const matchersUtil = new jasmineUnderTest.MatchersUtil();
|
||||||
|
const allOf = new jasmineUnderTest.AllOf(
|
||||||
|
new jasmineUnderTest.StringContaining('oo')
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(allOf.asymmetricMatch('foo', matchersUtil)).toBeTrue();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('matches multiple matchers', function() {
|
||||||
|
const matchersUtil = new jasmineUnderTest.MatchersUtil();
|
||||||
|
const allOf = new jasmineUnderTest.AllOf(
|
||||||
|
new jasmineUnderTest.StringContaining('o'),
|
||||||
|
new jasmineUnderTest.StringContaining('f')
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(allOf.asymmetricMatch('foo', matchersUtil)).toBeTrue();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does not match when value does not match', function() {
|
||||||
|
const matchersUtil = new jasmineUnderTest.MatchersUtil();
|
||||||
|
const allOf = new jasmineUnderTest.AllOf('bar');
|
||||||
|
|
||||||
|
expect(allOf.asymmetricMatch('foo', matchersUtil)).toBeFalse();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does not match when any matchers fail', function() {
|
||||||
|
const matchersUtil = new jasmineUnderTest.MatchersUtil();
|
||||||
|
const allOf = new jasmineUnderTest.AllOf(
|
||||||
|
new jasmineUnderTest.StringContaining('o'),
|
||||||
|
new jasmineUnderTest.StringContaining('x')
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(allOf.asymmetricMatch('foo', matchersUtil)).toBeFalse();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('jasmineToStrings itself', function() {
|
||||||
|
const matcher = new jasmineUnderTest.AllOf('o');
|
||||||
|
const pp = jasmine.createSpy('pp').and.returnValue('sample');
|
||||||
|
|
||||||
|
expect(matcher.jasmineToString(pp)).toEqual('<jasmine.allOf(sample)>');
|
||||||
|
expect(pp).toHaveBeenCalledWith(['o']);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('when called without an argument', function() {
|
||||||
|
it('tells the user to pass a constructor argument', function() {
|
||||||
|
expect(function() {
|
||||||
|
new jasmineUnderTest.AllOf();
|
||||||
|
}).toThrowError(
|
||||||
|
TypeError,
|
||||||
|
'jasmine.allOf() expects at least one argument to be passed.'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -2,8 +2,7 @@ describe('base helpers', function() {
|
|||||||
describe('isError_', function() {
|
describe('isError_', function() {
|
||||||
it('correctly handles WebSocket events', function(done) {
|
it('correctly handles WebSocket events', function(done) {
|
||||||
if (typeof jasmine.getGlobal().WebSocket === 'undefined') {
|
if (typeof jasmine.getGlobal().WebSocket === 'undefined') {
|
||||||
done();
|
pending('Environment does not provide WebSocket');
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const obj = (function() {
|
const obj = (function() {
|
||||||
|
|||||||
@@ -79,7 +79,7 @@ describe('buildExpectationResult', function() {
|
|||||||
|
|
||||||
it('handles nodejs assertions', function() {
|
it('handles nodejs assertions', function() {
|
||||||
if (typeof require === 'undefined') {
|
if (typeof require === 'undefined') {
|
||||||
return;
|
pending('This test only runs in Node');
|
||||||
}
|
}
|
||||||
const assert = require('assert');
|
const assert = require('assert');
|
||||||
const value = 8421;
|
const value = 8421;
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
1415
spec/core/integration/GlobalErrorHandlingSpec.js
Normal file
1415
spec/core/integration/GlobalErrorHandlingSpec.js
Normal file
File diff suppressed because it is too large
Load Diff
@@ -470,20 +470,28 @@ describe('Matchers (Integration)', function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('toBeNullish', function() {
|
describe('toBeNullish', function() {
|
||||||
verifyPasses(function(env) {
|
describe('with undefined', function() {
|
||||||
env.expect(undefined).toBeNullish();
|
verifyPasses(function(env) {
|
||||||
|
env.expect(undefined).toBeNullish();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
verifyPasses(function(env) {
|
describe('with null', function() {
|
||||||
env.expect(null).toBeNullish();
|
verifyPasses(function(env) {
|
||||||
|
env.expect(null).toBeNullish();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
verifyFails(function(env) {
|
describe('with a truthy value', function() {
|
||||||
env.expect(1).toBeNullish();
|
verifyFails(function(env) {
|
||||||
|
env.expect(1).toBeNullish();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
verifyFails(function(env) {
|
describe('with a non-null falsy value', function() {
|
||||||
env.expect('').toBeNullish();
|
verifyFails(function(env) {
|
||||||
|
env.expect('').toBeNullish();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -665,13 +673,17 @@ describe('Matchers (Integration)', function() {
|
|||||||
env.expect(spyObj).toHaveSpyInteractions();
|
env.expect(spyObj).toHaveSpyInteractions();
|
||||||
});
|
});
|
||||||
|
|
||||||
verifyFails(function(env) {
|
describe('with no methods called', function() {
|
||||||
env.expect(spyObj).toHaveSpyInteractions();
|
verifyFails(function(env) {
|
||||||
|
env.expect(spyObj).toHaveSpyInteractions();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
verifyFails(function(env) {
|
describe('with only non-spy methods called', function() {
|
||||||
spyObj.otherMethod();
|
verifyFails(function(env) {
|
||||||
env.expect(spyObj).toHaveSpyInteractions();
|
spyObj.otherMethod();
|
||||||
|
env.expect(spyObj).toHaveSpyInteractions();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -621,9 +621,14 @@ describe('spec running', function() {
|
|||||||
actions.push('spec3');
|
actions.push('spec3');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
spyOn(jasmineUnderTest.getEnv(), 'deprecated');
|
||||||
|
|
||||||
await env.execute([spec2.id, spec3.id, spec1.id]);
|
await env.execute([spec2.id, spec3.id, spec1.id]);
|
||||||
|
|
||||||
expect(actions).toEqual(['spec2', 'spec3', 'spec1']);
|
expect(actions).toEqual(['spec2', 'spec3', 'spec1']);
|
||||||
|
expect(jasmineUnderTest.getEnv().deprecated).toHaveBeenCalledWith(
|
||||||
|
'The specified spec/suite order splits up a suite, running unrelated specs in the middle of it. This will become an error in a future release.'
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('refuses to re-enter suites with a beforeAll', async function() {
|
it('refuses to re-enter suites with a beforeAll', async function() {
|
||||||
|
|||||||
@@ -161,6 +161,63 @@ describe('DiffBuilder', function() {
|
|||||||
expect(diffBuilder.getMessage()).toEqual(expectedMsg);
|
expect(diffBuilder.getMessage()).toEqual(expectedMsg);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('handles cases where only the expected has a custom object formatter', function() {
|
||||||
|
const formatter = function(x) {
|
||||||
|
if (typeof x === 'number') {
|
||||||
|
return '[number:' + x + ']';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const prettyPrinter = jasmineUnderTest.makePrettyPrinter([formatter]);
|
||||||
|
const diffBuilder = new jasmineUnderTest.DiffBuilder({
|
||||||
|
prettyPrinter: prettyPrinter
|
||||||
|
});
|
||||||
|
|
||||||
|
diffBuilder.setRoots('five', 4);
|
||||||
|
diffBuilder.recordMismatch();
|
||||||
|
|
||||||
|
expect(diffBuilder.getMessage()).toEqual(
|
||||||
|
"Expected 'five' to equal [number:4]."
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('handles cases where only the actual has a custom object formatter', function() {
|
||||||
|
const formatter = function(x) {
|
||||||
|
if (typeof x === 'number') {
|
||||||
|
return '[number:' + x + ']';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const prettyPrinter = jasmineUnderTest.makePrettyPrinter([formatter]);
|
||||||
|
const diffBuilder = new jasmineUnderTest.DiffBuilder({
|
||||||
|
prettyPrinter: prettyPrinter
|
||||||
|
});
|
||||||
|
|
||||||
|
diffBuilder.setRoots(5, 'four');
|
||||||
|
diffBuilder.recordMismatch();
|
||||||
|
|
||||||
|
expect(diffBuilder.getMessage()).toEqual(
|
||||||
|
"Expected [number:5] to equal 'four'."
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('handles complex cases where only one side has a custom object formatter', function() {
|
||||||
|
const formatter = function(x) {
|
||||||
|
if (typeof x === 'number') {
|
||||||
|
return '[number:' + x + ']';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const prettyPrinter = jasmineUnderTest.makePrettyPrinter([formatter]);
|
||||||
|
const diffBuilder = new jasmineUnderTest.DiffBuilder({
|
||||||
|
prettyPrinter: prettyPrinter
|
||||||
|
});
|
||||||
|
|
||||||
|
diffBuilder.setRoots(5, { foo: 'bar', fnord: { graults: ['wombat'] } });
|
||||||
|
diffBuilder.recordMismatch();
|
||||||
|
|
||||||
|
expect(diffBuilder.getMessage()).toEqual(
|
||||||
|
"Expected [number:5] to equal Object({ foo: 'bar', fnord: Object({ graults: [ 'wombat' ] }) })."
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
it('builds diffs involving asymmetric equality testers that implement valuesForDiff_ at the root', function() {
|
it('builds diffs involving asymmetric equality testers that implement valuesForDiff_ at the root', function() {
|
||||||
const prettyPrinter = jasmineUnderTest.makePrettyPrinter([]),
|
const prettyPrinter = jasmineUnderTest.makePrettyPrinter([]),
|
||||||
diffBuilder = new jasmineUnderTest.DiffBuilder({
|
diffBuilder = new jasmineUnderTest.DiffBuilder({
|
||||||
|
|||||||
@@ -250,10 +250,6 @@ describe('matchersUtil', function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('passes for equivalent Promises (GitHub issue #1314)', function() {
|
it('passes for equivalent Promises (GitHub issue #1314)', function() {
|
||||||
if (typeof Promise === 'undefined') {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const p1 = new Promise(function() {}),
|
const p1 = new Promise(function() {}),
|
||||||
p2 = new Promise(function() {}),
|
p2 = new Promise(function() {}),
|
||||||
matchersUtil = new jasmineUnderTest.MatchersUtil();
|
matchersUtil = new jasmineUnderTest.MatchersUtil();
|
||||||
@@ -263,14 +259,13 @@ describe('matchersUtil', function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('when running in a browser', function() {
|
describe('when running in a browser', function() {
|
||||||
function isNotRunningInBrowser() {
|
beforeEach(function() {
|
||||||
return typeof document === 'undefined';
|
if (typeof document === 'undefined') {
|
||||||
}
|
pending('This test only runs in browsers');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
it('passes for equivalent DOM nodes', function() {
|
it('passes for equivalent DOM nodes', function() {
|
||||||
if (isNotRunningInBrowser()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const a = document.createElement('div');
|
const a = document.createElement('div');
|
||||||
const matchersUtil = new jasmineUnderTest.MatchersUtil();
|
const matchersUtil = new jasmineUnderTest.MatchersUtil();
|
||||||
|
|
||||||
@@ -285,9 +280,6 @@ describe('matchersUtil', function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('passes for equivalent objects from different frames', function() {
|
it('passes for equivalent objects from different frames', function() {
|
||||||
if (isNotRunningInBrowser()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const matchersUtil = new jasmineUnderTest.MatchersUtil();
|
const matchersUtil = new jasmineUnderTest.MatchersUtil();
|
||||||
const iframe = document.createElement('iframe');
|
const iframe = document.createElement('iframe');
|
||||||
document.body.appendChild(iframe);
|
document.body.appendChild(iframe);
|
||||||
@@ -299,9 +291,6 @@ describe('matchersUtil', function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('fails for DOM nodes with different attributes or child nodes', function() {
|
it('fails for DOM nodes with different attributes or child nodes', function() {
|
||||||
if (isNotRunningInBrowser()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const matchersUtil = new jasmineUnderTest.MatchersUtil();
|
const matchersUtil = new jasmineUnderTest.MatchersUtil();
|
||||||
const a = document.createElement('div');
|
const a = document.createElement('div');
|
||||||
a.setAttribute('test-attr', 'attr-value');
|
a.setAttribute('test-attr', 'attr-value');
|
||||||
@@ -325,14 +314,13 @@ describe('matchersUtil', function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('when running in Node', function() {
|
describe('when running in Node', function() {
|
||||||
function isNotRunningInNode() {
|
beforeEach(function() {
|
||||||
return typeof require !== 'function';
|
if (typeof require !== 'function') {
|
||||||
}
|
pending('This test only runs in Node');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
it('passes for equivalent objects from different vm contexts', function() {
|
it('passes for equivalent objects from different vm contexts', function() {
|
||||||
if (isNotRunningInNode()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const matchersUtil = new jasmineUnderTest.MatchersUtil();
|
const matchersUtil = new jasmineUnderTest.MatchersUtil();
|
||||||
const vm = require('vm');
|
const vm = require('vm');
|
||||||
const sandbox = {
|
const sandbox = {
|
||||||
@@ -344,9 +332,6 @@ describe('matchersUtil', function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('passes for equivalent arrays from different vm contexts', function() {
|
it('passes for equivalent arrays from different vm contexts', function() {
|
||||||
if (isNotRunningInNode()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const matchersUtil = new jasmineUnderTest.MatchersUtil();
|
const matchersUtil = new jasmineUnderTest.MatchersUtil();
|
||||||
const vm = require('vm');
|
const vm = require('vm');
|
||||||
const sandbox = {
|
const sandbox = {
|
||||||
@@ -783,7 +768,22 @@ describe('matchersUtil', function() {
|
|||||||
a2[0] = 1;
|
a2[0] = 1;
|
||||||
const diffBuilder = new jasmineUnderTest.DiffBuilder();
|
const diffBuilder = new jasmineUnderTest.DiffBuilder();
|
||||||
expect(matchersUtil.equals(a1, a2, diffBuilder)).toBe(false);
|
expect(matchersUtil.equals(a1, a2, diffBuilder)).toBe(false);
|
||||||
jasmine.debugLog('Diff: ' + diffBuilder.getMessage());
|
jasmine.debugLog(
|
||||||
|
'a1 keys: ' +
|
||||||
|
jasmine.basicPrettyPrinter_(
|
||||||
|
jasmineUnderTest.MatchersUtil.keys(a1)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
jasmine.debugLog(
|
||||||
|
'a2 keys: ' +
|
||||||
|
jasmine.basicPrettyPrinter_(
|
||||||
|
jasmineUnderTest.MatchersUtil.keys(a2)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
jasmine.debugLog('a1 length:' + a1.length);
|
||||||
|
jasmine.debugLog('a2 length:' + a2.length);
|
||||||
|
jasmine.debugLog('a1[0]: ' + a1[0]);
|
||||||
|
jasmine.debugLog('a2[0]: ' + a2[0]);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -458,9 +458,9 @@ describe('toEqual', function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('reports mismatches between Functions', function() {
|
it('reports mismatches between Functions', function() {
|
||||||
const actual = { x: function() {} },
|
const actual = { x: function() {} };
|
||||||
expected = { x: function() {} },
|
const expected = { x: function() {} };
|
||||||
message = 'Expected $.x = Function to equal Function.';
|
const message = "Expected $.x = Function 'x' to equal Function 'x'.";
|
||||||
|
|
||||||
expect(compareEquals(actual, expected).message).toEqual(message);
|
expect(compareEquals(actual, expected).message).toEqual(message);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -82,7 +82,7 @@ describe('toThrowError', function() {
|
|||||||
|
|
||||||
it('passes if thrown is an instanceof Error regardless of global that contains its constructor', function() {
|
it('passes if thrown is an instanceof Error regardless of global that contains its constructor', function() {
|
||||||
if (isNotRunningInBrowser()) {
|
if (isNotRunningInBrowser()) {
|
||||||
return;
|
pending('This test only runs in browsers.');
|
||||||
}
|
}
|
||||||
|
|
||||||
const matcher = jasmineUnderTest.matchers.toThrowError();
|
const matcher = jasmineUnderTest.matchers.toThrowError();
|
||||||
@@ -281,22 +281,44 @@ describe('toThrowError', function() {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('fails if thrown is a type of Error and the expected is a different Error', function() {
|
describe('with a string', function() {
|
||||||
const matchersUtil = {
|
it('fails if thrown is a type of Error and the expected is a different Error', function() {
|
||||||
equals: jasmine.createSpy('delegated-equal').and.returnValue(false),
|
const matchersUtil = {
|
||||||
pp: jasmineUnderTest.makePrettyPrinter()
|
equals: jasmine.createSpy('delegated-equal').and.returnValue(false),
|
||||||
},
|
pp: jasmineUnderTest.makePrettyPrinter()
|
||||||
matcher = jasmineUnderTest.matchers.toThrowError(matchersUtil),
|
},
|
||||||
fn = function() {
|
matcher = jasmineUnderTest.matchers.toThrowError(matchersUtil),
|
||||||
throw new TypeError('foo');
|
fn = function() {
|
||||||
};
|
throw new TypeError('foo');
|
||||||
|
};
|
||||||
|
|
||||||
const result = matcher.compare(fn, TypeError, 'bar');
|
const result = matcher.compare(fn, TypeError, 'bar');
|
||||||
|
|
||||||
expect(result.pass).toBe(false);
|
expect(result.pass).toBe(false);
|
||||||
expect(result.message()).toEqual(
|
expect(result.message()).toEqual(
|
||||||
"Expected function to throw TypeError with message 'bar', but it threw TypeError with message 'foo'."
|
"Expected function to throw TypeError with message 'bar', but it threw TypeError with message 'foo'."
|
||||||
);
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('with a regex', function() {
|
||||||
|
it('fails if thrown is a type of Error and the expected is a different Error', function() {
|
||||||
|
const matchersUtil = {
|
||||||
|
equals: jasmine.createSpy('delegated-equal').and.returnValue(false),
|
||||||
|
pp: jasmineUnderTest.makePrettyPrinter()
|
||||||
|
},
|
||||||
|
matcher = jasmineUnderTest.matchers.toThrowError(matchersUtil),
|
||||||
|
fn = function() {
|
||||||
|
throw new TypeError('foo');
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = matcher.compare(fn, TypeError, /bar/);
|
||||||
|
|
||||||
|
expect(result.pass).toBe(false);
|
||||||
|
expect(result.message()).toEqual(
|
||||||
|
"Expected function to throw TypeError with a message matching /bar/, but it threw TypeError with message 'foo'."
|
||||||
|
);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('passes if thrown is a type of Error and has the same type as the expected Error and the message matches the expected message', function() {
|
it('passes if thrown is a type of Error and has the same type as the expected Error and the message matches the expected message', function() {
|
||||||
@@ -316,22 +338,4 @@ describe('toThrowError', function() {
|
|||||||
'Expected function not to throw TypeError with a message matching /foo/.'
|
'Expected function not to throw TypeError with a message matching /foo/.'
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('fails if thrown is a type of Error and the expected is a different Error', function() {
|
|
||||||
const matchersUtil = {
|
|
||||||
equals: jasmine.createSpy('delegated-equal').and.returnValue(false),
|
|
||||||
pp: jasmineUnderTest.makePrettyPrinter()
|
|
||||||
},
|
|
||||||
matcher = jasmineUnderTest.matchers.toThrowError(matchersUtil),
|
|
||||||
fn = function() {
|
|
||||||
throw new TypeError('foo');
|
|
||||||
};
|
|
||||||
|
|
||||||
const result = matcher.compare(fn, TypeError, /bar/);
|
|
||||||
|
|
||||||
expect(result.pass).toBe(false);
|
|
||||||
expect(result.message()).toEqual(
|
|
||||||
"Expected function to throw TypeError with a message matching /bar/, but it threw TypeError with message 'foo'."
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|||||||
5
spec/helpers/callerFilenameShim.js
Normal file
5
spec/helpers/callerFilenameShim.js
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
(function() {
|
||||||
|
specHelpers.callerFilenameShim = function(fn) {
|
||||||
|
return fn();
|
||||||
|
};
|
||||||
|
})();
|
||||||
@@ -499,7 +499,7 @@ describe('HtmlReporter', function() {
|
|||||||
expect(duration.innerHTML).toMatch(/finished in 0.1s/);
|
expect(duration.innerHTML).toMatch(/finished in 0.1s/);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('reports the suite and spec names with status', function() {
|
it('reports the suite names with status, and spec names with status and duration', function() {
|
||||||
const container = document.createElement('div'),
|
const container = document.createElement('div'),
|
||||||
getContainer = function() {
|
getContainer = function() {
|
||||||
return container;
|
return container;
|
||||||
@@ -532,7 +532,8 @@ describe('HtmlReporter', function() {
|
|||||||
fullName: 'A Suite with a spec',
|
fullName: 'A Suite with a spec',
|
||||||
status: 'passed',
|
status: 'passed',
|
||||||
failedExpectations: [],
|
failedExpectations: [],
|
||||||
passedExpectations: [{ passed: true }]
|
passedExpectations: [{ passed: true }],
|
||||||
|
duration: 1230
|
||||||
};
|
};
|
||||||
reporter.specStarted(specResult);
|
reporter.specStarted(specResult);
|
||||||
reporter.specDone(specResult);
|
reporter.specDone(specResult);
|
||||||
@@ -549,7 +550,8 @@ describe('HtmlReporter', function() {
|
|||||||
fullName: 'A Suite inner suite with another spec',
|
fullName: 'A Suite inner suite with another spec',
|
||||||
status: 'passed',
|
status: 'passed',
|
||||||
failedExpectations: [],
|
failedExpectations: [],
|
||||||
passedExpectations: [{ passed: true }]
|
passedExpectations: [{ passed: true }],
|
||||||
|
duration: 1240
|
||||||
};
|
};
|
||||||
reporter.specStarted(specResult);
|
reporter.specStarted(specResult);
|
||||||
reporter.specDone(specResult);
|
reporter.specDone(specResult);
|
||||||
@@ -567,7 +569,8 @@ describe('HtmlReporter', function() {
|
|||||||
fullName: 'A Suite inner with a failing spec',
|
fullName: 'A Suite inner with a failing spec',
|
||||||
status: 'failed',
|
status: 'failed',
|
||||||
failedExpectations: [{}],
|
failedExpectations: [{}],
|
||||||
passedExpectations: []
|
passedExpectations: [],
|
||||||
|
duration: 2090
|
||||||
};
|
};
|
||||||
reporter.specStarted(specResult);
|
reporter.specStarted(specResult);
|
||||||
reporter.specDone(specResult);
|
reporter.specDone(specResult);
|
||||||
@@ -614,6 +617,9 @@ describe('HtmlReporter', function() {
|
|||||||
expect(specLink.getAttribute('href')).toEqual(
|
expect(specLink.getAttribute('href')).toEqual(
|
||||||
'/?foo=bar&spec=A Suite with a spec'
|
'/?foo=bar&spec=A Suite with a spec'
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const specDuration = spec.childNodes[1];
|
||||||
|
expect(specDuration.innerHTML).toEqual('(1230ms)');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('has an options menu', function() {
|
it('has an options menu', function() {
|
||||||
|
|||||||
@@ -1,40 +0,0 @@
|
|||||||
describe('MatchersSpec - HTML Dependent', function() {
|
|
||||||
let env, spec;
|
|
||||||
|
|
||||||
beforeEach(function() {
|
|
||||||
env = new jasmineUnderTest.Env();
|
|
||||||
|
|
||||||
env.describe('suite', function() {
|
|
||||||
spec = env.it('spec', function() {});
|
|
||||||
});
|
|
||||||
spyOn(spec, 'addExpectationResult');
|
|
||||||
|
|
||||||
addMatchers({
|
|
||||||
toPass: function() {
|
|
||||||
return lastResult().passed;
|
|
||||||
},
|
|
||||||
toFail: function() {
|
|
||||||
return !lastResult().passed;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
afterEach(function() {
|
|
||||||
env.cleanup_();
|
|
||||||
});
|
|
||||||
|
|
||||||
function match(value) {
|
|
||||||
return spec.expect(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
function lastResult() {
|
|
||||||
return spec.addExpectationResult.mostRecentCall.args[1];
|
|
||||||
}
|
|
||||||
|
|
||||||
xit('toEqual with DOM nodes', function() {
|
|
||||||
const nodeA = document.createElement('div');
|
|
||||||
const nodeB = document.createElement('div');
|
|
||||||
expect(match(nodeA).toEqual(nodeA)).toPass();
|
|
||||||
expect(match(nodeA).toEqual(nodeB)).toFail();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -1,24 +1,21 @@
|
|||||||
const fs = require('node:fs');
|
const fs = require('node:fs');
|
||||||
const path = require('node:path');
|
const path = require('node:path');
|
||||||
const os = require('node:os');
|
const os = require('node:os');
|
||||||
const { rimrafSync } = require('rimraf');
|
const child_process = require('node:child_process');
|
||||||
|
|
||||||
describe('npm package', function() {
|
describe('npm package', function() {
|
||||||
beforeAll(function() {
|
beforeAll(function() {
|
||||||
const shell = require('shelljs'),
|
const packOutput = child_process.execSync('npm pack', {
|
||||||
pack = shell.exec('npm pack', { silent: true });
|
encoding: 'utf8',
|
||||||
|
stdio: ['pipe', 'pipe', 'pipe']
|
||||||
this.tarball = pack.stdout.split('\n')[0];
|
});
|
||||||
|
this.tarball = packOutput.split('\n')[0];
|
||||||
const prefix = path.join(os.tmpdir(), 'jasmine-npm-package');
|
const prefix = path.join(os.tmpdir(), 'jasmine-npm-package');
|
||||||
this.tmpDir = fs.mkdtempSync(prefix);
|
this.tmpDir = fs.mkdtempSync(prefix);
|
||||||
|
|
||||||
const untar = shell.exec(
|
child_process.execSync(`tar -xzf ${this.tarball} -C ${this.tmpDir}`, {
|
||||||
'tar -xzf ' + this.tarball + ' -C ' + this.tmpDir,
|
encoding: 'utf8'
|
||||||
{
|
});
|
||||||
silent: true
|
|
||||||
}
|
|
||||||
);
|
|
||||||
expect(untar.code).toBe(0);
|
|
||||||
|
|
||||||
this.packagedCore = require(path.join(
|
this.packagedCore = require(path.join(
|
||||||
this.tmpDir,
|
this.tmpDir,
|
||||||
@@ -43,7 +40,7 @@ describe('npm package', function() {
|
|||||||
|
|
||||||
afterAll(function() {
|
afterAll(function() {
|
||||||
fs.unlinkSync(this.tarball);
|
fs.unlinkSync(this.tarball);
|
||||||
rimrafSync(this.tmpDir);
|
fs.rmSync(this.tmpDir, { recursive: true });
|
||||||
});
|
});
|
||||||
|
|
||||||
it('has a root path', function() {
|
it('has a root path', function() {
|
||||||
|
|||||||
@@ -23,9 +23,13 @@ module.exports = {
|
|||||||
'helpers/BrowserFlags.js',
|
'helpers/BrowserFlags.js',
|
||||||
'helpers/domHelpers.js',
|
'helpers/domHelpers.js',
|
||||||
'helpers/integrationMatchers.js',
|
'helpers/integrationMatchers.js',
|
||||||
|
'helpers/callerFilenameShim.js',
|
||||||
'helpers/defineJasmineUnderTest.js',
|
'helpers/defineJasmineUnderTest.js',
|
||||||
'helpers/resetEnv.js'
|
'helpers/resetEnv.js'
|
||||||
],
|
],
|
||||||
|
env: {
|
||||||
|
forbidDuplicateNames: true
|
||||||
|
},
|
||||||
random: true,
|
random: true,
|
||||||
browser: {
|
browser: {
|
||||||
name: process.env.JASMINE_BROWSER || 'firefox',
|
name: process.env.JASMINE_BROWSER || 'firefox',
|
||||||
|
|||||||
@@ -8,9 +8,13 @@
|
|||||||
"helpers/init.js",
|
"helpers/init.js",
|
||||||
"helpers/domHelpers.js",
|
"helpers/domHelpers.js",
|
||||||
"helpers/integrationMatchers.js",
|
"helpers/integrationMatchers.js",
|
||||||
|
"helpers/callerFilenameShim.js",
|
||||||
"helpers/overrideConsoleLogForCircleCi.js",
|
"helpers/overrideConsoleLogForCircleCi.js",
|
||||||
"helpers/nodeDefineJasmineUnderTest.js",
|
"helpers/nodeDefineJasmineUnderTest.js",
|
||||||
"helpers/resetEnv.js"
|
"helpers/resetEnv.js"
|
||||||
],
|
],
|
||||||
|
"env": {
|
||||||
|
"forbidDuplicateNames": true
|
||||||
|
},
|
||||||
"random": true
|
"random": true
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ getJasmineRequireObj().CallTracker = function(j$) {
|
|||||||
|
|
||||||
this.track = function(context) {
|
this.track = function(context) {
|
||||||
if (opts.cloneArgs) {
|
if (opts.cloneArgs) {
|
||||||
context.args = j$.util.cloneArgs(context.args);
|
context.args = opts.argsCloner(context.args);
|
||||||
}
|
}
|
||||||
calls.push(context);
|
calls.push(context);
|
||||||
};
|
};
|
||||||
@@ -117,13 +117,15 @@ getJasmineRequireObj().CallTracker = function(j$) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set this spy to do a shallow clone of arguments passed to each invocation.
|
* Set this spy to do a clone of arguments passed to each invocation.
|
||||||
* @name Spy#calls#saveArgumentsByValue
|
* @name Spy#calls#saveArgumentsByValue
|
||||||
* @since 2.5.0
|
* @since 2.5.0
|
||||||
|
* @param {Function} [argsCloner] A function to use to clone the arguments. Defaults to a shallow cloning function.
|
||||||
* @function
|
* @function
|
||||||
*/
|
*/
|
||||||
this.saveArgumentsByValue = function() {
|
this.saveArgumentsByValue = function(argsCloner = j$.util.cloneArgs) {
|
||||||
opts.cloneArgs = true;
|
opts.cloneArgs = true;
|
||||||
|
opts.argsCloner = argsCloner;
|
||||||
};
|
};
|
||||||
|
|
||||||
this.unverifiedCount = function() {
|
this.unverifiedCount = function() {
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ getJasmineRequireObj().clearStack = function(j$) {
|
|||||||
|
|
||||||
function getUnclampedSetTimeout(global) {
|
function getUnclampedSetTimeout(global) {
|
||||||
const { setTimeout } = global;
|
const { setTimeout } = global;
|
||||||
if (j$.util.isUndefined(global.MessageChannel)) {
|
if (!global.MessageChannel) {
|
||||||
return setTimeout;
|
return setTimeout;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -104,10 +104,7 @@ getJasmineRequireObj().clearStack = function(j$) {
|
|||||||
// Unlike browsers, Node doesn't require us to do a periodic setTimeout
|
// Unlike browsers, Node doesn't require us to do a periodic setTimeout
|
||||||
// so we avoid the overhead.
|
// so we avoid the overhead.
|
||||||
return nodeQueueMicrotaskImpl(global);
|
return nodeQueueMicrotaskImpl(global);
|
||||||
} else if (
|
} else if (SAFARI_OR_WIN_WEBKIT || !global.MessageChannel /* tests */) {
|
||||||
SAFARI_OR_WIN_WEBKIT ||
|
|
||||||
j$.util.isUndefined(global.MessageChannel) /* tests */
|
|
||||||
) {
|
|
||||||
// queueMicrotask is dramatically faster than MessageChannel in Safari
|
// queueMicrotask is dramatically faster than MessageChannel in Safari
|
||||||
// and other WebKit-based browsers, such as the one distributed by Playwright
|
// and other WebKit-based browsers, such as the one distributed by Playwright
|
||||||
// to test Safari-like behavior on Windows.
|
// to test Safari-like behavior on Windows.
|
||||||
|
|||||||
@@ -69,6 +69,10 @@ getJasmineRequireObj().Clock = function() {
|
|||||||
* @function
|
* @function
|
||||||
*/
|
*/
|
||||||
this.uninstall = function() {
|
this.uninstall = function() {
|
||||||
|
// Ensure auto ticking loop is aborted when clock is uninstalled
|
||||||
|
if (tickMode.mode === 'auto') {
|
||||||
|
tickMode = { mode: 'manual', counter: tickMode.counter + 1 };
|
||||||
|
}
|
||||||
delayedFunctionScheduler = null;
|
delayedFunctionScheduler = null;
|
||||||
mockDate.uninstall();
|
mockDate.uninstall();
|
||||||
replace(global, realTimingFunctions);
|
replace(global, realTimingFunctions);
|
||||||
@@ -222,6 +226,12 @@ callbacks to execute _before_ running the next one.
|
|||||||
//
|
//
|
||||||
// @return {!Promise<undefined>}
|
// @return {!Promise<undefined>}
|
||||||
async function newMacrotask() {
|
async function newMacrotask() {
|
||||||
|
if (NODE_JS) {
|
||||||
|
// setImmediate is generally faster than setTimeout in Node
|
||||||
|
// https://nodejs.org/en/learn/asynchronous-work/event-loop-timers-and-nexttick#setimmediate-vs-settimeout
|
||||||
|
return new Promise(resolve => void setImmediate(resolve));
|
||||||
|
}
|
||||||
|
|
||||||
// MessageChannel ensures that setTimeout is not throttled to 4ms.
|
// 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://developer.mozilla.org/en-US/docs/Web/API/setTimeout#reasons_for_delays_longer_than_specified
|
||||||
// https://stackblitz.com/edit/stackblitz-starters-qtlpcc
|
// https://stackblitz.com/edit/stackblitz-starters-qtlpcc
|
||||||
|
|||||||
219
src/core/Configuration.js
Normal file
219
src/core/Configuration.js
Normal file
@@ -0,0 +1,219 @@
|
|||||||
|
getJasmineRequireObj().Configuration = function(j$) {
|
||||||
|
/**
|
||||||
|
* This represents the available options to configure Jasmine.
|
||||||
|
* Options that are not provided will use their default values.
|
||||||
|
* @see Env#configure
|
||||||
|
* @interface Configuration
|
||||||
|
* @since 3.3.0
|
||||||
|
*/
|
||||||
|
const defaultConfig = {
|
||||||
|
/**
|
||||||
|
* Whether to randomize spec execution order
|
||||||
|
* @name Configuration#random
|
||||||
|
* @since 3.3.0
|
||||||
|
* @type Boolean
|
||||||
|
* @default true
|
||||||
|
*/
|
||||||
|
random: true,
|
||||||
|
/**
|
||||||
|
* Seed to use as the basis of randomization.
|
||||||
|
* Null causes the seed to be determined randomly at the start of execution.
|
||||||
|
* @name Configuration#seed
|
||||||
|
* @since 3.3.0
|
||||||
|
* @type (number|string)
|
||||||
|
* @default null
|
||||||
|
*/
|
||||||
|
seed: null,
|
||||||
|
/**
|
||||||
|
* Whether to stop execution of the suite after the first spec failure
|
||||||
|
*
|
||||||
|
* <p>In parallel mode, `stopOnSpecFailure` works on a "best effort"
|
||||||
|
* basis. Jasmine will stop execution as soon as practical after a failure
|
||||||
|
* but it might not be immediate.</p>
|
||||||
|
* @name Configuration#stopOnSpecFailure
|
||||||
|
* @since 3.9.0
|
||||||
|
* @type Boolean
|
||||||
|
* @default false
|
||||||
|
*/
|
||||||
|
stopOnSpecFailure: false,
|
||||||
|
/**
|
||||||
|
* Whether to fail the spec if it ran no expectations. By default
|
||||||
|
* a spec that ran no expectations is reported as passed. Setting this
|
||||||
|
* to true will report such spec as a failure.
|
||||||
|
* @name Configuration#failSpecWithNoExpectations
|
||||||
|
* @since 3.5.0
|
||||||
|
* @type Boolean
|
||||||
|
* @default false
|
||||||
|
*/
|
||||||
|
failSpecWithNoExpectations: false,
|
||||||
|
/**
|
||||||
|
* Whether to cause specs to only have one expectation failure.
|
||||||
|
* @name Configuration#stopSpecOnExpectationFailure
|
||||||
|
* @since 3.3.0
|
||||||
|
* @type Boolean
|
||||||
|
* @default false
|
||||||
|
*/
|
||||||
|
stopSpecOnExpectationFailure: false,
|
||||||
|
/**
|
||||||
|
* A function that takes a spec and returns true if it should be executed
|
||||||
|
* or false if it should be skipped.
|
||||||
|
* @callback SpecFilter
|
||||||
|
* @param {Spec} spec - The spec that the filter is being applied to.
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* Function to use to filter specs
|
||||||
|
* @name Configuration#specFilter
|
||||||
|
* @since 3.3.0
|
||||||
|
* @type SpecFilter
|
||||||
|
* @default A function that always returns true.
|
||||||
|
*/
|
||||||
|
specFilter: function() {
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* Whether reporters should hide disabled specs from their output.
|
||||||
|
* Currently only supported by Jasmine's HTMLReporter
|
||||||
|
* @name Configuration#hideDisabled
|
||||||
|
* @since 3.3.0
|
||||||
|
* @type Boolean
|
||||||
|
* @default false
|
||||||
|
*/
|
||||||
|
hideDisabled: false,
|
||||||
|
/**
|
||||||
|
* Clean closures when a suite is done running (done by clearing the stored function reference).
|
||||||
|
* This prevents memory leaks, but you won't be able to run jasmine multiple times.
|
||||||
|
* @name Configuration#autoCleanClosures
|
||||||
|
* @since 3.10.0
|
||||||
|
* @type boolean
|
||||||
|
* @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 to issue warnings for certain deprecated functionality
|
||||||
|
* every time it's used. If not set or set to false, deprecation warnings
|
||||||
|
* for methods that tend to be called frequently will be issued only once
|
||||||
|
* or otherwise throttled to prevent the suite output from being flooded
|
||||||
|
* with warnings.
|
||||||
|
* @name Configuration#verboseDeprecations
|
||||||
|
* @since 3.6.0
|
||||||
|
* @type Boolean
|
||||||
|
* @default false
|
||||||
|
*/
|
||||||
|
verboseDeprecations: false,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether to detect late promise rejection handling during spec
|
||||||
|
* execution. If this option is enabled, a promise rejection that triggers
|
||||||
|
* the JavaScript runtime's unhandled rejection event will not be treated
|
||||||
|
* as an error as long as it's handled before the spec finishes.
|
||||||
|
*
|
||||||
|
* This option is off by default because it imposes a performance penalty.
|
||||||
|
* @name Configuration#detectLateRejectionHandling
|
||||||
|
* @since 5.10.0
|
||||||
|
* @type Boolean
|
||||||
|
* @default false
|
||||||
|
*/
|
||||||
|
detectLateRejectionHandling: false,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The number of extra stack frames inserted by a wrapper around {@link it}
|
||||||
|
* or by some other local modification. Jasmine uses this to determine the
|
||||||
|
* filename for {@link SpecStartedEvent} and {@link SpecDoneEvent}.
|
||||||
|
* @name Configuration#extraItStackFrames
|
||||||
|
* @since 5.13.0
|
||||||
|
* @type number
|
||||||
|
* @default 0
|
||||||
|
*/
|
||||||
|
extraItStackFrames: 0,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The number of extra stack frames inserted by a wrapper around
|
||||||
|
* {@link describe} or by some other local modification. Jasmine uses this
|
||||||
|
* to determine the filename for {@link SpecStartedEvent} and
|
||||||
|
* {@link SpecDoneEvent}.
|
||||||
|
* @name Configuration#extraDescribeStackFrames
|
||||||
|
* @since 5.13.0
|
||||||
|
* @type number
|
||||||
|
* @default 0
|
||||||
|
*/
|
||||||
|
extraDescribeStackFrames: 0
|
||||||
|
};
|
||||||
|
Object.freeze(defaultConfig);
|
||||||
|
|
||||||
|
class Configuration {
|
||||||
|
#values;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.#values = { ...defaultConfig };
|
||||||
|
|
||||||
|
for (const k of Object.keys(defaultConfig)) {
|
||||||
|
Object.defineProperty(this, k, {
|
||||||
|
enumerable: true,
|
||||||
|
get() {
|
||||||
|
return this.#values[k];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
copy() {
|
||||||
|
return { ...this.#values };
|
||||||
|
}
|
||||||
|
|
||||||
|
update(changes) {
|
||||||
|
const booleanProps = [
|
||||||
|
'random',
|
||||||
|
'failSpecWithNoExpectations',
|
||||||
|
'hideDisabled',
|
||||||
|
'stopOnSpecFailure',
|
||||||
|
'stopSpecOnExpectationFailure',
|
||||||
|
'autoCleanClosures',
|
||||||
|
'forbidDuplicateNames',
|
||||||
|
'detectLateRejectionHandling'
|
||||||
|
];
|
||||||
|
|
||||||
|
for (const k of booleanProps) {
|
||||||
|
if (typeof changes[k] !== 'undefined') {
|
||||||
|
this.#values[k] = changes[k];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (changes.specFilter) {
|
||||||
|
this.#values.specFilter = changes.specFilter;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 0 and null are valid values, so a truthiness check wouldn't work
|
||||||
|
if (typeof changes.seed !== 'undefined') {
|
||||||
|
this.#values.seed = changes.seed;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: in the next major release, make verboseDeprecations work like
|
||||||
|
// other boolean properties.
|
||||||
|
if (changes.hasOwnProperty('verboseDeprecations')) {
|
||||||
|
this.#values.verboseDeprecations = changes.verboseDeprecations;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 0 is a valid value for both of these, so a truthiness check wouldn't work
|
||||||
|
if (typeof changes.extraItStackFrames !== 'undefined') {
|
||||||
|
this.#values.extraItStackFrames = changes.extraItStackFrames;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof changes.extraDescribeStackFrames !== 'undefined') {
|
||||||
|
this.#values.extraDescribeStackFrames =
|
||||||
|
changes.extraDescribeStackFrames;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Configuration;
|
||||||
|
};
|
||||||
38
src/core/CurrentRunableTracker.js
Normal file
38
src/core/CurrentRunableTracker.js
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
getJasmineRequireObj().CurrentRunableTracker = function() {
|
||||||
|
class CurrentRunableTracker {
|
||||||
|
#currentSpec;
|
||||||
|
#currentlyExecutingSuites;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.#currentlyExecutingSuites = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
currentRunable() {
|
||||||
|
return this.currentSpec() || this.currentSuite();
|
||||||
|
}
|
||||||
|
|
||||||
|
currentSpec() {
|
||||||
|
return this.#currentSpec;
|
||||||
|
}
|
||||||
|
|
||||||
|
setCurrentSpec(spec) {
|
||||||
|
this.#currentSpec = spec;
|
||||||
|
}
|
||||||
|
|
||||||
|
currentSuite() {
|
||||||
|
return this.#currentlyExecutingSuites[
|
||||||
|
this.#currentlyExecutingSuites.length - 1
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
pushSuite(suite) {
|
||||||
|
this.#currentlyExecutingSuites.push(suite);
|
||||||
|
}
|
||||||
|
|
||||||
|
popSuite() {
|
||||||
|
this.#currentlyExecutingSuites.pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return CurrentRunableTracker;
|
||||||
|
};
|
||||||
@@ -6,7 +6,7 @@ getJasmineRequireObj().DelayedFunctionScheduler = function(j$) {
|
|||||||
this.scheduledLookup_ = [];
|
this.scheduledLookup_ = [];
|
||||||
this.scheduledFunctions_ = {};
|
this.scheduledFunctions_ = {};
|
||||||
this.currentTime_ = 0;
|
this.currentTime_ = 0;
|
||||||
this.delayedFnCount_ = 0;
|
this.delayedFnStartCount_ = 1e12; // arbitrarily large number to avoid collisions with native timer IDs;
|
||||||
this.deletedKeys_ = [];
|
this.deletedKeys_ = [];
|
||||||
|
|
||||||
this.tick = function(millis, tickDate) {
|
this.tick = function(millis, tickDate) {
|
||||||
@@ -38,7 +38,7 @@ getJasmineRequireObj().DelayedFunctionScheduler = function(j$) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
millis = millis || 0;
|
millis = millis || 0;
|
||||||
timeoutKey = timeoutKey || ++this.delayedFnCount_;
|
timeoutKey = timeoutKey || ++this.delayedFnStartCount_;
|
||||||
runAtMillis = runAtMillis || this.currentTime_ + millis;
|
runAtMillis = runAtMillis || this.currentTime_ + millis;
|
||||||
|
|
||||||
const funcToSchedule = {
|
const funcToSchedule = {
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ getJasmineRequireObj().Deprecator = function(j$) {
|
|||||||
|
|
||||||
Deprecator.prototype.stackTrace_ = function() {
|
Deprecator.prototype.stackTrace_ = function() {
|
||||||
const formatter = new j$.ExceptionFormatter();
|
const formatter = new j$.ExceptionFormatter();
|
||||||
return formatter.stack(j$.util.errorWithStack()).replace(/^Error\n/m, '');
|
return formatter.stack(new Error()).replace(/^Error\n/m, '');
|
||||||
};
|
};
|
||||||
|
|
||||||
Deprecator.prototype.report_ = function(runnable, deprecation, options) {
|
Deprecator.prototype.report_ = function(runnable, deprecation, options) {
|
||||||
|
|||||||
290
src/core/Env.js
290
src/core/Env.js
@@ -1,4 +1,6 @@
|
|||||||
getJasmineRequireObj().Env = function(j$) {
|
getJasmineRequireObj().Env = function(j$) {
|
||||||
|
const DEFAULT_IT_DESCRIBE_STACK_DEPTH = 3;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @class Env
|
* @class Env
|
||||||
* @since 2.0.0
|
* @since 2.0.0
|
||||||
@@ -7,11 +9,12 @@ getJasmineRequireObj().Env = function(j$) {
|
|||||||
* calling {@link jasmine.getEnv}.
|
* calling {@link jasmine.getEnv}.
|
||||||
* @hideconstructor
|
* @hideconstructor
|
||||||
*/
|
*/
|
||||||
function Env(options) {
|
function Env(envOptions) {
|
||||||
options = options || {};
|
envOptions = envOptions || {};
|
||||||
|
|
||||||
const self = this;
|
const self = this;
|
||||||
const global = options.global || j$.getGlobal();
|
const GlobalErrors = envOptions.GlobalErrors || j$.GlobalErrors;
|
||||||
|
const global = envOptions.global || j$.getGlobal();
|
||||||
|
|
||||||
const realSetTimeout = global.setTimeout;
|
const realSetTimeout = global.setTimeout;
|
||||||
const realClearTimeout = global.clearTimeout;
|
const realClearTimeout = global.clearTimeout;
|
||||||
@@ -24,13 +27,27 @@ getJasmineRequireObj().Env = function(j$) {
|
|||||||
new j$.MockDate(global)
|
new j$.MockDate(global)
|
||||||
);
|
);
|
||||||
|
|
||||||
const globalErrors = new j$.GlobalErrors();
|
const globalErrors = new GlobalErrors(
|
||||||
const installGlobalErrors = (function() {
|
undefined,
|
||||||
|
// Configuration is late-bound because GlobalErrors needs to be constructed
|
||||||
|
// before it's set to detect load-time errors in browsers
|
||||||
|
() => this.configuration()
|
||||||
|
);
|
||||||
|
const { installGlobalErrors, uninstallGlobalErrors } = (function() {
|
||||||
let installed = false;
|
let installed = false;
|
||||||
return function() {
|
|
||||||
if (!installed) {
|
return {
|
||||||
globalErrors.install();
|
installGlobalErrors() {
|
||||||
installed = true;
|
if (!installed) {
|
||||||
|
globalErrors.install();
|
||||||
|
installed = true;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
uninstallGlobalErrors() {
|
||||||
|
if (installed) {
|
||||||
|
globalErrors.uninstall();
|
||||||
|
installed = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
})();
|
})();
|
||||||
@@ -43,125 +60,14 @@ getJasmineRequireObj().Env = function(j$) {
|
|||||||
globalErrors
|
globalErrors
|
||||||
});
|
});
|
||||||
|
|
||||||
let reporter;
|
let reportDispatcher;
|
||||||
let topSuite;
|
let topSuite;
|
||||||
let runner;
|
let runner;
|
||||||
let parallelLoadingState = null; // 'specs', 'helpers', or null for non-parallel
|
let parallelLoadingState = null; // 'specs', 'helpers', or null for non-parallel
|
||||||
|
|
||||||
/**
|
const config = new j$.Configuration();
|
||||||
* This represents the available options to configure Jasmine.
|
|
||||||
* Options that are not provided will use their default values.
|
|
||||||
* @see Env#configure
|
|
||||||
* @interface Configuration
|
|
||||||
* @since 3.3.0
|
|
||||||
*/
|
|
||||||
const config = {
|
|
||||||
/**
|
|
||||||
* Whether to randomize spec execution order
|
|
||||||
* @name Configuration#random
|
|
||||||
* @since 3.3.0
|
|
||||||
* @type Boolean
|
|
||||||
* @default true
|
|
||||||
*/
|
|
||||||
random: true,
|
|
||||||
/**
|
|
||||||
* Seed to use as the basis of randomization.
|
|
||||||
* Null causes the seed to be determined randomly at the start of execution.
|
|
||||||
* @name Configuration#seed
|
|
||||||
* @since 3.3.0
|
|
||||||
* @type (number|string)
|
|
||||||
* @default null
|
|
||||||
*/
|
|
||||||
seed: null,
|
|
||||||
/**
|
|
||||||
* Whether to stop execution of the suite after the first spec failure
|
|
||||||
*
|
|
||||||
* <p>In parallel mode, `stopOnSpecFailure` works on a "best effort"
|
|
||||||
* basis. Jasmine will stop execution as soon as practical after a failure
|
|
||||||
* but it might not be immediate.</p>
|
|
||||||
* @name Configuration#stopOnSpecFailure
|
|
||||||
* @since 3.9.0
|
|
||||||
* @type Boolean
|
|
||||||
* @default false
|
|
||||||
*/
|
|
||||||
stopOnSpecFailure: false,
|
|
||||||
/**
|
|
||||||
* Whether to fail the spec if it ran no expectations. By default
|
|
||||||
* a spec that ran no expectations is reported as passed. Setting this
|
|
||||||
* to true will report such spec as a failure.
|
|
||||||
* @name Configuration#failSpecWithNoExpectations
|
|
||||||
* @since 3.5.0
|
|
||||||
* @type Boolean
|
|
||||||
* @default false
|
|
||||||
*/
|
|
||||||
failSpecWithNoExpectations: false,
|
|
||||||
/**
|
|
||||||
* Whether to cause specs to only have one expectation failure.
|
|
||||||
* @name Configuration#stopSpecOnExpectationFailure
|
|
||||||
* @since 3.3.0
|
|
||||||
* @type Boolean
|
|
||||||
* @default false
|
|
||||||
*/
|
|
||||||
stopSpecOnExpectationFailure: false,
|
|
||||||
/**
|
|
||||||
* A function that takes a spec and returns true if it should be executed
|
|
||||||
* or false if it should be skipped.
|
|
||||||
* @callback SpecFilter
|
|
||||||
* @param {Spec} spec - The spec that the filter is being applied to.
|
|
||||||
* @return boolean
|
|
||||||
*/
|
|
||||||
/**
|
|
||||||
* Function to use to filter specs
|
|
||||||
* @name Configuration#specFilter
|
|
||||||
* @since 3.3.0
|
|
||||||
* @type SpecFilter
|
|
||||||
* @default A function that always returns true.
|
|
||||||
*/
|
|
||||||
specFilter: function() {
|
|
||||||
return true;
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* Whether or not reporters should hide disabled specs from their output.
|
|
||||||
* Currently only supported by Jasmine's HTMLReporter
|
|
||||||
* @name Configuration#hideDisabled
|
|
||||||
* @since 3.3.0
|
|
||||||
* @type Boolean
|
|
||||||
* @default false
|
|
||||||
*/
|
|
||||||
hideDisabled: false,
|
|
||||||
/**
|
|
||||||
* Clean closures when a suite is done running (done by clearing the stored function reference).
|
|
||||||
* This prevents memory leaks, but you won't be able to run jasmine multiple times.
|
|
||||||
* @name Configuration#autoCleanClosures
|
|
||||||
* @since 3.10.0
|
|
||||||
* @type boolean
|
|
||||||
* @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
|
|
||||||
* for methods that tend to be called frequently will be issued only once
|
|
||||||
* or otherwise throttled to to prevent the suite output from being flooded
|
|
||||||
* with warnings.
|
|
||||||
* @name Configuration#verboseDeprecations
|
|
||||||
* @since 3.6.0
|
|
||||||
* @type Boolean
|
|
||||||
* @default false
|
|
||||||
*/
|
|
||||||
verboseDeprecations: false
|
|
||||||
};
|
|
||||||
|
|
||||||
if (!options.suppressLoadErrors) {
|
if (!envOptions.suppressLoadErrors) {
|
||||||
installGlobalErrors();
|
installGlobalErrors();
|
||||||
globalErrors.pushListener(function loadtimeErrorHandler(error, event) {
|
globalErrors.pushListener(function loadtimeErrorHandler(error, event) {
|
||||||
topSuite.result.failedExpectations.push({
|
topSuite.result.failedExpectations.push({
|
||||||
@@ -182,41 +88,15 @@ getJasmineRequireObj().Env = function(j$) {
|
|||||||
* @argument {Configuration} configuration
|
* @argument {Configuration} configuration
|
||||||
* @function
|
* @function
|
||||||
*/
|
*/
|
||||||
this.configure = function(configuration) {
|
this.configure = function(changes) {
|
||||||
if (parallelLoadingState) {
|
if (parallelLoadingState) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
'Jasmine cannot be configured via Env in parallel mode'
|
'Jasmine cannot be configured via Env in parallel mode'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const booleanProps = [
|
config.update(changes);
|
||||||
'random',
|
deprecator.verboseDeprecations(config.verboseDeprecations);
|
||||||
'failSpecWithNoExpectations',
|
|
||||||
'hideDisabled',
|
|
||||||
'stopOnSpecFailure',
|
|
||||||
'stopSpecOnExpectationFailure',
|
|
||||||
'autoCleanClosures',
|
|
||||||
'forbidDuplicateNames'
|
|
||||||
];
|
|
||||||
|
|
||||||
booleanProps.forEach(function(prop) {
|
|
||||||
if (typeof configuration[prop] !== 'undefined') {
|
|
||||||
config[prop] = !!configuration[prop];
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (configuration.specFilter) {
|
|
||||||
config.specFilter = configuration.specFilter;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof configuration.seed !== 'undefined') {
|
|
||||||
config.seed = configuration.seed;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (configuration.hasOwnProperty('verboseDeprecations')) {
|
|
||||||
config.verboseDeprecations = configuration.verboseDeprecations;
|
|
||||||
deprecator.verboseDeprecations(config.verboseDeprecations);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -227,11 +107,7 @@ getJasmineRequireObj().Env = function(j$) {
|
|||||||
* @returns {Configuration}
|
* @returns {Configuration}
|
||||||
*/
|
*/
|
||||||
this.configuration = function() {
|
this.configuration = function() {
|
||||||
const result = {};
|
return config.copy();
|
||||||
for (const property in config) {
|
|
||||||
result[property] = config[property];
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
this.setDefaultSpyStrategy = function(defaultStrategyFn) {
|
this.setDefaultSpyStrategy = function(defaultStrategyFn) {
|
||||||
@@ -434,7 +310,7 @@ getJasmineRequireObj().Env = function(j$) {
|
|||||||
deprecator.addDeprecationWarning(runable, deprecation, options);
|
deprecator.addDeprecationWarning(runable, deprecation, options);
|
||||||
};
|
};
|
||||||
|
|
||||||
function queueRunnerFactory(options) {
|
function runQueue(options) {
|
||||||
options.clearStack = options.clearStack || clearStack;
|
options.clearStack = options.clearStack || clearStack;
|
||||||
options.timeout = {
|
options.timeout = {
|
||||||
setTimeout: realSetTimeout,
|
setTimeout: realSetTimeout,
|
||||||
@@ -456,9 +332,7 @@ getJasmineRequireObj().Env = function(j$) {
|
|||||||
expectationFactory,
|
expectationFactory,
|
||||||
asyncExpectationFactory,
|
asyncExpectationFactory,
|
||||||
onLateError: recordLateError,
|
onLateError: recordLateError,
|
||||||
specResultCallback,
|
runQueue
|
||||||
specStarted,
|
|
||||||
queueRunnerFactory
|
|
||||||
});
|
});
|
||||||
topSuite = suiteBuilder.topSuite;
|
topSuite = suiteBuilder.topSuite;
|
||||||
const deprecator = new j$.Deprecator(topSuite);
|
const deprecator = new j$.Deprecator(topSuite);
|
||||||
@@ -481,11 +355,11 @@ getJasmineRequireObj().Env = function(j$) {
|
|||||||
* @interface Reporter
|
* @interface Reporter
|
||||||
* @see custom_reporter
|
* @see custom_reporter
|
||||||
*/
|
*/
|
||||||
reporter = new j$.ReportDispatcher(
|
reportDispatcher = new j$.ReportDispatcher(
|
||||||
j$.reporterEvents,
|
j$.reporterEvents,
|
||||||
function(options) {
|
function(options) {
|
||||||
options.SkipPolicy = j$.NeverSkipPolicy;
|
options.SkipPolicy = j$.NeverSkipPolicy;
|
||||||
return queueRunnerFactory(options);
|
return runQueue(options);
|
||||||
},
|
},
|
||||||
recordLateError
|
recordLateError
|
||||||
);
|
);
|
||||||
@@ -495,10 +369,11 @@ getJasmineRequireObj().Env = function(j$) {
|
|||||||
totalSpecsDefined: () => suiteBuilder.totalSpecsDefined,
|
totalSpecsDefined: () => suiteBuilder.totalSpecsDefined,
|
||||||
focusedRunables: () => suiteBuilder.focusedRunables,
|
focusedRunables: () => suiteBuilder.focusedRunables,
|
||||||
runableResources,
|
runableResources,
|
||||||
reporter,
|
reportDispatcher,
|
||||||
queueRunnerFactory,
|
runQueue,
|
||||||
getConfig: () => config,
|
TreeProcessor: j$.TreeProcessor,
|
||||||
reportSpecDone
|
globalErrors,
|
||||||
|
getConfig: () => config
|
||||||
});
|
});
|
||||||
|
|
||||||
this.setParallelLoadingState = function(state) {
|
this.setParallelLoadingState = function(state) {
|
||||||
@@ -561,7 +436,7 @@ getJasmineRequireObj().Env = function(j$) {
|
|||||||
throw new Error('Reporters cannot be added via Env in parallel mode');
|
throw new Error('Reporters cannot be added via Env in parallel mode');
|
||||||
}
|
}
|
||||||
|
|
||||||
reporter.addReporter(reporterToAdd);
|
reportDispatcher.addReporter(reporterToAdd);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -573,7 +448,7 @@ getJasmineRequireObj().Env = function(j$) {
|
|||||||
* @see custom_reporter
|
* @see custom_reporter
|
||||||
*/
|
*/
|
||||||
this.provideFallbackReporter = function(reporterToAdd) {
|
this.provideFallbackReporter = function(reporterToAdd) {
|
||||||
reporter.provideFallbackReporter(reporterToAdd);
|
reportDispatcher.provideFallbackReporter(reporterToAdd);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -587,7 +462,7 @@ getJasmineRequireObj().Env = function(j$) {
|
|||||||
throw new Error('Reporters cannot be removed via Env in parallel mode');
|
throw new Error('Reporters cannot be removed via Env in parallel mode');
|
||||||
}
|
}
|
||||||
|
|
||||||
reporter.clearReporters();
|
reportDispatcher.clearReporters();
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -720,14 +595,14 @@ getJasmineRequireObj().Env = function(j$) {
|
|||||||
|
|
||||||
this.describe = function(description, definitionFn) {
|
this.describe = function(description, definitionFn) {
|
||||||
ensureIsNotNested('describe');
|
ensureIsNotNested('describe');
|
||||||
const filename = callerCallerFilename();
|
const filename = indirectCallerFilename(describeStackDepth());
|
||||||
return suiteBuilder.describe(description, definitionFn, filename)
|
return suiteBuilder.describe(description, definitionFn, filename)
|
||||||
.metadata;
|
.metadata;
|
||||||
};
|
};
|
||||||
|
|
||||||
this.xdescribe = function(description, definitionFn) {
|
this.xdescribe = function(description, definitionFn) {
|
||||||
ensureIsNotNested('xdescribe');
|
ensureIsNotNested('xdescribe');
|
||||||
const filename = callerCallerFilename();
|
const filename = indirectCallerFilename(describeStackDepth());
|
||||||
return suiteBuilder.xdescribe(description, definitionFn, filename)
|
return suiteBuilder.xdescribe(description, definitionFn, filename)
|
||||||
.metadata;
|
.metadata;
|
||||||
};
|
};
|
||||||
@@ -735,52 +610,58 @@ getJasmineRequireObj().Env = function(j$) {
|
|||||||
this.fdescribe = function(description, definitionFn) {
|
this.fdescribe = function(description, definitionFn) {
|
||||||
ensureIsNotNested('fdescribe');
|
ensureIsNotNested('fdescribe');
|
||||||
ensureNonParallel('fdescribe');
|
ensureNonParallel('fdescribe');
|
||||||
const filename = callerCallerFilename();
|
const filename = indirectCallerFilename(describeStackDepth());
|
||||||
return suiteBuilder.fdescribe(description, definitionFn, filename)
|
return suiteBuilder.fdescribe(description, definitionFn, filename)
|
||||||
.metadata;
|
.metadata;
|
||||||
};
|
};
|
||||||
|
|
||||||
function specResultCallback(spec, result, next) {
|
|
||||||
runableResources.clearForRunable(spec.id);
|
|
||||||
runner.currentSpec = null;
|
|
||||||
|
|
||||||
if (result.status === 'failed') {
|
|
||||||
runner.hasFailures = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
reportSpecDone(spec, result, next);
|
|
||||||
}
|
|
||||||
|
|
||||||
function specStarted(spec, suite, next) {
|
|
||||||
runner.currentSpec = spec;
|
|
||||||
runableResources.initForRunable(spec.id, suite.id);
|
|
||||||
reporter.specStarted(spec.result).then(next);
|
|
||||||
}
|
|
||||||
|
|
||||||
function reportSpecDone(spec, result, next) {
|
|
||||||
spec.reportedDone = true;
|
|
||||||
reporter.specDone(result).then(next);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.it = function(description, fn, timeout) {
|
this.it = function(description, fn, timeout) {
|
||||||
ensureIsNotNested('it');
|
ensureIsNotNested('it');
|
||||||
const filename = callerCallerFilename();
|
const filename = indirectCallerFilename(itStackDepth());
|
||||||
return suiteBuilder.it(description, fn, timeout, filename).metadata;
|
return suiteBuilder.it(description, fn, timeout, filename).metadata;
|
||||||
};
|
};
|
||||||
|
|
||||||
this.xit = function(description, fn, timeout) {
|
this.xit = function(description, fn, timeout) {
|
||||||
ensureIsNotNested('xit');
|
ensureIsNotNested('xit');
|
||||||
const filename = callerCallerFilename();
|
const filename = indirectCallerFilename(itStackDepth());
|
||||||
return suiteBuilder.xit(description, fn, timeout, filename).metadata;
|
return suiteBuilder.xit(description, fn, timeout, filename).metadata;
|
||||||
};
|
};
|
||||||
|
|
||||||
this.fit = function(description, fn, timeout) {
|
this.fit = function(description, fn, timeout) {
|
||||||
ensureIsNotNested('fit');
|
ensureIsNotNested('fit');
|
||||||
ensureNonParallel('fit');
|
ensureNonParallel('fit');
|
||||||
const filename = callerCallerFilename();
|
const filename = indirectCallerFilename(itStackDepth());
|
||||||
return suiteBuilder.fit(description, fn, timeout, filename).metadata;
|
return suiteBuilder.fit(description, fn, timeout, filename).metadata;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function itStackDepth() {
|
||||||
|
return DEFAULT_IT_DESCRIBE_STACK_DEPTH + config.extraItStackFrames;
|
||||||
|
}
|
||||||
|
|
||||||
|
function describeStackDepth() {
|
||||||
|
return DEFAULT_IT_DESCRIBE_STACK_DEPTH + config.extraDescribeStackFrames;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a user-defined property as part of the properties field of {@link SpecResult}
|
||||||
|
* @name Env#getSpecProperty
|
||||||
|
* @since 5.10.0
|
||||||
|
* @function
|
||||||
|
* @param {String} key The name of the property
|
||||||
|
* @returns {*} The value of the property
|
||||||
|
*/
|
||||||
|
this.getSpecProperty = function(key) {
|
||||||
|
if (
|
||||||
|
!runner.currentRunable() ||
|
||||||
|
runner.currentRunable() == runner.currentSuite()
|
||||||
|
) {
|
||||||
|
throw new Error(
|
||||||
|
"'getSpecProperty' was used when there was no current spec"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return runner.currentRunable().getSpecProperty(key);
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets a user-defined property that will be provided to reporters as part of the properties field of {@link SpecResult}
|
* Sets a user-defined property that will be provided to reporters as part of the properties field of {@link SpecResult}
|
||||||
* @name Env#setSpecProperty
|
* @name Env#setSpecProperty
|
||||||
@@ -942,17 +823,16 @@ getJasmineRequireObj().Env = function(j$) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
this.cleanup_ = function() {
|
this.cleanup_ = function() {
|
||||||
if (globalErrors) {
|
uninstallGlobalErrors();
|
||||||
globalErrors.uninstall();
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function callerCallerFilename() {
|
function indirectCallerFilename(depth) {
|
||||||
const frames = new j$.StackTrace(new Error()).frames;
|
const frames = new j$.StackTrace(new Error()).frames;
|
||||||
// frames[3] should always exist except in Jasmine's own tests, which bypass
|
// The specified frame should always exist except in Jasmine's own tests,
|
||||||
// the global it/describe layer, but don't crash if it doesn't.
|
// which bypass the global it/describe layer, but could be absent in case
|
||||||
return frames[3] && frames[3].file;
|
// of misconfiguration. Don't crash if it's absent.
|
||||||
|
return frames[depth] && frames[depth].file;
|
||||||
}
|
}
|
||||||
|
|
||||||
return Env;
|
return Env;
|
||||||
|
|||||||
@@ -148,7 +148,7 @@ getJasmineRequireObj().Expectation = function(j$) {
|
|||||||
return function() {
|
return function() {
|
||||||
// Capture the call stack here, before we go async, so that it will contain
|
// Capture the call stack here, before we go async, so that it will contain
|
||||||
// frames that are relevant to the user instead of just parts of Jasmine.
|
// frames that are relevant to the user instead of just parts of Jasmine.
|
||||||
const errorForStack = j$.util.errorWithStack();
|
const errorForStack = new Error();
|
||||||
|
|
||||||
return this.expector
|
return this.expector
|
||||||
.compare(name, matcherFactory, arguments)
|
.compare(name, matcherFactory, arguments)
|
||||||
|
|||||||
@@ -1,133 +1,44 @@
|
|||||||
getJasmineRequireObj().GlobalErrors = function(j$) {
|
getJasmineRequireObj().GlobalErrors = function(j$) {
|
||||||
function GlobalErrors(global) {
|
class GlobalErrors {
|
||||||
global = global || j$.getGlobal();
|
#getConfig;
|
||||||
|
#adapter;
|
||||||
|
#handlers;
|
||||||
|
#overrideHandler;
|
||||||
|
#onRemoveOverrideHandler;
|
||||||
|
#pendingUnhandledRejections;
|
||||||
|
|
||||||
const handlers = [];
|
constructor(global, getConfig) {
|
||||||
let overrideHandler = null,
|
global = global || j$.getGlobal();
|
||||||
onRemoveOverrideHandler = null;
|
this.#getConfig = getConfig;
|
||||||
|
this.#pendingUnhandledRejections = new Map();
|
||||||
|
this.#handlers = [];
|
||||||
|
this.#overrideHandler = null;
|
||||||
|
this.#onRemoveOverrideHandler = null;
|
||||||
|
|
||||||
function onBrowserError(event) {
|
const dispatch = {
|
||||||
dispatchBrowserError(event.error, event);
|
onUncaughtException: this.#onUncaughtException.bind(this),
|
||||||
}
|
onUnhandledRejection: this.#onUnhandledRejection.bind(this),
|
||||||
|
onRejectionHandled: this.#onRejectionHandled.bind(this)
|
||||||
function dispatchBrowserError(error, event) {
|
|
||||||
if (overrideHandler) {
|
|
||||||
// See discussion of spyOnGlobalErrorsAsync in base.js
|
|
||||||
overrideHandler(error);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const handler = handlers[handlers.length - 1];
|
|
||||||
|
|
||||||
if (handler) {
|
|
||||||
handler(error, event);
|
|
||||||
} else {
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.originalHandlers = {};
|
|
||||||
this.jasmineHandlers = {};
|
|
||||||
this.installOne_ = function installOne_(errorType, jasmineMessage) {
|
|
||||||
function taggedOnError(error) {
|
|
||||||
if (j$.isError_(error)) {
|
|
||||||
error.jasmineMessage = jasmineMessage + ': ' + error;
|
|
||||||
} else {
|
|
||||||
let substituteMsg;
|
|
||||||
|
|
||||||
if (error) {
|
|
||||||
substituteMsg = jasmineMessage + ': ' + error;
|
|
||||||
} else {
|
|
||||||
substituteMsg = jasmineMessage + ' with no error or message';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (errorType === 'unhandledRejection') {
|
|
||||||
substituteMsg +=
|
|
||||||
'\n' +
|
|
||||||
'(Tip: to get a useful stack trace, use ' +
|
|
||||||
'Promise.reject(new Error(...)) instead of Promise.reject(' +
|
|
||||||
(error ? '...' : '') +
|
|
||||||
').)';
|
|
||||||
}
|
|
||||||
|
|
||||||
error = new Error(substituteMsg);
|
|
||||||
}
|
|
||||||
|
|
||||||
const handler = handlers[handlers.length - 1];
|
|
||||||
|
|
||||||
if (overrideHandler) {
|
|
||||||
// See discussion of spyOnGlobalErrorsAsync in base.js
|
|
||||||
overrideHandler(error);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (handler) {
|
|
||||||
handler(error);
|
|
||||||
} else {
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.originalHandlers[errorType] = global.process.listeners(errorType);
|
|
||||||
this.jasmineHandlers[errorType] = taggedOnError;
|
|
||||||
|
|
||||||
global.process.removeAllListeners(errorType);
|
|
||||||
global.process.on(errorType, taggedOnError);
|
|
||||||
|
|
||||||
this.uninstall = function uninstall() {
|
|
||||||
const errorTypes = Object.keys(this.originalHandlers);
|
|
||||||
for (const errorType of errorTypes) {
|
|
||||||
global.process.removeListener(
|
|
||||||
errorType,
|
|
||||||
this.jasmineHandlers[errorType]
|
|
||||||
);
|
|
||||||
|
|
||||||
for (let i = 0; i < this.originalHandlers[errorType].length; i++) {
|
|
||||||
global.process.on(errorType, this.originalHandlers[errorType][i]);
|
|
||||||
}
|
|
||||||
delete this.originalHandlers[errorType];
|
|
||||||
delete this.jasmineHandlers[errorType];
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
};
|
|
||||||
|
|
||||||
this.install = function install() {
|
|
||||||
if (
|
if (
|
||||||
global.process &&
|
global.process &&
|
||||||
global.process.listeners &&
|
global.process.listeners &&
|
||||||
j$.isFunction_(global.process.on)
|
j$.isFunction_(global.process.on)
|
||||||
) {
|
) {
|
||||||
this.installOne_('uncaughtException', 'Uncaught exception');
|
this.#adapter = new NodeAdapter(global, dispatch);
|
||||||
this.installOne_('unhandledRejection', 'Unhandled promise rejection');
|
|
||||||
} else {
|
} else {
|
||||||
global.addEventListener('error', onBrowserError);
|
this.#adapter = new BrowserAdapter(global, dispatch);
|
||||||
|
|
||||||
const browserRejectionHandler = function browserRejectionHandler(
|
|
||||||
event
|
|
||||||
) {
|
|
||||||
if (j$.isError_(event.reason)) {
|
|
||||||
event.reason.jasmineMessage =
|
|
||||||
'Unhandled promise rejection: ' + event.reason;
|
|
||||||
dispatchBrowserError(event.reason, event);
|
|
||||||
} else {
|
|
||||||
dispatchBrowserError(
|
|
||||||
'Unhandled promise rejection: ' + event.reason,
|
|
||||||
event
|
|
||||||
);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
global.addEventListener('unhandledrejection', browserRejectionHandler);
|
|
||||||
|
|
||||||
this.uninstall = function uninstall() {
|
|
||||||
global.removeEventListener('error', onBrowserError);
|
|
||||||
global.removeEventListener(
|
|
||||||
'unhandledrejection',
|
|
||||||
browserRejectionHandler
|
|
||||||
);
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
|
install() {
|
||||||
|
this.#adapter.install();
|
||||||
|
}
|
||||||
|
|
||||||
|
uninstall() {
|
||||||
|
this.#adapter.uninstall();
|
||||||
|
}
|
||||||
|
|
||||||
// The listener at the top of the stack will be called with two arguments:
|
// The listener at the top of the stack will be called with two arguments:
|
||||||
// the error and the event. Either of them may be falsy.
|
// the error and the event. Either of them may be falsy.
|
||||||
@@ -136,35 +47,246 @@ getJasmineRequireObj().GlobalErrors = function(j$) {
|
|||||||
// browsers but will be falsy in Node.
|
// browsers but will be falsy in Node.
|
||||||
// Listeners that are pushed after spec files have been loaded should be
|
// Listeners that are pushed after spec files have been loaded should be
|
||||||
// able to just use the error parameter.
|
// able to just use the error parameter.
|
||||||
this.pushListener = function pushListener(listener) {
|
pushListener(listener) {
|
||||||
handlers.push(listener);
|
this.#handlers.push(listener);
|
||||||
};
|
}
|
||||||
|
|
||||||
this.popListener = function popListener(listener) {
|
popListener(listener) {
|
||||||
if (!listener) {
|
if (!listener) {
|
||||||
throw new Error('popListener expects a listener');
|
throw new Error('popListener expects a listener');
|
||||||
}
|
}
|
||||||
|
|
||||||
handlers.pop();
|
this.#handlers.pop();
|
||||||
};
|
}
|
||||||
|
|
||||||
this.setOverrideListener = function(listener, onRemove) {
|
setOverrideListener(listener, onRemove) {
|
||||||
if (overrideHandler) {
|
if (this.#overrideHandler) {
|
||||||
throw new Error("Can't set more than one override listener at a time");
|
throw new Error("Can't set more than one override listener at a time");
|
||||||
}
|
}
|
||||||
|
|
||||||
overrideHandler = listener;
|
this.#overrideHandler = listener;
|
||||||
onRemoveOverrideHandler = onRemove;
|
this.#onRemoveOverrideHandler = onRemove;
|
||||||
};
|
}
|
||||||
|
|
||||||
this.removeOverrideListener = function() {
|
removeOverrideListener() {
|
||||||
if (onRemoveOverrideHandler) {
|
if (this.#onRemoveOverrideHandler) {
|
||||||
onRemoveOverrideHandler();
|
this.#onRemoveOverrideHandler();
|
||||||
}
|
}
|
||||||
|
|
||||||
overrideHandler = null;
|
this.#overrideHandler = null;
|
||||||
onRemoveOverrideHandler = null;
|
this.#onRemoveOverrideHandler = null;
|
||||||
};
|
}
|
||||||
|
|
||||||
|
reportUnhandledRejections() {
|
||||||
|
for (const {
|
||||||
|
reason,
|
||||||
|
event
|
||||||
|
} of this.#pendingUnhandledRejections.values()) {
|
||||||
|
this.#dispatchError(reason, event);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.#pendingUnhandledRejections.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Either error or event may be undefined
|
||||||
|
#onUncaughtException(error, event) {
|
||||||
|
this.#dispatchError(error, event);
|
||||||
|
}
|
||||||
|
|
||||||
|
// event or promise may be undefined
|
||||||
|
// event is passed through for backwards compatibility reasons. It's probably
|
||||||
|
// unnecessary, but user code could depend on it.
|
||||||
|
#onUnhandledRejection(reason, promise, event) {
|
||||||
|
if (this.#detectLateRejectionHandling() && promise) {
|
||||||
|
this.#pendingUnhandledRejections.set(promise, { reason, event });
|
||||||
|
} else {
|
||||||
|
this.#dispatchError(reason, event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#detectLateRejectionHandling() {
|
||||||
|
return this.#getConfig().detectLateRejectionHandling;
|
||||||
|
}
|
||||||
|
|
||||||
|
#onRejectionHandled(promise) {
|
||||||
|
this.#pendingUnhandledRejections.delete(promise);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Either error or event may be undefined
|
||||||
|
#dispatchError(error, event) {
|
||||||
|
if (this.#overrideHandler) {
|
||||||
|
// See discussion of spyOnGlobalErrorsAsync in base.js
|
||||||
|
this.#overrideHandler(error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const handler = this.#handlers[this.#handlers.length - 1];
|
||||||
|
|
||||||
|
if (handler) {
|
||||||
|
handler(error, event);
|
||||||
|
} else {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class BrowserAdapter {
|
||||||
|
#global;
|
||||||
|
#dispatch;
|
||||||
|
#onError;
|
||||||
|
#onUnhandledRejection;
|
||||||
|
#onRejectionHandled;
|
||||||
|
|
||||||
|
constructor(global, dispatch) {
|
||||||
|
this.#global = global;
|
||||||
|
this.#dispatch = dispatch;
|
||||||
|
this.#onError = event => dispatch.onUncaughtException(event.error, event);
|
||||||
|
this.#onUnhandledRejection = this.#unhandledRejectionHandler.bind(this);
|
||||||
|
this.#onRejectionHandled = this.#rejectionHandledHandler.bind(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
install() {
|
||||||
|
this.#global.addEventListener('error', this.#onError);
|
||||||
|
this.#global.addEventListener(
|
||||||
|
'unhandledrejection',
|
||||||
|
this.#onUnhandledRejection
|
||||||
|
);
|
||||||
|
this.#global.addEventListener(
|
||||||
|
'rejectionhandled',
|
||||||
|
this.#onRejectionHandled
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
uninstall() {
|
||||||
|
this.#global.removeEventListener('error', this.#onError);
|
||||||
|
this.#global.removeEventListener(
|
||||||
|
'unhandledrejection',
|
||||||
|
this.#onUnhandledRejection
|
||||||
|
);
|
||||||
|
this.#global.removeEventListener(
|
||||||
|
'rejectionhandled',
|
||||||
|
this.#onRejectionHandled
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#unhandledRejectionHandler(event) {
|
||||||
|
const jasmineMessage = 'Unhandled promise rejection: ' + event.reason;
|
||||||
|
let reason;
|
||||||
|
|
||||||
|
if (j$.isError_(event.reason)) {
|
||||||
|
reason = event.reason;
|
||||||
|
reason.jasmineMessage = jasmineMessage;
|
||||||
|
} else {
|
||||||
|
reason = jasmineMessage;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.#dispatch.onUnhandledRejection(reason, event.promise, event);
|
||||||
|
}
|
||||||
|
|
||||||
|
#rejectionHandledHandler(event) {
|
||||||
|
this.#dispatch.onRejectionHandled(event.promise);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class NodeAdapter {
|
||||||
|
#global;
|
||||||
|
#dispatch;
|
||||||
|
#originalHandlers;
|
||||||
|
#jasmineHandlers;
|
||||||
|
|
||||||
|
constructor(global, dispatch) {
|
||||||
|
this.#global = global;
|
||||||
|
this.#dispatch = dispatch;
|
||||||
|
|
||||||
|
this.#jasmineHandlers = {};
|
||||||
|
this.#originalHandlers = {};
|
||||||
|
|
||||||
|
this.onError = this.onError.bind(this);
|
||||||
|
this.onUnhandledRejection = this.onUnhandledRejection.bind(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
install() {
|
||||||
|
this.#installHandler('uncaughtException', this.onError);
|
||||||
|
this.#installHandler('unhandledRejection', this.onUnhandledRejection);
|
||||||
|
this.#installHandler(
|
||||||
|
'rejectionHandled',
|
||||||
|
this.#dispatch.onRejectionHandled
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
uninstall() {
|
||||||
|
const errorTypes = Object.keys(this.#originalHandlers);
|
||||||
|
for (const errorType of errorTypes) {
|
||||||
|
this.#global.process.removeListener(
|
||||||
|
errorType,
|
||||||
|
this.#jasmineHandlers[errorType]
|
||||||
|
);
|
||||||
|
|
||||||
|
for (let i = 0; i < this.#originalHandlers[errorType].length; i++) {
|
||||||
|
this.#global.process.on(
|
||||||
|
errorType,
|
||||||
|
this.#originalHandlers[errorType][i]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
delete this.#originalHandlers[errorType];
|
||||||
|
delete this.#jasmineHandlers[errorType];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#installHandler(errorType, handler) {
|
||||||
|
this.#originalHandlers[errorType] = this.#global.process.listeners(
|
||||||
|
errorType
|
||||||
|
);
|
||||||
|
this.#jasmineHandlers[errorType] = handler;
|
||||||
|
|
||||||
|
this.#global.process.removeAllListeners(errorType);
|
||||||
|
this.#global.process.on(errorType, handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
#augmentError(error, isUnhandledRejection) {
|
||||||
|
let jasmineMessagePrefix;
|
||||||
|
|
||||||
|
if (isUnhandledRejection) {
|
||||||
|
jasmineMessagePrefix = 'Unhandled promise rejection';
|
||||||
|
} else {
|
||||||
|
jasmineMessagePrefix = 'Uncaught exception';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (j$.isError_(error)) {
|
||||||
|
error.jasmineMessage = jasmineMessagePrefix + ': ' + error;
|
||||||
|
return error;
|
||||||
|
} else {
|
||||||
|
let substituteMsg;
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
substituteMsg = jasmineMessagePrefix + ': ' + error;
|
||||||
|
} else {
|
||||||
|
substituteMsg = jasmineMessagePrefix + ' with no error or message';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isUnhandledRejection) {
|
||||||
|
substituteMsg +=
|
||||||
|
'\n' +
|
||||||
|
'(Tip: to get a useful stack trace, use ' +
|
||||||
|
'Promise.reject(n' +
|
||||||
|
'ew Error(...)) instead of Promise.reject(' +
|
||||||
|
(error ? '...' : '') +
|
||||||
|
').)';
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Error(substituteMsg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onError(error) {
|
||||||
|
error = this.#augmentError(error, false);
|
||||||
|
this.#dispatch.onUncaughtException(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
onUnhandledRejection(reason, promise) {
|
||||||
|
reason = this.#augmentError(reason, true);
|
||||||
|
this.#dispatch.onUnhandledRejection(reason, promise);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return GlobalErrors;
|
return GlobalErrors;
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ getJasmineRequireObj().MockDate = function(j$) {
|
|||||||
if (mockDate instanceof GlobalDate) {
|
if (mockDate instanceof GlobalDate) {
|
||||||
currentTime = mockDate.getTime();
|
currentTime = mockDate.getTime();
|
||||||
} else {
|
} else {
|
||||||
if (!j$.util.isUndefined(mockDate)) {
|
if (mockDate !== undefined) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
'The argument to jasmine.clock().mockDate(), if specified, ' +
|
'The argument to jasmine.clock().mockDate(), if specified, ' +
|
||||||
'should be a Date instance.'
|
'should be a Date instance.'
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ getJasmineRequireObj().makePrettyPrinter = function(j$) {
|
|||||||
|
|
||||||
if (customFormatResult) {
|
if (customFormatResult) {
|
||||||
this.emitScalar(customFormatResult);
|
this.emitScalar(customFormatResult);
|
||||||
} else if (j$.util.isUndefined(value)) {
|
} else if (value === undefined) {
|
||||||
this.emitScalar('undefined');
|
this.emitScalar('undefined');
|
||||||
} else if (value === null) {
|
} else if (value === null) {
|
||||||
this.emitScalar('null');
|
this.emitScalar('null');
|
||||||
@@ -35,7 +35,11 @@ getJasmineRequireObj().makePrettyPrinter = function(j$) {
|
|||||||
} else if (value instanceof RegExp) {
|
} else if (value instanceof RegExp) {
|
||||||
this.emitScalar(value.toString());
|
this.emitScalar(value.toString());
|
||||||
} else if (typeof value === 'function') {
|
} else if (typeof value === 'function') {
|
||||||
this.emitScalar('Function');
|
if (value.name) {
|
||||||
|
this.emitScalar(`Function '${value.name}'`);
|
||||||
|
} else {
|
||||||
|
this.emitScalar('Function');
|
||||||
|
}
|
||||||
} else if (j$.isDomNode(value)) {
|
} else if (j$.isDomNode(value)) {
|
||||||
if (value.tagName) {
|
if (value.tagName) {
|
||||||
this.emitDomElement(value);
|
this.emitDomElement(value);
|
||||||
|
|||||||
@@ -37,6 +37,17 @@ getJasmineRequireObj().QueueRunner = function(j$) {
|
|||||||
function QueueRunner(attrs) {
|
function QueueRunner(attrs) {
|
||||||
this.id_ = nextid++;
|
this.id_ = nextid++;
|
||||||
this.queueableFns = attrs.queueableFns || [];
|
this.queueableFns = attrs.queueableFns || [];
|
||||||
|
|
||||||
|
for (const f of this.queueableFns) {
|
||||||
|
if (!f) {
|
||||||
|
throw new Error('Received a falsy queueableFn');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!f.fn) {
|
||||||
|
throw new Error('Received a queueableFn with no fn');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
this.onComplete = attrs.onComplete || emptyFn;
|
this.onComplete = attrs.onComplete || emptyFn;
|
||||||
this.clearStack =
|
this.clearStack =
|
||||||
attrs.clearStack ||
|
attrs.clearStack ||
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
getJasmineRequireObj().ReportDispatcher = function(j$) {
|
getJasmineRequireObj().ReportDispatcher = function(j$) {
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
function ReportDispatcher(methods, queueRunnerFactory, onLateError) {
|
function ReportDispatcher(methods, runQueue, onLateError) {
|
||||||
const dispatchedMethods = methods || [];
|
const dispatchedMethods = methods || [];
|
||||||
|
|
||||||
for (const method of dispatchedMethods) {
|
for (const method of dispatchedMethods) {
|
||||||
@@ -39,7 +39,7 @@ getJasmineRequireObj().ReportDispatcher = function(j$) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return new Promise(function(resolve) {
|
return new Promise(function(resolve) {
|
||||||
queueRunnerFactory({
|
runQueue({
|
||||||
queueableFns: fns,
|
queueableFns: fns,
|
||||||
onComplete: resolve,
|
onComplete: resolve,
|
||||||
isReporter: true,
|
isReporter: true,
|
||||||
|
|||||||
@@ -1,51 +1,66 @@
|
|||||||
getJasmineRequireObj().Runner = function(j$) {
|
getJasmineRequireObj().Runner = function(j$) {
|
||||||
class Runner {
|
class Runner {
|
||||||
constructor(options) {
|
#topSuite;
|
||||||
this.topSuite_ = options.topSuite;
|
#getTotalSpecsDefined;
|
||||||
// TODO use names that read like getters
|
#getFocusedRunables;
|
||||||
this.totalSpecsDefined_ = options.totalSpecsDefined;
|
#runableResources;
|
||||||
this.focusedRunables_ = options.focusedRunables;
|
#runQueue;
|
||||||
this.runableResources_ = options.runableResources;
|
#TreeProcessor;
|
||||||
this.queueRunnerFactory_ = options.queueRunnerFactory;
|
#executionTree;
|
||||||
this.reporter_ = options.reporter;
|
#globalErrors;
|
||||||
this.getConfig_ = options.getConfig;
|
#reportDispatcher;
|
||||||
this.reportSpecDone_ = options.reportSpecDone;
|
#getConfig;
|
||||||
this.hasFailures = false;
|
#executedBefore;
|
||||||
this.executedBefore_ = false;
|
#currentRunableTracker;
|
||||||
|
|
||||||
this.currentlyExecutingSuites_ = [];
|
constructor(options) {
|
||||||
this.currentSpec = null;
|
this.#topSuite = options.topSuite;
|
||||||
|
this.#getTotalSpecsDefined = options.totalSpecsDefined;
|
||||||
|
this.#getFocusedRunables = options.focusedRunables;
|
||||||
|
this.#runableResources = options.runableResources;
|
||||||
|
this.#runQueue = options.runQueue;
|
||||||
|
this.#TreeProcessor = options.TreeProcessor;
|
||||||
|
this.#globalErrors = options.globalErrors;
|
||||||
|
this.#reportDispatcher = options.reportDispatcher;
|
||||||
|
this.#getConfig = options.getConfig;
|
||||||
|
this.#executedBefore = false;
|
||||||
|
this.#currentRunableTracker = new j$.CurrentRunableTracker();
|
||||||
|
}
|
||||||
|
|
||||||
|
currentSpec() {
|
||||||
|
return this.#currentRunableTracker.currentSpec();
|
||||||
|
}
|
||||||
|
|
||||||
|
setCurrentSpec(spec) {
|
||||||
|
this.#currentRunableTracker.setCurrentSpec(spec);
|
||||||
}
|
}
|
||||||
|
|
||||||
currentRunable() {
|
currentRunable() {
|
||||||
return this.currentSpec || this.currentSuite();
|
return this.#currentRunableTracker.currentRunable();
|
||||||
}
|
}
|
||||||
|
|
||||||
currentSuite() {
|
currentSuite() {
|
||||||
return this.currentlyExecutingSuites_[
|
return this.#currentRunableTracker.currentSuite();
|
||||||
this.currentlyExecutingSuites_.length - 1
|
|
||||||
];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
parallelReset() {
|
parallelReset() {
|
||||||
this.executedBefore_ = false;
|
this.#executedBefore = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
async execute(runablesToRun) {
|
async execute(runablesToRun) {
|
||||||
if (this.executedBefore_) {
|
if (this.#executedBefore) {
|
||||||
this.topSuite_.reset();
|
this.#topSuite.reset();
|
||||||
}
|
}
|
||||||
this.executedBefore_ = true;
|
this.#executedBefore = true;
|
||||||
|
|
||||||
this.hasFailures = false;
|
const focusedRunables = this.#getFocusedRunables();
|
||||||
const focusedRunables = this.focusedRunables_();
|
const config = this.#getConfig();
|
||||||
const config = this.getConfig_();
|
|
||||||
|
|
||||||
if (!runablesToRun) {
|
if (!runablesToRun) {
|
||||||
if (focusedRunables.length) {
|
if (focusedRunables.length) {
|
||||||
runablesToRun = focusedRunables;
|
runablesToRun = focusedRunables;
|
||||||
} else {
|
} else {
|
||||||
runablesToRun = [this.topSuite_.id];
|
runablesToRun = [this.#topSuite.id];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -54,52 +69,9 @@ getJasmineRequireObj().Runner = function(j$) {
|
|||||||
seed: j$.isNumber_(config.seed) ? config.seed + '' : config.seed
|
seed: j$.isNumber_(config.seed) ? config.seed + '' : config.seed
|
||||||
});
|
});
|
||||||
|
|
||||||
const processor = new j$.TreeProcessor({
|
const treeProcessor = new this.#TreeProcessor({
|
||||||
tree: this.topSuite_,
|
tree: this.#topSuite,
|
||||||
runnableIds: runablesToRun,
|
runnableIds: runablesToRun,
|
||||||
queueRunnerFactory: options => {
|
|
||||||
if (options.isLeaf) {
|
|
||||||
// A spec
|
|
||||||
options.SkipPolicy = j$.CompleteOnFirstErrorSkipPolicy;
|
|
||||||
} else {
|
|
||||||
// A suite
|
|
||||||
if (config.stopOnSpecFailure) {
|
|
||||||
options.SkipPolicy = j$.CompleteOnFirstErrorSkipPolicy;
|
|
||||||
} else {
|
|
||||||
options.SkipPolicy = j$.SkipAfterBeforeAllErrorPolicy;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.queueRunnerFactory_(options);
|
|
||||||
},
|
|
||||||
failSpecWithNoExpectations: config.failSpecWithNoExpectations,
|
|
||||||
nodeStart: (suite, next) => {
|
|
||||||
this.currentlyExecutingSuites_.push(suite);
|
|
||||||
this.runableResources_.initForRunable(suite.id, suite.parentSuite.id);
|
|
||||||
this.reporter_.suiteStarted(suite.result).then(next);
|
|
||||||
suite.startTimer();
|
|
||||||
},
|
|
||||||
nodeComplete: (suite, result, next) => {
|
|
||||||
if (suite !== this.currentSuite()) {
|
|
||||||
throw new Error('Tried to complete the wrong suite');
|
|
||||||
}
|
|
||||||
|
|
||||||
this.runableResources_.clearForRunable(suite.id);
|
|
||||||
this.currentlyExecutingSuites_.pop();
|
|
||||||
|
|
||||||
if (result.status === 'failed') {
|
|
||||||
this.hasFailures = true;
|
|
||||||
}
|
|
||||||
suite.endTimer();
|
|
||||||
|
|
||||||
if (suite.hadBeforeAllFailure) {
|
|
||||||
this.reportChildrenOfBeforeAllFailure_(suite).then(() => {
|
|
||||||
this.reportSuiteDone_(suite, result, next);
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
this.reportSuiteDone_(suite, result, next);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
orderChildren: function(node) {
|
orderChildren: function(node) {
|
||||||
return order.sort(node.children);
|
return order.sort(node.children);
|
||||||
},
|
},
|
||||||
@@ -107,20 +79,15 @@ getJasmineRequireObj().Runner = function(j$) {
|
|||||||
return !config.specFilter(spec);
|
return !config.specFilter(spec);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
this.#executionTree = treeProcessor.processTree();
|
||||||
|
|
||||||
if (!processor.processTree().valid) {
|
return this.#execute2(runablesToRun, order);
|
||||||
throw new Error(
|
|
||||||
'Invalid order: would cause a beforeAll or afterAll to be run multiple times'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.execute2_(runablesToRun, order, processor);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async execute2_(runablesToRun, order, processor) {
|
async #execute2(runablesToRun, order) {
|
||||||
const totalSpecsDefined = this.totalSpecsDefined_();
|
const totalSpecsDefined = this.#getTotalSpecsDefined();
|
||||||
|
|
||||||
this.runableResources_.initForRunable(this.topSuite_.id);
|
this.#runableResources.initForRunable(this.#topSuite.id);
|
||||||
const jasmineTimer = new j$.Timer();
|
const jasmineTimer = new j$.Timer();
|
||||||
jasmineTimer.start();
|
jasmineTimer.start();
|
||||||
|
|
||||||
@@ -132,31 +99,39 @@ getJasmineRequireObj().Runner = function(j$) {
|
|||||||
* @property {Boolean} parallel - Whether Jasmine is being run in parallel mode.
|
* @property {Boolean} parallel - Whether Jasmine is being run in parallel mode.
|
||||||
* @since 2.0.0
|
* @since 2.0.0
|
||||||
*/
|
*/
|
||||||
await this.reporter_.jasmineStarted({
|
await this.#reportDispatcher.jasmineStarted({
|
||||||
// In parallel mode, the jasmineStarted event is separately dispatched
|
// In parallel mode, the jasmineStarted event is separately dispatched
|
||||||
// by jasmine-npm. This event only reaches reporters in non-parallel.
|
// by jasmine-npm. This event only reaches reporters in non-parallel.
|
||||||
totalSpecsDefined,
|
totalSpecsDefined,
|
||||||
|
/**
|
||||||
|
* Information about the ordering (random or not) of this execution of the suite.
|
||||||
|
* @typedef Order
|
||||||
|
* @property {boolean} random - Whether the suite is running in random order
|
||||||
|
* @property {string} seed - The random seed
|
||||||
|
*/
|
||||||
order: order,
|
order: order,
|
||||||
parallel: false
|
parallel: false
|
||||||
});
|
});
|
||||||
|
|
||||||
this.currentlyExecutingSuites_.push(this.topSuite_);
|
this.#currentRunableTracker.pushSuite(this.#topSuite);
|
||||||
await processor.execute();
|
const treeRunner = new j$.TreeRunner({
|
||||||
|
executionTree: this.#executionTree,
|
||||||
|
globalErrors: this.#globalErrors,
|
||||||
|
runableResources: this.#runableResources,
|
||||||
|
reportDispatcher: this.#reportDispatcher,
|
||||||
|
runQueue: this.#runQueue,
|
||||||
|
getConfig: this.#getConfig,
|
||||||
|
currentRunableTracker: this.#currentRunableTracker
|
||||||
|
});
|
||||||
|
const { hasFailures } = await treeRunner.execute();
|
||||||
|
|
||||||
if (this.topSuite_.hadBeforeAllFailure) {
|
this.#runableResources.clearForRunable(this.#topSuite.id);
|
||||||
await this.reportChildrenOfBeforeAllFailure_(this.topSuite_);
|
this.#currentRunableTracker.popSuite();
|
||||||
}
|
|
||||||
|
|
||||||
this.runableResources_.clearForRunable(this.topSuite_.id);
|
|
||||||
this.currentlyExecutingSuites_.pop();
|
|
||||||
let overallStatus, incompleteReason, incompleteCode;
|
let overallStatus, incompleteReason, incompleteCode;
|
||||||
|
|
||||||
if (
|
if (hasFailures || this.#topSuite.result.failedExpectations.length > 0) {
|
||||||
this.hasFailures ||
|
|
||||||
this.topSuite_.result.failedExpectations.length > 0
|
|
||||||
) {
|
|
||||||
overallStatus = 'failed';
|
overallStatus = 'failed';
|
||||||
} else if (this.focusedRunables_().length > 0) {
|
} else if (this.#getFocusedRunables().length > 0) {
|
||||||
overallStatus = 'incomplete';
|
overallStatus = 'incomplete';
|
||||||
incompleteReason = 'fit() or fdescribe() was found';
|
incompleteReason = 'fit() or fdescribe() was found';
|
||||||
incompleteCode = 'focused';
|
incompleteCode = 'focused';
|
||||||
@@ -187,53 +162,13 @@ getJasmineRequireObj().Runner = function(j$) {
|
|||||||
incompleteReason: incompleteReason,
|
incompleteReason: incompleteReason,
|
||||||
incompleteCode: incompleteCode,
|
incompleteCode: incompleteCode,
|
||||||
order: order,
|
order: order,
|
||||||
failedExpectations: this.topSuite_.result.failedExpectations,
|
failedExpectations: this.#topSuite.result.failedExpectations,
|
||||||
deprecationWarnings: this.topSuite_.result.deprecationWarnings
|
deprecationWarnings: this.#topSuite.result.deprecationWarnings
|
||||||
};
|
};
|
||||||
this.topSuite_.reportedDone = true;
|
this.#topSuite.reportedDone = true;
|
||||||
await this.reporter_.jasmineDone(jasmineDoneInfo);
|
await this.#reportDispatcher.jasmineDone(jasmineDoneInfo);
|
||||||
return jasmineDoneInfo;
|
return jasmineDoneInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
reportSuiteDone_(suite, result, next) {
|
|
||||||
suite.reportedDone = true;
|
|
||||||
this.reporter_.suiteDone(result).then(next);
|
|
||||||
}
|
|
||||||
|
|
||||||
async reportChildrenOfBeforeAllFailure_(suite) {
|
|
||||||
for (const child of suite.children) {
|
|
||||||
if (child instanceof j$.Suite) {
|
|
||||||
await this.reporter_.suiteStarted(child.result);
|
|
||||||
await this.reportChildrenOfBeforeAllFailure_(child);
|
|
||||||
|
|
||||||
// Marking the suite passed is consistent with how suites that
|
|
||||||
// contain failed specs but no suite-level failures are reported.
|
|
||||||
child.result.status = 'passed';
|
|
||||||
|
|
||||||
await this.reporter_.suiteDone(child.result);
|
|
||||||
} else {
|
|
||||||
/* a spec */
|
|
||||||
await this.reporter_.specStarted(child.result);
|
|
||||||
|
|
||||||
child.addExpectationResult(
|
|
||||||
false,
|
|
||||||
{
|
|
||||||
passed: false,
|
|
||||||
message:
|
|
||||||
'Not run because a beforeAll function failed. The ' +
|
|
||||||
'beforeAll failure will be reported on the suite that ' +
|
|
||||||
'caused it.'
|
|
||||||
},
|
|
||||||
true
|
|
||||||
);
|
|
||||||
child.result.status = 'failed';
|
|
||||||
|
|
||||||
await new Promise(resolve => {
|
|
||||||
this.reportSpecDone_(child, child.result, resolve);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return Runner;
|
return Runner;
|
||||||
|
|||||||
566
src/core/Spec.js
566
src/core/Spec.js
@@ -1,320 +1,248 @@
|
|||||||
getJasmineRequireObj().Spec = function(j$) {
|
getJasmineRequireObj().Spec = function(j$) {
|
||||||
function Spec(attrs) {
|
class Spec {
|
||||||
this.expectationFactory = attrs.expectationFactory;
|
#autoCleanClosures;
|
||||||
this.asyncExpectationFactory = attrs.asyncExpectationFactory;
|
#throwOnExpectationFailure;
|
||||||
this.resultCallback = attrs.resultCallback || function() {};
|
#timer;
|
||||||
this.id = attrs.id;
|
#metadata;
|
||||||
this.filename = attrs.filename;
|
|
||||||
this.parentSuiteId = attrs.parentSuiteId;
|
|
||||||
this.description = attrs.description || '';
|
|
||||||
this.queueableFn = attrs.queueableFn;
|
|
||||||
this.beforeAndAfterFns =
|
|
||||||
attrs.beforeAndAfterFns ||
|
|
||||||
function() {
|
|
||||||
return { befores: [], afters: [] };
|
|
||||||
};
|
|
||||||
this.userContext =
|
|
||||||
attrs.userContext ||
|
|
||||||
function() {
|
|
||||||
return {};
|
|
||||||
};
|
|
||||||
this.onStart = attrs.onStart || function() {};
|
|
||||||
this.autoCleanClosures =
|
|
||||||
attrs.autoCleanClosures === undefined ? true : !!attrs.autoCleanClosures;
|
|
||||||
|
|
||||||
this.getPath = function() {
|
constructor(attrs) {
|
||||||
return attrs.getPath ? attrs.getPath(this) : [];
|
this.expectationFactory = attrs.expectationFactory;
|
||||||
};
|
this.asyncExpectationFactory = attrs.asyncExpectationFactory;
|
||||||
|
this.id = attrs.id;
|
||||||
this.onLateError = attrs.onLateError || function() {};
|
this.filename = attrs.filename;
|
||||||
this.catchingExceptions =
|
this.parentSuiteId = attrs.parentSuiteId;
|
||||||
attrs.catchingExceptions ||
|
this.description = attrs.description || '';
|
||||||
function() {
|
this.queueableFn = attrs.queueableFn;
|
||||||
return true;
|
this.beforeAndAfterFns =
|
||||||
|
attrs.beforeAndAfterFns ||
|
||||||
|
function() {
|
||||||
|
return { befores: [], afters: [] };
|
||||||
|
};
|
||||||
|
this.userContext =
|
||||||
|
attrs.userContext ||
|
||||||
|
function() {
|
||||||
|
return {};
|
||||||
|
};
|
||||||
|
this.getPath = function() {
|
||||||
|
return attrs.getPath ? attrs.getPath(this) : [];
|
||||||
};
|
};
|
||||||
this.throwOnExpectationFailure = !!attrs.throwOnExpectationFailure;
|
|
||||||
this.timer = attrs.timer || new j$.Timer();
|
|
||||||
|
|
||||||
if (!this.queueableFn.fn) {
|
this.#autoCleanClosures =
|
||||||
this.exclude();
|
attrs.autoCleanClosures === undefined
|
||||||
|
? true
|
||||||
|
: !!attrs.autoCleanClosures;
|
||||||
|
this.onLateError = attrs.onLateError || function() {};
|
||||||
|
this.#throwOnExpectationFailure = !!attrs.throwOnExpectationFailure;
|
||||||
|
this.#timer = attrs.timer || new j$.Timer();
|
||||||
|
|
||||||
|
if (!this.queueableFn.fn) {
|
||||||
|
this.exclude();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
this.reset();
|
addExpectationResult(passed, data, isError) {
|
||||||
}
|
const expectationResult = j$.buildExpectationResult(data);
|
||||||
|
|
||||||
Spec.prototype.addExpectationResult = function(passed, data, isError) {
|
if (passed) {
|
||||||
const expectationResult = j$.buildExpectationResult(data);
|
this.result.passedExpectations.push(expectationResult);
|
||||||
|
|
||||||
if (passed) {
|
|
||||||
this.result.passedExpectations.push(expectationResult);
|
|
||||||
} else {
|
|
||||||
if (this.reportedDone) {
|
|
||||||
this.onLateError(expectationResult);
|
|
||||||
} else {
|
} else {
|
||||||
this.result.failedExpectations.push(expectationResult);
|
if (this.reportedDone) {
|
||||||
|
this.onLateError(expectationResult);
|
||||||
// TODO: refactor so that we don't need to override cached status
|
|
||||||
if (this.result.status) {
|
|
||||||
this.result.status = 'failed';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.throwOnExpectationFailure && !isError) {
|
|
||||||
throw new j$.errors.ExpectationFailed();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Spec.prototype.setSpecProperty = function(key, value) {
|
|
||||||
this.result.properties = this.result.properties || {};
|
|
||||||
this.result.properties[key] = value;
|
|
||||||
};
|
|
||||||
|
|
||||||
Spec.prototype.execute = function(
|
|
||||||
queueRunnerFactory,
|
|
||||||
onComplete,
|
|
||||||
excluded,
|
|
||||||
failSpecWithNoExp
|
|
||||||
) {
|
|
||||||
const onStart = {
|
|
||||||
fn: done => {
|
|
||||||
this.timer.start();
|
|
||||||
this.onStart(this, done);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const complete = {
|
|
||||||
fn: done => {
|
|
||||||
if (this.autoCleanClosures) {
|
|
||||||
this.queueableFn.fn = null;
|
|
||||||
}
|
|
||||||
this.result.status = this.status(excluded, failSpecWithNoExp);
|
|
||||||
this.result.duration = this.timer.elapsed();
|
|
||||||
|
|
||||||
if (this.result.status !== 'failed') {
|
|
||||||
this.result.debugLogs = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.resultCallback(this.result, done);
|
|
||||||
},
|
|
||||||
type: 'specCleanup'
|
|
||||||
};
|
|
||||||
|
|
||||||
const fns = this.beforeAndAfterFns();
|
|
||||||
|
|
||||||
const runnerConfig = {
|
|
||||||
isLeaf: true,
|
|
||||||
queueableFns: [...fns.befores, this.queueableFn, ...fns.afters],
|
|
||||||
onException: e => this.handleException(e),
|
|
||||||
onMultipleDone: () => {
|
|
||||||
// Issue a deprecation. Include the context ourselves and pass
|
|
||||||
// ignoreRunnable: true, since getting here always means that we've already
|
|
||||||
// moved on and the current runnable isn't the one that caused the problem.
|
|
||||||
this.onLateError(
|
|
||||||
new Error(
|
|
||||||
'An asynchronous spec, beforeEach, or afterEach function called its ' +
|
|
||||||
"'done' callback more than once.\n(in spec: " +
|
|
||||||
this.getFullName() +
|
|
||||||
')'
|
|
||||||
)
|
|
||||||
);
|
|
||||||
},
|
|
||||||
onComplete: () => {
|
|
||||||
if (this.result.status === 'failed') {
|
|
||||||
onComplete(new j$.StopExecutionError('spec failed'));
|
|
||||||
} else {
|
} else {
|
||||||
onComplete();
|
this.result.failedExpectations.push(expectationResult);
|
||||||
}
|
|
||||||
},
|
|
||||||
userContext: this.userContext(),
|
|
||||||
runnableName: this.getFullName.bind(this)
|
|
||||||
};
|
|
||||||
|
|
||||||
if (this.markedPending || excluded === true) {
|
// TODO: refactor so that we don't need to override cached status
|
||||||
runnerConfig.queueableFns = [];
|
if (this.result.status) {
|
||||||
|
this.result.status = 'failed';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.#throwOnExpectationFailure && !isError) {
|
||||||
|
throw new j$.errors.ExpectationFailed();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
runnerConfig.queueableFns.unshift(onStart);
|
getSpecProperty(key) {
|
||||||
runnerConfig.queueableFns.push(complete);
|
this.result.properties = this.result.properties || {};
|
||||||
|
return this.result.properties[key];
|
||||||
|
}
|
||||||
|
|
||||||
queueRunnerFactory(runnerConfig);
|
setSpecProperty(key, value) {
|
||||||
};
|
this.result.properties = this.result.properties || {};
|
||||||
|
this.result.properties[key] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
executionStarted() {
|
||||||
|
this.#timer.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
executionFinished(excluded, failSpecWithNoExp) {
|
||||||
|
if (this.#autoCleanClosures) {
|
||||||
|
this.queueableFn.fn = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.result.status = this.#status(excluded, failSpecWithNoExp);
|
||||||
|
this.result.duration = this.#timer.elapsed();
|
||||||
|
|
||||||
|
if (this.result.status !== 'failed') {
|
||||||
|
this.result.debugLogs = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
reset() {
|
||||||
|
/**
|
||||||
|
* @typedef SpecResult
|
||||||
|
* @property {String} id - The unique id of this spec.
|
||||||
|
* @property {String} description - The description passed to the {@link it} that created this spec.
|
||||||
|
* @property {String} fullName - The full description including all ancestors of this spec.
|
||||||
|
* @property {String|null} parentSuiteId - The ID of the suite containing this spec, or null if this spec is not in a describe().
|
||||||
|
* @property {String} filename - The name of the file the spec was defined in.
|
||||||
|
* 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. You can fix that by setting
|
||||||
|
* {@link Configuration#extraItStackFrames}.
|
||||||
|
* @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.
|
||||||
|
* @property {Object} properties - User-supplied properties, if any, that were set using {@link Env#setSpecProperty}
|
||||||
|
* @property {DebugLogEntry[]|null} debugLogs - Messages, if any, that were logged using {@link jasmine.debugLog} during a failing spec.
|
||||||
|
* @since 2.0.0
|
||||||
|
*/
|
||||||
|
this.result = {
|
||||||
|
id: this.id,
|
||||||
|
description: this.description,
|
||||||
|
fullName: this.getFullName(),
|
||||||
|
parentSuiteId: this.parentSuiteId,
|
||||||
|
filename: this.filename,
|
||||||
|
failedExpectations: [],
|
||||||
|
passedExpectations: [],
|
||||||
|
deprecationWarnings: [],
|
||||||
|
pendingReason: this.excludeMessage || '',
|
||||||
|
duration: null,
|
||||||
|
properties: null,
|
||||||
|
debugLogs: null
|
||||||
|
};
|
||||||
|
this.markedPending = this.markedExcluding;
|
||||||
|
this.reportedDone = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
handleException(e) {
|
||||||
|
if (Spec.isPendingSpecException(e)) {
|
||||||
|
this.pend(extractCustomPendingMessage(e));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (e instanceof j$.errors.ExpectationFailed) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.addExpectationResult(
|
||||||
|
false,
|
||||||
|
{
|
||||||
|
matcherName: '',
|
||||||
|
passed: false,
|
||||||
|
expected: '',
|
||||||
|
actual: '',
|
||||||
|
error: e
|
||||||
|
},
|
||||||
|
true
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
pend(message) {
|
||||||
|
this.markedPending = true;
|
||||||
|
if (message) {
|
||||||
|
this.result.pendingReason = message;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Like pend(), but pending state will survive reset().
|
||||||
|
// Useful for fit, xit, where pending state remains.
|
||||||
|
exclude(message) {
|
||||||
|
this.markedExcluding = true;
|
||||||
|
if (this.message) {
|
||||||
|
this.excludeMessage = message;
|
||||||
|
}
|
||||||
|
this.pend(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: ensure that all access to result goes through .getResult()
|
||||||
|
// so that the status is correct.
|
||||||
|
getResult() {
|
||||||
|
this.result.status = this.#status();
|
||||||
|
return this.result;
|
||||||
|
}
|
||||||
|
|
||||||
|
#status(excluded, failSpecWithNoExpectations) {
|
||||||
|
if (excluded === true) {
|
||||||
|
return 'excluded';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.markedPending) {
|
||||||
|
return 'pending';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
this.result.failedExpectations.length > 0 ||
|
||||||
|
(failSpecWithNoExpectations &&
|
||||||
|
this.result.failedExpectations.length +
|
||||||
|
this.result.passedExpectations.length ===
|
||||||
|
0)
|
||||||
|
) {
|
||||||
|
return 'failed';
|
||||||
|
}
|
||||||
|
|
||||||
|
return 'passed';
|
||||||
|
}
|
||||||
|
|
||||||
|
getFullName() {
|
||||||
|
return this.getPath().join(' ');
|
||||||
|
}
|
||||||
|
|
||||||
|
addDeprecationWarning(deprecation) {
|
||||||
|
if (typeof deprecation === 'string') {
|
||||||
|
deprecation = { message: deprecation };
|
||||||
|
}
|
||||||
|
this.result.deprecationWarnings.push(
|
||||||
|
j$.buildExpectationResult(deprecation)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
debugLog(msg) {
|
||||||
|
if (!this.result.debugLogs) {
|
||||||
|
this.result.debugLogs = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef DebugLogEntry
|
||||||
|
* @property {String} message - The message that was passed to {@link jasmine.debugLog}.
|
||||||
|
* @property {number} timestamp - The time when the entry was added, in
|
||||||
|
* milliseconds from the spec's start time
|
||||||
|
*/
|
||||||
|
this.result.debugLogs.push({
|
||||||
|
message: msg,
|
||||||
|
timestamp: this.#timer.elapsed()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
Spec.prototype.reset = function() {
|
|
||||||
/**
|
/**
|
||||||
* @typedef SpecResult
|
* @interface Spec
|
||||||
* @property {String} id - The unique id of this spec.
|
* @see Configuration#specFilter
|
||||||
* @property {String} description - The description passed to the {@link it} that created this spec.
|
|
||||||
* @property {String} fullName - The full description including all ancestors of this spec.
|
|
||||||
* @property {String|null} parentSuiteId - The ID of the suite containing this spec, or null if this spec is not in a describe().
|
|
||||||
* @property {String} filename - The name of the file the spec was defined in.
|
|
||||||
* 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.
|
|
||||||
* @property {Object} properties - User-supplied properties, if any, that were set using {@link Env#setSpecProperty}
|
|
||||||
* @property {DebugLogEntry[]|null} debugLogs - Messages, if any, that were logged using {@link jasmine.debugLog} during a failing spec.
|
|
||||||
* @since 2.0.0
|
* @since 2.0.0
|
||||||
*/
|
*/
|
||||||
this.result = {
|
get metadata() {
|
||||||
id: this.id,
|
// NOTE: Although most of jasmine-core only exposes these metadata objects,
|
||||||
description: this.description,
|
// actual Spec instances are still passed to Configuration#specFilter. Until
|
||||||
fullName: this.getFullName(),
|
// that is fixed, it's important to make sure that all metadata properties
|
||||||
parentSuiteId: this.parentSuiteId,
|
// also exist in compatible form on the underlying Spec.
|
||||||
filename: this.filename,
|
if (!this.#metadata) {
|
||||||
failedExpectations: [],
|
this.#metadata = {
|
||||||
passedExpectations: [],
|
|
||||||
deprecationWarnings: [],
|
|
||||||
pendingReason: this.excludeMessage || '',
|
|
||||||
duration: null,
|
|
||||||
properties: null,
|
|
||||||
debugLogs: null
|
|
||||||
};
|
|
||||||
this.markedPending = this.markedExcluding;
|
|
||||||
this.reportedDone = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
Spec.prototype.handleException = function handleException(e) {
|
|
||||||
if (Spec.isPendingSpecException(e)) {
|
|
||||||
this.pend(extractCustomPendingMessage(e));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (e instanceof j$.errors.ExpectationFailed) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.addExpectationResult(
|
|
||||||
false,
|
|
||||||
{
|
|
||||||
matcherName: '',
|
|
||||||
passed: false,
|
|
||||||
expected: '',
|
|
||||||
actual: '',
|
|
||||||
error: e
|
|
||||||
},
|
|
||||||
true
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Marks state as pending
|
|
||||||
* @param {string} [message] An optional reason message
|
|
||||||
*/
|
|
||||||
Spec.prototype.pend = function(message) {
|
|
||||||
this.markedPending = true;
|
|
||||||
if (message) {
|
|
||||||
this.result.pendingReason = message;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Like {@link Spec#pend}, but pending state will survive {@link Spec#reset}
|
|
||||||
* Useful for fit, xit, where pending state remains.
|
|
||||||
* @param {string} [message] An optional reason message
|
|
||||||
*/
|
|
||||||
Spec.prototype.exclude = function(message) {
|
|
||||||
this.markedExcluding = true;
|
|
||||||
if (this.message) {
|
|
||||||
this.excludeMessage = message;
|
|
||||||
}
|
|
||||||
this.pend(message);
|
|
||||||
};
|
|
||||||
|
|
||||||
Spec.prototype.getResult = function() {
|
|
||||||
this.result.status = this.status();
|
|
||||||
return this.result;
|
|
||||||
};
|
|
||||||
|
|
||||||
Spec.prototype.status = function(excluded, failSpecWithNoExpectations) {
|
|
||||||
if (excluded === true) {
|
|
||||||
return 'excluded';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.markedPending) {
|
|
||||||
return 'pending';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
|
||||||
this.result.failedExpectations.length > 0 ||
|
|
||||||
(failSpecWithNoExpectations &&
|
|
||||||
this.result.failedExpectations.length +
|
|
||||||
this.result.passedExpectations.length ===
|
|
||||||
0)
|
|
||||||
) {
|
|
||||||
return 'failed';
|
|
||||||
}
|
|
||||||
|
|
||||||
return 'passed';
|
|
||||||
};
|
|
||||||
|
|
||||||
Spec.prototype.getFullName = function() {
|
|
||||||
return this.getPath().join(' ');
|
|
||||||
};
|
|
||||||
|
|
||||||
Spec.prototype.addDeprecationWarning = function(deprecation) {
|
|
||||||
if (typeof deprecation === 'string') {
|
|
||||||
deprecation = { message: deprecation };
|
|
||||||
}
|
|
||||||
this.result.deprecationWarnings.push(
|
|
||||||
j$.buildExpectationResult(deprecation)
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
Spec.prototype.debugLog = function(msg) {
|
|
||||||
if (!this.result.debugLogs) {
|
|
||||||
this.result.debugLogs = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @typedef DebugLogEntry
|
|
||||||
* @property {String} message - The message that was passed to {@link jasmine.debugLog}.
|
|
||||||
* @property {number} timestamp - The time when the entry was added, in
|
|
||||||
* milliseconds from the spec's start time
|
|
||||||
*/
|
|
||||||
this.result.debugLogs.push({
|
|
||||||
message: msg,
|
|
||||||
timestamp: this.timer.elapsed()
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const extractCustomPendingMessage = function(e) {
|
|
||||||
const fullMessage = e.toString(),
|
|
||||||
boilerplateStart = fullMessage.indexOf(Spec.pendingSpecExceptionMessage),
|
|
||||||
boilerplateEnd =
|
|
||||||
boilerplateStart + Spec.pendingSpecExceptionMessage.length;
|
|
||||||
|
|
||||||
return fullMessage.slice(boilerplateEnd);
|
|
||||||
};
|
|
||||||
|
|
||||||
Spec.pendingSpecExceptionMessage = '=> marked Pending';
|
|
||||||
|
|
||||||
Spec.isPendingSpecException = function(e) {
|
|
||||||
return !!(
|
|
||||||
e &&
|
|
||||||
e.toString &&
|
|
||||||
e.toString().indexOf(Spec.pendingSpecExceptionMessage) !== -1
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @interface Spec
|
|
||||||
* @see Configuration#specFilter
|
|
||||||
* @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_ = {
|
|
||||||
/**
|
/**
|
||||||
* The unique ID of this spec.
|
* The unique ID of this spec.
|
||||||
* @name Spec#id
|
* @name Spec#id
|
||||||
@@ -349,13 +277,45 @@ getJasmineRequireObj().Spec = function(j$) {
|
|||||||
* @returns {Array.<string>}
|
* @returns {Array.<string>}
|
||||||
* @since 5.7.0
|
* @since 5.7.0
|
||||||
*/
|
*/
|
||||||
getPath: this.getPath.bind(this)
|
getPath: this.getPath.bind(this),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The name of the file the spec was defined in.
|
||||||
|
* 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. You can fix that by setting
|
||||||
|
* {@link Configuration#extraItStackFrames}.
|
||||||
|
* @name Spec#filename
|
||||||
|
* @readonly
|
||||||
|
* @type {string}
|
||||||
|
* @since 5.13.0
|
||||||
|
*/
|
||||||
|
filename: this.filename
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.metadata_;
|
return this.#metadata;
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
|
||||||
|
const extractCustomPendingMessage = function(e) {
|
||||||
|
const fullMessage = e.toString(),
|
||||||
|
boilerplateStart = fullMessage.indexOf(Spec.pendingSpecExceptionMessage),
|
||||||
|
boilerplateEnd =
|
||||||
|
boilerplateStart + Spec.pendingSpecExceptionMessage.length;
|
||||||
|
|
||||||
|
return fullMessage.slice(boilerplateEnd);
|
||||||
|
};
|
||||||
|
|
||||||
|
Spec.pendingSpecExceptionMessage = '=> marked Pending';
|
||||||
|
|
||||||
|
Spec.isPendingSpecException = function(e) {
|
||||||
|
return !!(
|
||||||
|
e &&
|
||||||
|
e.toString &&
|
||||||
|
e.toString().indexOf(Spec.pendingSpecExceptionMessage) !== -1
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
return Spec;
|
return Spec;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -161,7 +161,7 @@ getJasmineRequireObj().Spy = function(j$) {
|
|||||||
"Spy '" +
|
"Spy '" +
|
||||||
strategyArgs.name +
|
strategyArgs.name +
|
||||||
"' received a call with arguments " +
|
"' received a call with arguments " +
|
||||||
j$.basicPrettyPrinter_(Array.prototype.slice.call(args)) +
|
matchersUtil.pp(Array.prototype.slice.call(args)) +
|
||||||
' but all configured strategies specify other arguments.'
|
' but all configured strategies specify other arguments.'
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -54,7 +54,9 @@ getJasmineRequireObj().SpyFactory = function(j$) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (methods.length === 0 && properties.length === 0) {
|
if (methods.length === 0 && properties.length === 0) {
|
||||||
throw 'createSpyObj requires a non-empty array or object of method names to create spies for';
|
throw new Error(
|
||||||
|
'createSpyObj requires a non-empty array or object of method names to create spies for'
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return obj;
|
return obj;
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ getJasmineRequireObj().SpyRegistry = function(j$) {
|
|||||||
this.spyOn = function(obj, methodName) {
|
this.spyOn = function(obj, methodName) {
|
||||||
const getErrorMsg = spyOnMsg;
|
const getErrorMsg = spyOnMsg;
|
||||||
|
|
||||||
if (j$.util.isUndefined(obj) || obj === null) {
|
if (obj === undefined || obj === null) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
getErrorMsg(
|
getErrorMsg(
|
||||||
'could not find an object to spy upon for ' + methodName + '()'
|
'could not find an object to spy upon for ' + methodName + '()'
|
||||||
@@ -33,11 +33,11 @@ getJasmineRequireObj().SpyRegistry = function(j$) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (j$.util.isUndefined(methodName) || methodName === null) {
|
if (methodName === undefined || methodName === null) {
|
||||||
throw new Error(getErrorMsg('No method name supplied'));
|
throw new Error(getErrorMsg('No method name supplied'));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (j$.util.isUndefined(obj[methodName])) {
|
if (obj[methodName] === undefined) {
|
||||||
throw new Error(getErrorMsg(methodName + '() method does not exist'));
|
throw new Error(getErrorMsg(methodName + '() method does not exist'));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -102,7 +102,7 @@ getJasmineRequireObj().SpyRegistry = function(j$) {
|
|||||||
|
|
||||||
accessType = accessType || 'get';
|
accessType = accessType || 'get';
|
||||||
|
|
||||||
if (j$.util.isUndefined(obj)) {
|
if (!obj) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
getErrorMsg(
|
getErrorMsg(
|
||||||
'spyOn could not find an object to spy upon for ' +
|
'spyOn could not find an object to spy upon for ' +
|
||||||
@@ -112,7 +112,7 @@ getJasmineRequireObj().SpyRegistry = function(j$) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (j$.util.isUndefined(propertyName)) {
|
if (propertyName === undefined) {
|
||||||
throw new Error(getErrorMsg('No property name supplied'));
|
throw new Error(getErrorMsg('No property name supplied'));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -177,7 +177,7 @@ getJasmineRequireObj().SpyRegistry = function(j$) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
this.spyOnAllFunctions = function(obj, includeNonEnumerable) {
|
this.spyOnAllFunctions = function(obj, includeNonEnumerable) {
|
||||||
if (j$.util.isUndefined(obj)) {
|
if (!obj) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
'spyOnAllFunctions could not find an object to spy upon'
|
'spyOnAllFunctions could not find an object to spy upon'
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,86 +1,275 @@
|
|||||||
getJasmineRequireObj().Suite = function(j$) {
|
getJasmineRequireObj().Suite = function(j$) {
|
||||||
function Suite(attrs) {
|
class Suite {
|
||||||
this.env = attrs.env;
|
#reportedParentSuiteId;
|
||||||
this.id = attrs.id;
|
#throwOnExpectationFailure;
|
||||||
this.parentSuite = attrs.parentSuite;
|
#autoCleanClosures;
|
||||||
this.description = attrs.description;
|
#timer;
|
||||||
this.reportedParentSuiteId = attrs.reportedParentSuiteId;
|
|
||||||
this.filename = attrs.filename;
|
|
||||||
this.expectationFactory = attrs.expectationFactory;
|
|
||||||
this.asyncExpectationFactory = attrs.asyncExpectationFactory;
|
|
||||||
this.throwOnExpectationFailure = !!attrs.throwOnExpectationFailure;
|
|
||||||
this.autoCleanClosures =
|
|
||||||
attrs.autoCleanClosures === undefined ? true : !!attrs.autoCleanClosures;
|
|
||||||
this.onLateError = attrs.onLateError || function() {};
|
|
||||||
|
|
||||||
this.beforeFns = [];
|
constructor(attrs) {
|
||||||
this.afterFns = [];
|
this.id = attrs.id;
|
||||||
this.beforeAllFns = [];
|
this.parentSuite = attrs.parentSuite;
|
||||||
this.afterAllFns = [];
|
this.description = attrs.description;
|
||||||
this.timer = attrs.timer || new j$.Timer();
|
this.filename = attrs.filename;
|
||||||
this.children = [];
|
this.expectationFactory = attrs.expectationFactory;
|
||||||
|
this.asyncExpectationFactory = attrs.asyncExpectationFactory;
|
||||||
|
this.onLateError = attrs.onLateError || function() {};
|
||||||
|
this.#reportedParentSuiteId = attrs.reportedParentSuiteId;
|
||||||
|
this.#throwOnExpectationFailure = !!attrs.throwOnExpectationFailure;
|
||||||
|
this.#autoCleanClosures =
|
||||||
|
attrs.autoCleanClosures === undefined
|
||||||
|
? true
|
||||||
|
: !!attrs.autoCleanClosures;
|
||||||
|
this.#timer = attrs.timer || new j$.Timer();
|
||||||
|
|
||||||
this.reset();
|
this.beforeFns = [];
|
||||||
}
|
this.afterFns = [];
|
||||||
|
this.beforeAllFns = [];
|
||||||
|
this.afterAllFns = [];
|
||||||
|
this.children = [];
|
||||||
|
|
||||||
Suite.prototype.setSuiteProperty = function(key, value) {
|
this.reset();
|
||||||
this.result.properties = this.result.properties || {};
|
}
|
||||||
this.result.properties[key] = value;
|
|
||||||
};
|
|
||||||
|
|
||||||
Suite.prototype.getFullName = function() {
|
setSuiteProperty(key, value) {
|
||||||
const fullName = [];
|
this.result.properties = this.result.properties || {};
|
||||||
for (
|
this.result.properties[key] = value;
|
||||||
let parentSuite = this;
|
}
|
||||||
parentSuite;
|
|
||||||
parentSuite = parentSuite.parentSuite
|
getFullName() {
|
||||||
) {
|
const fullName = [];
|
||||||
if (parentSuite.parentSuite) {
|
for (
|
||||||
fullName.unshift(parentSuite.description);
|
let parentSuite = this;
|
||||||
|
parentSuite;
|
||||||
|
parentSuite = parentSuite.parentSuite
|
||||||
|
) {
|
||||||
|
if (parentSuite.parentSuite) {
|
||||||
|
fullName.unshift(parentSuite.description);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return fullName.join(' ');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mark the suite with "pending" status
|
||||||
|
pend() {
|
||||||
|
this.markedPending = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Like pend(), but pending state will survive reset().
|
||||||
|
// Useful for fdescribe, xdescribe, where pending state should remain.
|
||||||
|
exclude() {
|
||||||
|
this.pend();
|
||||||
|
this.markedExcluding = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
beforeEach(fn) {
|
||||||
|
this.beforeFns.unshift({ ...fn, suite: this });
|
||||||
|
}
|
||||||
|
|
||||||
|
beforeAll(fn) {
|
||||||
|
this.beforeAllFns.push({ ...fn, type: 'beforeAll', suite: this });
|
||||||
|
}
|
||||||
|
|
||||||
|
afterEach(fn) {
|
||||||
|
this.afterFns.unshift({ ...fn, suite: this, type: 'afterEach' });
|
||||||
|
}
|
||||||
|
|
||||||
|
afterAll(fn) {
|
||||||
|
this.afterAllFns.unshift({ ...fn, type: 'afterAll' });
|
||||||
|
}
|
||||||
|
|
||||||
|
startTimer() {
|
||||||
|
this.#timer.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
endTimer() {
|
||||||
|
this.result.duration = this.#timer.elapsed();
|
||||||
|
}
|
||||||
|
|
||||||
|
cleanupBeforeAfter() {
|
||||||
|
if (this.#autoCleanClosures) {
|
||||||
|
removeFns(this.beforeAllFns);
|
||||||
|
removeFns(this.afterAllFns);
|
||||||
|
removeFns(this.beforeFns);
|
||||||
|
removeFns(this.afterFns);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return fullName.join(' ');
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
reset() {
|
||||||
* Mark the suite with "pending" status
|
/**
|
||||||
*/
|
* @typedef SuiteResult
|
||||||
Suite.prototype.pend = function() {
|
* @property {String} id - The unique id of this suite.
|
||||||
this.markedPending = true;
|
* @property {String} description - The description text passed to the {@link describe} that made this suite.
|
||||||
};
|
* @property {String} fullName - The full description including all ancestors of this suite.
|
||||||
|
* @property {String|null} parentSuiteId - The ID of the suite containing this suite, or null if this is not in another describe().
|
||||||
|
* @property {String} filename - The name of the file the suite was defined in.
|
||||||
|
* Note: The value may be incorrect if zone.js is installed or
|
||||||
|
* `describe`/`fdescribe`/`xdescribe` have been replaced with versions that
|
||||||
|
* don't maintain the same call stack height as the originals. You can fix
|
||||||
|
* that by setting {@link Configuration#extraDescribeStackFrames}.
|
||||||
|
* @property {ExpectationResult[]} failedExpectations - The list of expectations that failed in an {@link afterAll} for this suite.
|
||||||
|
* @property {ExpectationResult[]} deprecationWarnings - The list of deprecation warnings that occurred on this suite.
|
||||||
|
* @property {String} status - Once the suite has completed, this string represents the pass/fail status of this suite.
|
||||||
|
* @property {number} duration - The time in ms for Suite execution, including any before/afterAll, before/afterEach.
|
||||||
|
* @property {Object} properties - User-supplied properties, if any, that were set using {@link Env#setSuiteProperty}
|
||||||
|
* @since 2.0.0
|
||||||
|
*/
|
||||||
|
this.result = {
|
||||||
|
id: this.id,
|
||||||
|
description: this.description,
|
||||||
|
fullName: this.getFullName(),
|
||||||
|
parentSuiteId: this.#reportedParentSuiteId,
|
||||||
|
filename: this.filename,
|
||||||
|
failedExpectations: [],
|
||||||
|
deprecationWarnings: [],
|
||||||
|
duration: null,
|
||||||
|
properties: null
|
||||||
|
};
|
||||||
|
this.markedPending = this.markedExcluding;
|
||||||
|
this.children.forEach(function(child) {
|
||||||
|
child.reset();
|
||||||
|
});
|
||||||
|
this.reportedDone = false;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
removeChildren() {
|
||||||
* Like {@link Suite#pend}, but pending state will survive {@link Spec#reset}
|
this.children = [];
|
||||||
* Useful for fdescribe, xdescribe, where pending state should remain.
|
}
|
||||||
*/
|
|
||||||
Suite.prototype.exclude = function() {
|
|
||||||
this.pend();
|
|
||||||
this.markedExcluding = true;
|
|
||||||
};
|
|
||||||
|
|
||||||
Suite.prototype.beforeEach = function(fn) {
|
addChild(child) {
|
||||||
this.beforeFns.unshift({ ...fn, suite: this });
|
this.children.push(child);
|
||||||
};
|
}
|
||||||
|
|
||||||
Suite.prototype.beforeAll = function(fn) {
|
#status() {
|
||||||
this.beforeAllFns.push({ ...fn, type: 'beforeAll', suite: this });
|
if (this.markedPending) {
|
||||||
};
|
return 'pending';
|
||||||
|
}
|
||||||
|
|
||||||
Suite.prototype.afterEach = function(fn) {
|
if (this.result.failedExpectations.length > 0) {
|
||||||
this.afterFns.unshift({ ...fn, suite: this, type: 'afterEach' });
|
return 'failed';
|
||||||
};
|
} else {
|
||||||
|
return 'passed';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Suite.prototype.afterAll = function(fn) {
|
getResult() {
|
||||||
this.afterAllFns.unshift({ ...fn, type: 'afterAll' });
|
this.result.status = this.#status();
|
||||||
};
|
return this.result;
|
||||||
|
}
|
||||||
|
|
||||||
Suite.prototype.startTimer = function() {
|
canBeReentered() {
|
||||||
this.timer.start();
|
return this.beforeAllFns.length === 0 && this.afterAllFns.length === 0;
|
||||||
};
|
}
|
||||||
|
|
||||||
Suite.prototype.endTimer = function() {
|
sharedUserContext() {
|
||||||
this.result.duration = this.timer.elapsed();
|
if (!this.sharedContext) {
|
||||||
};
|
this.sharedContext = this.parentSuite
|
||||||
|
? this.parentSuite.clonedSharedUserContext()
|
||||||
|
: new j$.UserContext();
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.sharedContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
clonedSharedUserContext() {
|
||||||
|
return j$.UserContext.fromExisting(this.sharedUserContext());
|
||||||
|
}
|
||||||
|
|
||||||
|
handleException() {
|
||||||
|
if (arguments[0] instanceof j$.errors.ExpectationFailed) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = {
|
||||||
|
matcherName: '',
|
||||||
|
passed: false,
|
||||||
|
expected: '',
|
||||||
|
actual: '',
|
||||||
|
error: arguments[0]
|
||||||
|
};
|
||||||
|
const failedExpectation = j$.buildExpectationResult(data);
|
||||||
|
|
||||||
|
if (!this.parentSuite) {
|
||||||
|
failedExpectation.globalErrorType = 'afterAll';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.reportedDone) {
|
||||||
|
this.onLateError(failedExpectation);
|
||||||
|
} else {
|
||||||
|
this.result.failedExpectations.push(failedExpectation);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onMultipleDone() {
|
||||||
|
let msg;
|
||||||
|
|
||||||
|
// Issue an error. Include the context ourselves and pass
|
||||||
|
// ignoreRunnable: true, since getting here always means that we've already
|
||||||
|
// moved on and the current runnable isn't the one that caused the problem.
|
||||||
|
if (this.parentSuite) {
|
||||||
|
msg =
|
||||||
|
"An asynchronous beforeAll or afterAll function called its 'done' " +
|
||||||
|
'callback more than once.\n' +
|
||||||
|
'(in suite: ' +
|
||||||
|
this.getFullName() +
|
||||||
|
')';
|
||||||
|
} else {
|
||||||
|
msg =
|
||||||
|
'A top-level beforeAll or afterAll function called its ' +
|
||||||
|
"'done' callback more than once.";
|
||||||
|
}
|
||||||
|
|
||||||
|
this.onLateError(new Error(msg));
|
||||||
|
}
|
||||||
|
|
||||||
|
addExpectationResult() {
|
||||||
|
if (isFailure(arguments)) {
|
||||||
|
const data = arguments[1];
|
||||||
|
const expectationResult = j$.buildExpectationResult(data);
|
||||||
|
|
||||||
|
if (this.reportedDone) {
|
||||||
|
this.onLateError(expectationResult);
|
||||||
|
} else {
|
||||||
|
this.result.failedExpectations.push(expectationResult);
|
||||||
|
|
||||||
|
// TODO: refactor so that we don't need to override cached status
|
||||||
|
if (this.result.status) {
|
||||||
|
this.result.status = 'failed';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.#throwOnExpectationFailure) {
|
||||||
|
throw new j$.errors.ExpectationFailed();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
addDeprecationWarning(deprecation) {
|
||||||
|
if (typeof deprecation === 'string') {
|
||||||
|
deprecation = { message: deprecation };
|
||||||
|
}
|
||||||
|
this.result.deprecationWarnings.push(
|
||||||
|
j$.buildExpectationResult(deprecation)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
hasChildWithDescription(description) {
|
||||||
|
for (const child of this.children) {
|
||||||
|
if (child.description === description) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
get metadata() {
|
||||||
|
if (!this.metadata_) {
|
||||||
|
this.metadata_ = new SuiteMetadata(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.metadata_;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function removeFns(queueableFns) {
|
function removeFns(queueableFns) {
|
||||||
for (const qf of queueableFns) {
|
for (const qf of queueableFns) {
|
||||||
@@ -88,248 +277,77 @@ getJasmineRequireObj().Suite = function(j$) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Suite.prototype.cleanupBeforeAfter = function() {
|
|
||||||
if (this.autoCleanClosures) {
|
|
||||||
removeFns(this.beforeAllFns);
|
|
||||||
removeFns(this.afterAllFns);
|
|
||||||
removeFns(this.beforeFns);
|
|
||||||
removeFns(this.afterFns);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Suite.prototype.reset = function() {
|
|
||||||
/**
|
|
||||||
* @typedef SuiteResult
|
|
||||||
* @property {String} id - The unique id of this suite.
|
|
||||||
* @property {String} description - The description text passed to the {@link describe} that made this suite.
|
|
||||||
* @property {String} fullName - The full description including all ancestors of this suite.
|
|
||||||
* @property {String|null} parentSuiteId - The ID of the suite containing this suite, or null if this is not in another describe().
|
|
||||||
* @property {String} filename - The name of the file the suite was defined in.
|
|
||||||
* Note: The value may be incorrect if zone.js is installed or
|
|
||||||
* `describe`/`fdescribe`/`xdescribe` 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 in an {@link afterAll} for this suite.
|
|
||||||
* @property {ExpectationResult[]} deprecationWarnings - The list of deprecation warnings that occurred on this suite.
|
|
||||||
* @property {String} status - Once the suite has completed, this string represents the pass/fail status of this suite.
|
|
||||||
* @property {number} duration - The time in ms for Suite execution, including any before/afterAll, before/afterEach.
|
|
||||||
* @property {Object} properties - User-supplied properties, if any, that were set using {@link Env#setSuiteProperty}
|
|
||||||
* @since 2.0.0
|
|
||||||
*/
|
|
||||||
this.result = {
|
|
||||||
id: this.id,
|
|
||||||
description: this.description,
|
|
||||||
fullName: this.getFullName(),
|
|
||||||
parentSuiteId: this.reportedParentSuiteId,
|
|
||||||
filename: this.filename,
|
|
||||||
failedExpectations: [],
|
|
||||||
deprecationWarnings: [],
|
|
||||||
duration: null,
|
|
||||||
properties: null
|
|
||||||
};
|
|
||||||
this.markedPending = this.markedExcluding;
|
|
||||||
this.children.forEach(function(child) {
|
|
||||||
child.reset();
|
|
||||||
});
|
|
||||||
this.reportedDone = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
Suite.prototype.removeChildren = function() {
|
|
||||||
this.children = [];
|
|
||||||
};
|
|
||||||
|
|
||||||
Suite.prototype.addChild = function(child) {
|
|
||||||
this.children.push(child);
|
|
||||||
};
|
|
||||||
|
|
||||||
Suite.prototype.status = function() {
|
|
||||||
if (this.markedPending) {
|
|
||||||
return 'pending';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.result.failedExpectations.length > 0) {
|
|
||||||
return 'failed';
|
|
||||||
} else {
|
|
||||||
return 'passed';
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Suite.prototype.canBeReentered = function() {
|
|
||||||
return this.beforeAllFns.length === 0 && this.afterAllFns.length === 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
Suite.prototype.getResult = function() {
|
|
||||||
this.result.status = this.status();
|
|
||||||
return this.result;
|
|
||||||
};
|
|
||||||
|
|
||||||
Suite.prototype.sharedUserContext = function() {
|
|
||||||
if (!this.sharedContext) {
|
|
||||||
this.sharedContext = this.parentSuite
|
|
||||||
? this.parentSuite.clonedSharedUserContext()
|
|
||||||
: new j$.UserContext();
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.sharedContext;
|
|
||||||
};
|
|
||||||
|
|
||||||
Suite.prototype.clonedSharedUserContext = function() {
|
|
||||||
return j$.UserContext.fromExisting(this.sharedUserContext());
|
|
||||||
};
|
|
||||||
|
|
||||||
Suite.prototype.handleException = function() {
|
|
||||||
if (arguments[0] instanceof j$.errors.ExpectationFailed) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const data = {
|
|
||||||
matcherName: '',
|
|
||||||
passed: false,
|
|
||||||
expected: '',
|
|
||||||
actual: '',
|
|
||||||
error: arguments[0]
|
|
||||||
};
|
|
||||||
const failedExpectation = j$.buildExpectationResult(data);
|
|
||||||
|
|
||||||
if (!this.parentSuite) {
|
|
||||||
failedExpectation.globalErrorType = 'afterAll';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.reportedDone) {
|
|
||||||
this.onLateError(failedExpectation);
|
|
||||||
} else {
|
|
||||||
this.result.failedExpectations.push(failedExpectation);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Suite.prototype.onMultipleDone = function() {
|
|
||||||
let msg;
|
|
||||||
|
|
||||||
// Issue a deprecation. Include the context ourselves and pass
|
|
||||||
// ignoreRunnable: true, since getting here always means that we've already
|
|
||||||
// moved on and the current runnable isn't the one that caused the problem.
|
|
||||||
if (this.parentSuite) {
|
|
||||||
msg =
|
|
||||||
"An asynchronous beforeAll or afterAll function called its 'done' " +
|
|
||||||
'callback more than once.\n' +
|
|
||||||
'(in suite: ' +
|
|
||||||
this.getFullName() +
|
|
||||||
')';
|
|
||||||
} else {
|
|
||||||
msg =
|
|
||||||
'A top-level beforeAll or afterAll function called its ' +
|
|
||||||
"'done' callback more than once.";
|
|
||||||
}
|
|
||||||
|
|
||||||
this.onLateError(new Error(msg));
|
|
||||||
};
|
|
||||||
|
|
||||||
Suite.prototype.addExpectationResult = function() {
|
|
||||||
if (isFailure(arguments)) {
|
|
||||||
const data = arguments[1];
|
|
||||||
const expectationResult = j$.buildExpectationResult(data);
|
|
||||||
|
|
||||||
if (this.reportedDone) {
|
|
||||||
this.onLateError(expectationResult);
|
|
||||||
} else {
|
|
||||||
this.result.failedExpectations.push(expectationResult);
|
|
||||||
|
|
||||||
// TODO: refactor so that we don't need to override cached status
|
|
||||||
if (this.result.status) {
|
|
||||||
this.result.status = 'failed';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.throwOnExpectationFailure) {
|
|
||||||
throw new j$.errors.ExpectationFailed();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Suite.prototype.addDeprecationWarning = function(deprecation) {
|
|
||||||
if (typeof deprecation === 'string') {
|
|
||||||
deprecation = { message: deprecation };
|
|
||||||
}
|
|
||||||
this.result.deprecationWarnings.push(
|
|
||||||
j$.buildExpectationResult(deprecation)
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
Suite.prototype.hasChildWithDescription = function(description) {
|
|
||||||
for (const child of this.children) {
|
|
||||||
if (child.description === description) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
|
|
||||||
Object.defineProperty(Suite.prototype, 'metadata', {
|
|
||||||
get: function() {
|
|
||||||
if (!this.metadata_) {
|
|
||||||
this.metadata_ = new SuiteMetadata(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.metadata_;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @interface Suite
|
* @interface Suite
|
||||||
* @see Env#topSuite
|
* @see Env#topSuite
|
||||||
* @since 2.0.0
|
* @since 2.0.0
|
||||||
*/
|
*/
|
||||||
function SuiteMetadata(suite) {
|
class SuiteMetadata {
|
||||||
this.suite_ = suite;
|
#suite;
|
||||||
/**
|
|
||||||
* The unique ID of this suite.
|
|
||||||
* @name Suite#id
|
|
||||||
* @readonly
|
|
||||||
* @type {string}
|
|
||||||
* @since 2.0.0
|
|
||||||
*/
|
|
||||||
this.id = suite.id;
|
|
||||||
|
|
||||||
/**
|
constructor(suite) {
|
||||||
* The parent of this suite, or null if this is the top suite.
|
this.#suite = suite;
|
||||||
* @name Suite#parentSuite
|
/**
|
||||||
* @readonly
|
* The unique ID of this suite.
|
||||||
* @type {Suite}
|
* @name Suite#id
|
||||||
*/
|
* @readonly
|
||||||
this.parentSuite = suite.parentSuite ? suite.parentSuite.metadata : null;
|
* @type {string}
|
||||||
|
* @since 2.0.0
|
||||||
|
*/
|
||||||
|
this.id = suite.id;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The description passed to the {@link describe} that created this suite.
|
* The parent of this suite, or null if this is the top suite.
|
||||||
* @name Suite#description
|
* @name Suite#parentSuite
|
||||||
* @readonly
|
* @readonly
|
||||||
* @type {string}
|
* @type {Suite}
|
||||||
* @since 2.0.0
|
*/
|
||||||
*/
|
this.parentSuite = suite.parentSuite ? suite.parentSuite.metadata : null;
|
||||||
this.description = suite.description;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The full description including all ancestors of this suite.
|
* The description passed to the {@link describe} that created this suite.
|
||||||
* @name Suite#getFullName
|
* @name Suite#description
|
||||||
* @function
|
* @readonly
|
||||||
* @returns {string}
|
* @type {string}
|
||||||
* @since 2.0.0
|
* @since 2.0.0
|
||||||
*/
|
*/
|
||||||
SuiteMetadata.prototype.getFullName = function() {
|
this.description = suite.description;
|
||||||
return this.suite_.getFullName();
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The suite's children.
|
* The name of the file the suite was defined in.
|
||||||
* @name Suite#children
|
* Note: The value may be incorrect if zone.js is installed or
|
||||||
* @type {Array.<(Spec|Suite)>}
|
* `describe`/`fdescribe`/`xdescribe` have been replaced with versions
|
||||||
* @since 2.0.0
|
* that don't maintain the same call stack height as the originals. You
|
||||||
*/
|
* can fix that by setting {@link Configuration#extraItStackFrames}.
|
||||||
Object.defineProperty(SuiteMetadata.prototype, 'children', {
|
* @name Suite#filename
|
||||||
get: function() {
|
* @readonly
|
||||||
return this.suite_.children.map(child => child.metadata);
|
* @type {string}
|
||||||
|
* @since 5.13.0
|
||||||
|
*/
|
||||||
|
this.filename = suite.filename;
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
/**
|
||||||
|
* The full description including all ancestors of this suite.
|
||||||
|
* @name Suite#getFullName
|
||||||
|
* @function
|
||||||
|
* @returns {string}
|
||||||
|
* @since 2.0.0
|
||||||
|
*/
|
||||||
|
getFullName() {
|
||||||
|
return this.#suite.getFullName();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The suite's children.
|
||||||
|
* @name Suite#children
|
||||||
|
* @type {Array.<(Spec|Suite)>}
|
||||||
|
* @since 2.0.0
|
||||||
|
*/
|
||||||
|
get children() {
|
||||||
|
return this.#suite.children.map(child => child.metadata);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function isFailure(args) {
|
function isFailure(args) {
|
||||||
return !args[0];
|
return !args[0];
|
||||||
|
|||||||
@@ -10,8 +10,6 @@ getJasmineRequireObj().SuiteBuilder = function(j$) {
|
|||||||
return options.asyncExpectationFactory(actual, suite, 'Spec');
|
return options.asyncExpectationFactory(actual, suite, 'Spec');
|
||||||
};
|
};
|
||||||
this.onLateError_ = options.onLateError;
|
this.onLateError_ = options.onLateError;
|
||||||
this.specResultCallback_ = options.specResultCallback;
|
|
||||||
this.specStarted_ = options.specStarted;
|
|
||||||
|
|
||||||
this.nextSuiteId_ = 0;
|
this.nextSuiteId_ = 0;
|
||||||
this.nextSpecId_ = 0;
|
this.nextSpecId_ = 0;
|
||||||
@@ -247,11 +245,7 @@ getJasmineRequireObj().SuiteBuilder = function(j$) {
|
|||||||
expectationFactory: this.expectationFactory_,
|
expectationFactory: this.expectationFactory_,
|
||||||
asyncExpectationFactory: this.specAsyncExpectationFactory_,
|
asyncExpectationFactory: this.specAsyncExpectationFactory_,
|
||||||
onLateError: this.onLateError_,
|
onLateError: this.onLateError_,
|
||||||
resultCallback: (result, next) => {
|
|
||||||
this.specResultCallback_(spec, result, next);
|
|
||||||
},
|
|
||||||
getPath: spec => this.getSpecPath_(spec, suite),
|
getPath: spec => this.getSpecPath_(spec, suite),
|
||||||
onStart: (spec, next) => this.specStarted_(spec, suite, next),
|
|
||||||
description: description,
|
description: description,
|
||||||
userContext: function() {
|
userContext: function() {
|
||||||
return suite.clonedSharedUserContext();
|
return suite.clonedSharedUserContext();
|
||||||
|
|||||||
@@ -1,76 +1,59 @@
|
|||||||
getJasmineRequireObj().TreeProcessor = function() {
|
getJasmineRequireObj().TreeProcessor = function(j$) {
|
||||||
function TreeProcessor(attrs) {
|
const defaultMin = Infinity;
|
||||||
const tree = attrs.tree;
|
const defaultMax = 1 - Infinity;
|
||||||
const runnableIds = attrs.runnableIds;
|
|
||||||
const queueRunnerFactory = attrs.queueRunnerFactory;
|
|
||||||
const nodeStart = attrs.nodeStart || function() {};
|
|
||||||
const nodeComplete = attrs.nodeComplete || function() {};
|
|
||||||
const failSpecWithNoExpectations = !!attrs.failSpecWithNoExpectations;
|
|
||||||
const orderChildren =
|
|
||||||
attrs.orderChildren ||
|
|
||||||
function(node) {
|
|
||||||
return node.children;
|
|
||||||
};
|
|
||||||
const excludeNode =
|
|
||||||
attrs.excludeNode ||
|
|
||||||
function(node) {
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
let stats = { valid: true };
|
|
||||||
let processed = false;
|
|
||||||
const defaultMin = Infinity;
|
|
||||||
const defaultMax = 1 - Infinity;
|
|
||||||
|
|
||||||
this.processTree = function() {
|
// Transforms the suite tree into an execution tree, which represents the set
|
||||||
processNode(tree, true);
|
// of specs and (possibly interleaved) suites to be run in the order they are
|
||||||
processed = true;
|
// to be run in.
|
||||||
return stats;
|
class TreeProcessor {
|
||||||
};
|
#tree;
|
||||||
|
#runnableIds;
|
||||||
|
#orderChildren;
|
||||||
|
#excludeNode;
|
||||||
|
#stats;
|
||||||
|
|
||||||
this.execute = async function() {
|
constructor(attrs) {
|
||||||
if (!processed) {
|
this.#tree = attrs.tree;
|
||||||
this.processTree();
|
this.#runnableIds = attrs.runnableIds;
|
||||||
}
|
|
||||||
|
|
||||||
if (!stats.valid) {
|
this.#orderChildren =
|
||||||
throw 'invalid order';
|
attrs.orderChildren ||
|
||||||
}
|
function(node) {
|
||||||
|
return node.children;
|
||||||
|
};
|
||||||
|
this.#excludeNode =
|
||||||
|
attrs.excludeNode ||
|
||||||
|
function(node) {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
const childFns = wrapChildren(tree, 0);
|
processTree() {
|
||||||
|
this.#stats = {};
|
||||||
|
this.#processNode(this.#tree, true);
|
||||||
|
const result = new ExecutionTree(this.#tree, this.#stats);
|
||||||
|
this.#stats = null;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
await new Promise(function(resolve) {
|
#runnableIndex(id) {
|
||||||
queueRunnerFactory({
|
for (let i = 0; i < this.#runnableIds.length; i++) {
|
||||||
queueableFns: childFns,
|
if (this.#runnableIds[i] === id) {
|
||||||
userContext: tree.sharedUserContext(),
|
|
||||||
onException: function() {
|
|
||||||
tree.handleException.apply(tree, arguments);
|
|
||||||
},
|
|
||||||
onComplete: resolve,
|
|
||||||
onMultipleDone: tree.onMultipleDone
|
|
||||||
? tree.onMultipleDone.bind(tree)
|
|
||||||
: null
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
function runnableIndex(id) {
|
|
||||||
for (let i = 0; i < runnableIds.length; i++) {
|
|
||||||
if (runnableIds[i] === id) {
|
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function processNode(node, parentExcluded) {
|
#processNode(node, parentExcluded) {
|
||||||
const executableIndex = runnableIndex(node.id);
|
const executableIndex = this.#runnableIndex(node.id);
|
||||||
|
|
||||||
if (executableIndex !== undefined) {
|
if (executableIndex !== undefined) {
|
||||||
parentExcluded = false;
|
parentExcluded = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!node.children) {
|
if (!node.children) {
|
||||||
const excluded = parentExcluded || excludeNode(node);
|
const excluded = parentExcluded || this.#excludeNode(node);
|
||||||
stats[node.id] = {
|
this.#stats[node.id] = {
|
||||||
excluded: excluded,
|
excluded: excluded,
|
||||||
willExecute: !excluded && !node.markedPending,
|
willExecute: !excluded && !node.markedPending,
|
||||||
segments: [
|
segments: [
|
||||||
@@ -86,178 +69,147 @@ getJasmineRequireObj().TreeProcessor = function() {
|
|||||||
} else {
|
} else {
|
||||||
let hasExecutableChild = false;
|
let hasExecutableChild = false;
|
||||||
|
|
||||||
const orderedChildren = orderChildren(node);
|
const orderedChildren = this.#orderChildren(node);
|
||||||
|
|
||||||
for (let i = 0; i < orderedChildren.length; i++) {
|
for (let i = 0; i < orderedChildren.length; i++) {
|
||||||
const child = orderedChildren[i];
|
const child = orderedChildren[i];
|
||||||
|
this.#processNode(child, parentExcluded);
|
||||||
processNode(child, parentExcluded);
|
const childStats = this.#stats[child.id];
|
||||||
|
|
||||||
if (!stats.valid) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const childStats = stats[child.id];
|
|
||||||
|
|
||||||
hasExecutableChild = hasExecutableChild || childStats.willExecute;
|
hasExecutableChild = hasExecutableChild || childStats.willExecute;
|
||||||
}
|
}
|
||||||
|
|
||||||
stats[node.id] = {
|
this.#stats[node.id] = {
|
||||||
excluded: parentExcluded,
|
excluded: parentExcluded,
|
||||||
willExecute: hasExecutableChild
|
willExecute: hasExecutableChild
|
||||||
};
|
};
|
||||||
|
|
||||||
segmentChildren(node, orderedChildren, stats[node.id], executableIndex);
|
segmentChildren(node, orderedChildren, this.#stats, executableIndex);
|
||||||
|
|
||||||
if (!node.canBeReentered() && stats[node.id].segments.length > 1) {
|
if (this.#stats[node.id].segments.length > 1) {
|
||||||
stats = { valid: false };
|
if (node.canBeReentered()) {
|
||||||
}
|
j$.getEnv().deprecated(
|
||||||
}
|
'The specified spec/suite order splits up a suite, running unrelated specs in the middle of it. This will become an error in a future release.'
|
||||||
}
|
);
|
||||||
|
|
||||||
function startingMin(executableIndex) {
|
|
||||||
return executableIndex === undefined ? defaultMin : executableIndex;
|
|
||||||
}
|
|
||||||
|
|
||||||
function startingMax(executableIndex) {
|
|
||||||
return executableIndex === undefined ? defaultMax : executableIndex;
|
|
||||||
}
|
|
||||||
|
|
||||||
function segmentChildren(
|
|
||||||
node,
|
|
||||||
orderedChildren,
|
|
||||||
nodeStats,
|
|
||||||
executableIndex
|
|
||||||
) {
|
|
||||||
let currentSegment = {
|
|
||||||
index: 0,
|
|
||||||
owner: node,
|
|
||||||
nodes: [],
|
|
||||||
min: startingMin(executableIndex),
|
|
||||||
max: startingMax(executableIndex)
|
|
||||||
},
|
|
||||||
result = [currentSegment],
|
|
||||||
lastMax = defaultMax,
|
|
||||||
orderedChildSegments = orderChildSegments(orderedChildren);
|
|
||||||
|
|
||||||
function isSegmentBoundary(minIndex) {
|
|
||||||
return (
|
|
||||||
lastMax !== defaultMax &&
|
|
||||||
minIndex !== defaultMin &&
|
|
||||||
lastMax < minIndex - 1
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let i = 0; i < orderedChildSegments.length; i++) {
|
|
||||||
const childSegment = orderedChildSegments[i],
|
|
||||||
maxIndex = childSegment.max,
|
|
||||||
minIndex = childSegment.min;
|
|
||||||
|
|
||||||
if (isSegmentBoundary(minIndex)) {
|
|
||||||
currentSegment = {
|
|
||||||
index: result.length,
|
|
||||||
owner: node,
|
|
||||||
nodes: [],
|
|
||||||
min: defaultMin,
|
|
||||||
max: defaultMax
|
|
||||||
};
|
|
||||||
result.push(currentSegment);
|
|
||||||
}
|
|
||||||
|
|
||||||
currentSegment.nodes.push(childSegment);
|
|
||||||
currentSegment.min = Math.min(currentSegment.min, minIndex);
|
|
||||||
currentSegment.max = Math.max(currentSegment.max, maxIndex);
|
|
||||||
lastMax = maxIndex;
|
|
||||||
}
|
|
||||||
|
|
||||||
nodeStats.segments = result;
|
|
||||||
}
|
|
||||||
|
|
||||||
function orderChildSegments(children) {
|
|
||||||
const specifiedOrder = [],
|
|
||||||
unspecifiedOrder = [];
|
|
||||||
|
|
||||||
for (let i = 0; i < children.length; i++) {
|
|
||||||
const child = children[i],
|
|
||||||
segments = stats[child.id].segments;
|
|
||||||
|
|
||||||
for (let j = 0; j < segments.length; j++) {
|
|
||||||
const seg = segments[j];
|
|
||||||
|
|
||||||
if (seg.min === defaultMin) {
|
|
||||||
unspecifiedOrder.push(seg);
|
|
||||||
} else {
|
} else {
|
||||||
specifiedOrder.push(seg);
|
throw new Error(
|
||||||
}
|
'Invalid order: would cause a beforeAll or afterAll to be run multiple times'
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
specifiedOrder.sort(function(a, b) {
|
|
||||||
return a.min - b.min;
|
|
||||||
});
|
|
||||||
|
|
||||||
return specifiedOrder.concat(unspecifiedOrder);
|
|
||||||
}
|
|
||||||
|
|
||||||
function executeNode(node, segmentNumber) {
|
|
||||||
if (node.children) {
|
|
||||||
return {
|
|
||||||
fn: function(done) {
|
|
||||||
const onStart = {
|
|
||||||
fn: function(next) {
|
|
||||||
nodeStart(node, next);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
queueRunnerFactory({
|
|
||||||
onComplete: function() {
|
|
||||||
const args = Array.prototype.slice.call(arguments, [0]);
|
|
||||||
node.cleanupBeforeAfter();
|
|
||||||
nodeComplete(node, node.getResult(), function() {
|
|
||||||
done.apply(undefined, args);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
queueableFns: [onStart].concat(wrapChildren(node, segmentNumber)),
|
|
||||||
userContext: node.sharedUserContext(),
|
|
||||||
onException: function() {
|
|
||||||
node.handleException.apply(node, arguments);
|
|
||||||
},
|
|
||||||
onMultipleDone: node.onMultipleDone
|
|
||||||
? node.onMultipleDone.bind(node)
|
|
||||||
: null
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
return {
|
|
||||||
fn: function(done) {
|
|
||||||
node.execute(
|
|
||||||
queueRunnerFactory,
|
|
||||||
done,
|
|
||||||
stats[node.id].excluded,
|
|
||||||
failSpecWithNoExpectations
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ExecutionTree {
|
||||||
|
#stats;
|
||||||
|
|
||||||
|
constructor(topSuite, stats) {
|
||||||
|
Object.defineProperty(this, 'topSuite', {
|
||||||
|
writable: false,
|
||||||
|
value: topSuite
|
||||||
|
});
|
||||||
|
this.#stats = stats;
|
||||||
|
}
|
||||||
|
|
||||||
|
childrenOfTopSuite() {
|
||||||
|
return this.childrenOfSuiteSegment(this.topSuite, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
childrenOfSuiteSegment(suite, segmentNumber) {
|
||||||
|
const segmentChildren = this.#stats[suite.id].segments[segmentNumber]
|
||||||
|
.nodes;
|
||||||
|
return segmentChildren.map(function(child) {
|
||||||
|
if (child.owner.children) {
|
||||||
|
return { suite: child.owner, segmentNumber: child.index };
|
||||||
|
} else {
|
||||||
|
return { spec: child.owner };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
isExcluded(node) {
|
||||||
|
const nodeStats = this.#stats[node.id];
|
||||||
|
return node.children ? !nodeStats.willExecute : nodeStats.excluded;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function segmentChildren(node, orderedChildren, stats, executableIndex) {
|
||||||
|
let currentSegment = {
|
||||||
|
index: 0,
|
||||||
|
owner: node,
|
||||||
|
nodes: [],
|
||||||
|
min: startingMin(executableIndex),
|
||||||
|
max: startingMax(executableIndex)
|
||||||
|
},
|
||||||
|
result = [currentSegment],
|
||||||
|
lastMax = defaultMax,
|
||||||
|
orderedChildSegments = orderChildSegments(orderedChildren, stats);
|
||||||
|
|
||||||
|
function isSegmentBoundary(minIndex) {
|
||||||
|
return (
|
||||||
|
lastMax !== defaultMax &&
|
||||||
|
minIndex !== defaultMin &&
|
||||||
|
lastMax < minIndex - 1
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = 0; i < orderedChildSegments.length; i++) {
|
||||||
|
const childSegment = orderedChildSegments[i],
|
||||||
|
maxIndex = childSegment.max,
|
||||||
|
minIndex = childSegment.min;
|
||||||
|
|
||||||
|
if (isSegmentBoundary(minIndex)) {
|
||||||
|
currentSegment = {
|
||||||
|
index: result.length,
|
||||||
|
owner: node,
|
||||||
|
nodes: [],
|
||||||
|
min: defaultMin,
|
||||||
|
max: defaultMax
|
||||||
};
|
};
|
||||||
|
result.push(currentSegment);
|
||||||
|
}
|
||||||
|
|
||||||
|
currentSegment.nodes.push(childSegment);
|
||||||
|
currentSegment.min = Math.min(currentSegment.min, minIndex);
|
||||||
|
currentSegment.max = Math.max(currentSegment.max, maxIndex);
|
||||||
|
lastMax = maxIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
stats[node.id].segments = result;
|
||||||
|
}
|
||||||
|
|
||||||
|
function orderChildSegments(children, stats) {
|
||||||
|
const specifiedOrder = [],
|
||||||
|
unspecifiedOrder = [];
|
||||||
|
|
||||||
|
for (let i = 0; i < children.length; i++) {
|
||||||
|
const child = children[i],
|
||||||
|
segments = stats[child.id].segments;
|
||||||
|
|
||||||
|
for (let j = 0; j < segments.length; j++) {
|
||||||
|
const seg = segments[j];
|
||||||
|
|
||||||
|
if (seg.min === defaultMin) {
|
||||||
|
unspecifiedOrder.push(seg);
|
||||||
|
} else {
|
||||||
|
specifiedOrder.push(seg);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function wrapChildren(node, segmentNumber) {
|
specifiedOrder.sort(function(a, b) {
|
||||||
const result = [],
|
return a.min - b.min;
|
||||||
segmentChildren = stats[node.id].segments[segmentNumber].nodes;
|
});
|
||||||
|
|
||||||
for (let i = 0; i < segmentChildren.length; i++) {
|
return specifiedOrder.concat(unspecifiedOrder);
|
||||||
result.push(
|
}
|
||||||
executeNode(segmentChildren[i].owner, segmentChildren[i].index)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!stats[node.id].willExecute) {
|
function startingMin(executableIndex) {
|
||||||
return result;
|
return executableIndex === undefined ? defaultMin : executableIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
return node.beforeAllFns.concat(result).concat(node.afterAllFns);
|
function startingMax(executableIndex) {
|
||||||
}
|
return executableIndex === undefined ? defaultMax : executableIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
return TreeProcessor;
|
return TreeProcessor;
|
||||||
|
|||||||
306
src/core/TreeRunner.js
Normal file
306
src/core/TreeRunner.js
Normal file
@@ -0,0 +1,306 @@
|
|||||||
|
getJasmineRequireObj().TreeRunner = function(j$) {
|
||||||
|
class TreeRunner {
|
||||||
|
#executionTree;
|
||||||
|
#setTimeout;
|
||||||
|
#globalErrors;
|
||||||
|
#runableResources;
|
||||||
|
#reportDispatcher;
|
||||||
|
#runQueue;
|
||||||
|
#getConfig;
|
||||||
|
#currentRunableTracker;
|
||||||
|
#hasFailures;
|
||||||
|
|
||||||
|
constructor(attrs) {
|
||||||
|
this.#executionTree = attrs.executionTree;
|
||||||
|
this.#globalErrors = attrs.globalErrors;
|
||||||
|
this.#setTimeout = attrs.setTimeout || setTimeout.bind(globalThis);
|
||||||
|
this.#runableResources = attrs.runableResources;
|
||||||
|
this.#reportDispatcher = attrs.reportDispatcher;
|
||||||
|
this.#runQueue = attrs.runQueue;
|
||||||
|
this.#getConfig = attrs.getConfig;
|
||||||
|
this.#currentRunableTracker = attrs.currentRunableTracker;
|
||||||
|
}
|
||||||
|
|
||||||
|
async execute() {
|
||||||
|
this.#hasFailures = false;
|
||||||
|
await new Promise(resolve => {
|
||||||
|
this.#executeSuiteSegment(this.#executionTree.topSuite, 0, resolve);
|
||||||
|
});
|
||||||
|
return { hasFailures: this.#hasFailures };
|
||||||
|
}
|
||||||
|
|
||||||
|
#wrapNodes(nodes) {
|
||||||
|
return nodes.map(node => {
|
||||||
|
return {
|
||||||
|
fn: done => {
|
||||||
|
if (node.suite) {
|
||||||
|
this.#executeSuiteSegment(node.suite, node.segmentNumber, done);
|
||||||
|
} else {
|
||||||
|
this._executeSpec(node.spec, done);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only exposed for testing.
|
||||||
|
_executeSpec(spec, specOverallDone) {
|
||||||
|
const onStart = next => {
|
||||||
|
this.#currentRunableTracker.setCurrentSpec(spec);
|
||||||
|
this.#runableResources.initForRunable(
|
||||||
|
spec.id,
|
||||||
|
spec.parentSuiteId || this.#executionTree.topSuite.id
|
||||||
|
);
|
||||||
|
this.#reportDispatcher.specStarted(spec.result).then(next);
|
||||||
|
};
|
||||||
|
const resultCallback = (result, next) => {
|
||||||
|
this.#specComplete(spec).then(next);
|
||||||
|
};
|
||||||
|
const queueableFns = this.#specQueueableFns(
|
||||||
|
spec,
|
||||||
|
onStart,
|
||||||
|
resultCallback
|
||||||
|
);
|
||||||
|
|
||||||
|
this.#runQueue({
|
||||||
|
isLeaf: true,
|
||||||
|
queueableFns,
|
||||||
|
onException: e => spec.handleException(e),
|
||||||
|
onMultipleDone: () => {
|
||||||
|
// Issue an erorr. Include the context ourselves and pass
|
||||||
|
// ignoreRunnable: true, since getting here always means that we've already
|
||||||
|
// moved on and the current runnable isn't the one that caused the problem.
|
||||||
|
spec.onLateError(
|
||||||
|
new Error(
|
||||||
|
'An asynchronous spec, beforeEach, or afterEach function called its ' +
|
||||||
|
"'done' callback more than once.\n(in spec: " +
|
||||||
|
spec.getFullName() +
|
||||||
|
')'
|
||||||
|
)
|
||||||
|
);
|
||||||
|
},
|
||||||
|
onComplete: () => {
|
||||||
|
if (spec.result.status === 'failed') {
|
||||||
|
specOverallDone(new j$.StopExecutionError('spec failed'));
|
||||||
|
} else {
|
||||||
|
specOverallDone();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
userContext: spec.userContext(),
|
||||||
|
runnableName: spec.getFullName.bind(spec),
|
||||||
|
SkipPolicy: j$.CompleteOnFirstErrorSkipPolicy
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#specQueueableFns(spec, onStart, resultCallback) {
|
||||||
|
const config = this.#getConfig();
|
||||||
|
const excluded = this.#executionTree.isExcluded(spec);
|
||||||
|
const ba = spec.beforeAndAfterFns();
|
||||||
|
let fns = [...ba.befores, spec.queueableFn, ...ba.afters];
|
||||||
|
|
||||||
|
if (spec.markedPending || excluded === true) {
|
||||||
|
fns = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
const start = {
|
||||||
|
fn(done) {
|
||||||
|
spec.executionStarted();
|
||||||
|
onStart(done);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const complete = {
|
||||||
|
fn(done) {
|
||||||
|
spec.executionFinished(excluded, config.failSpecWithNoExpectations);
|
||||||
|
resultCallback(spec.result, done);
|
||||||
|
},
|
||||||
|
type: 'specCleanup'
|
||||||
|
};
|
||||||
|
|
||||||
|
fns.unshift(start);
|
||||||
|
|
||||||
|
if (config.detectLateRejectionHandling) {
|
||||||
|
fns.push(this.#lateUnhandledRejectionChecker());
|
||||||
|
}
|
||||||
|
|
||||||
|
fns.push(complete);
|
||||||
|
return fns;
|
||||||
|
}
|
||||||
|
|
||||||
|
#executeSuiteSegment(suite, segmentNumber, done) {
|
||||||
|
const isTopSuite = suite === this.#executionTree.topSuite;
|
||||||
|
const isExcluded = this.#executionTree.isExcluded(suite);
|
||||||
|
let befores = [];
|
||||||
|
let afters = [];
|
||||||
|
|
||||||
|
if (suite.beforeAllFns.length > 0 && !isExcluded) {
|
||||||
|
befores = [...suite.beforeAllFns];
|
||||||
|
if (this.#getConfig().detectLateRejectionHandling) {
|
||||||
|
befores.push(this.#lateUnhandledRejectionChecker());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (suite.afterAllFns.length > 0 && !isExcluded) {
|
||||||
|
afters = [...suite.afterAllFns];
|
||||||
|
if (this.#getConfig().detectLateRejectionHandling) {
|
||||||
|
afters.push(this.#lateUnhandledRejectionChecker());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const children = isTopSuite
|
||||||
|
? this.#executionTree.childrenOfTopSuite()
|
||||||
|
: this.#executionTree.childrenOfSuiteSegment(suite, segmentNumber);
|
||||||
|
const queueableFns = [
|
||||||
|
...befores,
|
||||||
|
...this.#wrapNodes(children),
|
||||||
|
...afters
|
||||||
|
];
|
||||||
|
|
||||||
|
if (!isTopSuite) {
|
||||||
|
queueableFns.unshift({
|
||||||
|
fn: next => {
|
||||||
|
this.#suiteSegmentStart(suite, next);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
this.#runQueue({
|
||||||
|
onComplete: maybeError => {
|
||||||
|
this.#suiteSegmentComplete(suite, suite.getResult(), () => {
|
||||||
|
done(maybeError);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
queueableFns,
|
||||||
|
userContext: suite.sharedUserContext(),
|
||||||
|
onException: function() {
|
||||||
|
suite.handleException.apply(suite, arguments);
|
||||||
|
},
|
||||||
|
onMultipleDone: suite.onMultipleDone
|
||||||
|
? suite.onMultipleDone.bind(suite)
|
||||||
|
: null,
|
||||||
|
SkipPolicy: this.#suiteSkipPolicy()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns a queueable fn that reports any still-unhandled rejections in
|
||||||
|
// cases where detectLateRejectionHandling is enabled. Should only be called
|
||||||
|
// when detectLateRejectionHandling is enabled, because the setTimeout
|
||||||
|
// imposes a significant performance penalty in suites with lots of fast
|
||||||
|
// specs.
|
||||||
|
#lateUnhandledRejectionChecker() {
|
||||||
|
const globalErrors = this.#globalErrors;
|
||||||
|
return {
|
||||||
|
fn: done => {
|
||||||
|
// setTimeout is necessary to trigger rejectionhandled events
|
||||||
|
this.#setTimeout(function() {
|
||||||
|
globalErrors.reportUnhandledRejections();
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#suiteSegmentStart(suite, next) {
|
||||||
|
this.#currentRunableTracker.pushSuite(suite);
|
||||||
|
this.#runableResources.initForRunable(suite.id, suite.parentSuite.id);
|
||||||
|
this.#reportDispatcher.suiteStarted(suite.result).then(next);
|
||||||
|
suite.startTimer();
|
||||||
|
}
|
||||||
|
|
||||||
|
#suiteSegmentComplete(suite, result, next) {
|
||||||
|
const isTopSuite = suite === this.#executionTree.topSuite;
|
||||||
|
|
||||||
|
if (!isTopSuite) {
|
||||||
|
if (suite !== this.#currentRunableTracker.currentSuite()) {
|
||||||
|
throw new Error('Tried to complete the wrong suite');
|
||||||
|
}
|
||||||
|
|
||||||
|
// suite.cleanupBeforeAfter() is conditional because calling it on the
|
||||||
|
// top suite breaks parallel mode. The top suite is reentered every time
|
||||||
|
// a runner runs another file, so its before and after fns need to be
|
||||||
|
// preserved.
|
||||||
|
suite.cleanupBeforeAfter();
|
||||||
|
this.#runableResources.clearForRunable(suite.id);
|
||||||
|
this.#currentRunableTracker.popSuite();
|
||||||
|
|
||||||
|
if (result.status === 'failed') {
|
||||||
|
this.#hasFailures = true;
|
||||||
|
}
|
||||||
|
suite.endTimer();
|
||||||
|
}
|
||||||
|
|
||||||
|
const finish = isTopSuite
|
||||||
|
? next
|
||||||
|
: () => this.#reportSuiteDone(suite, result, next);
|
||||||
|
|
||||||
|
if (suite.hadBeforeAllFailure) {
|
||||||
|
this.#reportChildrenOfBeforeAllFailure(suite).then(finish);
|
||||||
|
} else {
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#reportSuiteDone(suite, result, next) {
|
||||||
|
suite.reportedDone = true;
|
||||||
|
this.#reportDispatcher.suiteDone(result).then(next);
|
||||||
|
}
|
||||||
|
|
||||||
|
async #specComplete(spec) {
|
||||||
|
this.#runableResources.clearForRunable(spec.id);
|
||||||
|
this.#currentRunableTracker.setCurrentSpec(null);
|
||||||
|
|
||||||
|
if (spec.result.status === 'failed') {
|
||||||
|
this.#hasFailures = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.#reportSpecDone(spec);
|
||||||
|
}
|
||||||
|
|
||||||
|
async #reportSpecDone(spec) {
|
||||||
|
spec.reportedDone = true;
|
||||||
|
await this.#reportDispatcher.specDone(spec.result);
|
||||||
|
}
|
||||||
|
|
||||||
|
async #reportChildrenOfBeforeAllFailure(suite) {
|
||||||
|
for (const child of suite.children) {
|
||||||
|
if (child instanceof j$.Suite) {
|
||||||
|
await this.#reportDispatcher.suiteStarted(child.result);
|
||||||
|
await this.#reportChildrenOfBeforeAllFailure(child);
|
||||||
|
|
||||||
|
// Marking the suite passed is consistent with how suites that
|
||||||
|
// contain failed specs but no suite-level failures are reported.
|
||||||
|
child.result.status = 'passed';
|
||||||
|
|
||||||
|
await this.#reportDispatcher.suiteDone(child.result);
|
||||||
|
} else {
|
||||||
|
/* a spec */
|
||||||
|
await this.#reportDispatcher.specStarted(child.result);
|
||||||
|
|
||||||
|
child.addExpectationResult(
|
||||||
|
false,
|
||||||
|
{
|
||||||
|
passed: false,
|
||||||
|
message:
|
||||||
|
'Not run because a beforeAll function failed. The ' +
|
||||||
|
'beforeAll failure will be reported on the suite that ' +
|
||||||
|
'caused it.'
|
||||||
|
},
|
||||||
|
true
|
||||||
|
);
|
||||||
|
child.result.status = 'failed';
|
||||||
|
await this.#reportSpecDone(child);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#suiteSkipPolicy() {
|
||||||
|
if (this.#getConfig().stopOnSpecFailure) {
|
||||||
|
return j$.CompleteOnFirstErrorSkipPolicy;
|
||||||
|
} else {
|
||||||
|
return j$.SkipAfterBeforeAllErrorPolicy;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return TreeRunner;
|
||||||
|
};
|
||||||
27
src/core/asymmetric_equality/AllOf.js
Normal file
27
src/core/asymmetric_equality/AllOf.js
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
getJasmineRequireObj().AllOf = function(j$) {
|
||||||
|
function AllOf() {
|
||||||
|
const expectedValues = Array.from(arguments);
|
||||||
|
if (expectedValues.length === 0) {
|
||||||
|
throw new TypeError(
|
||||||
|
'jasmine.allOf() expects at least one argument to be passed.'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
this.expectedValues = expectedValues;
|
||||||
|
}
|
||||||
|
|
||||||
|
AllOf.prototype.asymmetricMatch = function(other, matchersUtil) {
|
||||||
|
for (const expectedValue of this.expectedValues) {
|
||||||
|
if (!matchersUtil.equals(other, expectedValue)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
AllOf.prototype.jasmineToString = function(pp) {
|
||||||
|
return '<jasmine.allOf(' + pp(this.expectedValues) + ')>';
|
||||||
|
};
|
||||||
|
|
||||||
|
return AllOf;
|
||||||
|
};
|
||||||
@@ -2,7 +2,7 @@ getJasmineRequireObj().Anything = function(j$) {
|
|||||||
function Anything() {}
|
function Anything() {}
|
||||||
|
|
||||||
Anything.prototype.asymmetricMatch = function(other) {
|
Anything.prototype.asymmetricMatch = function(other) {
|
||||||
return !j$.util.isUndefined(other) && other !== null;
|
return other !== undefined && other !== null;
|
||||||
};
|
};
|
||||||
|
|
||||||
Anything.prototype.jasmineToString = function() {
|
Anything.prototype.jasmineToString = function() {
|
||||||
|
|||||||
@@ -73,9 +73,7 @@ getJasmineRequireObj().base = function(j$, jasmineGlobal) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
j$.isObject_ = function(value) {
|
j$.isObject_ = function(value) {
|
||||||
return (
|
return value !== undefined && value !== null && j$.isA_('Object', value);
|
||||||
!j$.util.isUndefined(value) && value !== null && j$.isA_('Object', value)
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
j$.isString_ = function(value) {
|
j$.isString_ = function(value) {
|
||||||
@@ -223,6 +221,19 @@ getJasmineRequireObj().base = function(j$, jasmineGlobal) {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get an {@link AsymmetricEqualityTester} that will succeed if the actual
|
||||||
|
* value being compared matches every provided equality tester.
|
||||||
|
* @name asymmetricEqualityTesters.allOf
|
||||||
|
* @emittedName jasmine.allOf
|
||||||
|
* @since 5.13.0
|
||||||
|
* @function
|
||||||
|
* @param {...*} arguments - The asymmetric equality checkers to compare.
|
||||||
|
*/
|
||||||
|
j$.allOf = function() {
|
||||||
|
return new j$.AllOf(...arguments);
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get an {@link AsymmetricEqualityTester} that will succeed if the actual
|
* Get an {@link AsymmetricEqualityTester} that will succeed if the actual
|
||||||
* value being compared is an instance of the specified class/constructor.
|
* value being compared is an instance of the specified class/constructor.
|
||||||
|
|||||||
@@ -31,13 +31,14 @@ getJasmineRequireObj().DiffBuilder = function(j$) {
|
|||||||
|
|
||||||
const actualCustom = this.prettyPrinter_.customFormat_(actual);
|
const actualCustom = this.prettyPrinter_.customFormat_(actual);
|
||||||
const expectedCustom = this.prettyPrinter_.customFormat_(expected);
|
const expectedCustom = this.prettyPrinter_.customFormat_(expected);
|
||||||
const useCustom = !(
|
const useCustom =
|
||||||
j$.util.isUndefined(actualCustom) &&
|
actualCustom !== undefined || expectedCustom !== undefined;
|
||||||
j$.util.isUndefined(expectedCustom)
|
|
||||||
);
|
|
||||||
|
|
||||||
if (useCustom) {
|
if (useCustom) {
|
||||||
messages.push(wrapPrettyPrinted(actualCustom, expectedCustom, path));
|
const prettyActual = actualCustom || this.prettyPrinter_(actual);
|
||||||
|
const prettyExpected =
|
||||||
|
expectedCustom || this.prettyPrinter_(expected);
|
||||||
|
messages.push(wrapPrettyPrinted(prettyActual, prettyExpected, path));
|
||||||
return false; // don't recurse further
|
return false; // don't recurse further
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -177,13 +177,13 @@ getJasmineRequireObj().MatchersUtil = function(j$) {
|
|||||||
bStack,
|
bStack,
|
||||||
diffBuilder
|
diffBuilder
|
||||||
);
|
);
|
||||||
if (!j$.util.isUndefined(asymmetricResult)) {
|
if (asymmetricResult !== undefined) {
|
||||||
return asymmetricResult;
|
return asymmetricResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const tester of this.customTesters_) {
|
for (const tester of this.customTesters_) {
|
||||||
const customTesterResult = tester(a, b);
|
const customTesterResult = tester(a, b);
|
||||||
if (!j$.util.isUndefined(customTesterResult)) {
|
if (customTesterResult !== undefined) {
|
||||||
if (!customTesterResult) {
|
if (!customTesterResult) {
|
||||||
diffBuilder.recordMismatch();
|
diffBuilder.recordMismatch();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ var getJasmineRequireObj = (function(jasmineGlobal) {
|
|||||||
j$.util = jRequire.util(j$);
|
j$.util = jRequire.util(j$);
|
||||||
j$.errors = jRequire.errors();
|
j$.errors = jRequire.errors();
|
||||||
j$.formatErrorMsg = jRequire.formatErrorMsg();
|
j$.formatErrorMsg = jRequire.formatErrorMsg();
|
||||||
|
j$.AllOf = jRequire.AllOf(j$);
|
||||||
j$.Any = jRequire.Any(j$);
|
j$.Any = jRequire.Any(j$);
|
||||||
j$.Anything = jRequire.Anything(j$);
|
j$.Anything = jRequire.Anything(j$);
|
||||||
j$.CallTracker = jRequire.CallTracker(j$);
|
j$.CallTracker = jRequire.CallTracker(j$);
|
||||||
@@ -43,6 +44,7 @@ var getJasmineRequireObj = (function(jasmineGlobal) {
|
|||||||
j$.Clock = jRequire.Clock();
|
j$.Clock = jRequire.Clock();
|
||||||
j$.DelayedFunctionScheduler = jRequire.DelayedFunctionScheduler(j$);
|
j$.DelayedFunctionScheduler = jRequire.DelayedFunctionScheduler(j$);
|
||||||
j$.Deprecator = jRequire.Deprecator(j$);
|
j$.Deprecator = jRequire.Deprecator(j$);
|
||||||
|
j$.Configuration = jRequire.Configuration(j$);
|
||||||
j$.Env = jRequire.Env(j$);
|
j$.Env = jRequire.Env(j$);
|
||||||
j$.StackTrace = jRequire.StackTrace(j$);
|
j$.StackTrace = jRequire.StackTrace(j$);
|
||||||
j$.ExceptionFormatter = jRequire.ExceptionFormatter(j$);
|
j$.ExceptionFormatter = jRequire.ExceptionFormatter(j$);
|
||||||
@@ -70,6 +72,7 @@ var getJasmineRequireObj = (function(jasmineGlobal) {
|
|||||||
j$.reporterEvents = jRequire.reporterEvents(j$);
|
j$.reporterEvents = jRequire.reporterEvents(j$);
|
||||||
j$.ReportDispatcher = jRequire.ReportDispatcher(j$);
|
j$.ReportDispatcher = jRequire.ReportDispatcher(j$);
|
||||||
j$.ParallelReportDispatcher = jRequire.ParallelReportDispatcher(j$);
|
j$.ParallelReportDispatcher = jRequire.ParallelReportDispatcher(j$);
|
||||||
|
j$.CurrentRunableTracker = jRequire.CurrentRunableTracker();
|
||||||
j$.RunableResources = jRequire.RunableResources(j$);
|
j$.RunableResources = jRequire.RunableResources(j$);
|
||||||
j$.Runner = jRequire.Runner(j$);
|
j$.Runner = jRequire.Runner(j$);
|
||||||
j$.Spec = jRequire.Spec(j$);
|
j$.Spec = jRequire.Spec(j$);
|
||||||
@@ -83,14 +86,27 @@ var getJasmineRequireObj = (function(jasmineGlobal) {
|
|||||||
j$.Suite = jRequire.Suite(j$);
|
j$.Suite = jRequire.Suite(j$);
|
||||||
j$.SuiteBuilder = jRequire.SuiteBuilder(j$);
|
j$.SuiteBuilder = jRequire.SuiteBuilder(j$);
|
||||||
j$.Timer = jRequire.Timer();
|
j$.Timer = jRequire.Timer();
|
||||||
j$.TreeProcessor = jRequire.TreeProcessor();
|
j$.TreeProcessor = jRequire.TreeProcessor(j$);
|
||||||
|
j$.TreeRunner = jRequire.TreeRunner(j$);
|
||||||
j$.version = jRequire.version();
|
j$.version = jRequire.version();
|
||||||
j$.Order = jRequire.Order();
|
j$.Order = jRequire.Order();
|
||||||
j$.DiffBuilder = jRequire.DiffBuilder(j$);
|
j$.DiffBuilder = jRequire.DiffBuilder(j$);
|
||||||
j$.NullDiffBuilder = jRequire.NullDiffBuilder(j$);
|
j$.NullDiffBuilder = jRequire.NullDiffBuilder(j$);
|
||||||
j$.ObjectPath = jRequire.ObjectPath(j$);
|
j$.ObjectPath = jRequire.ObjectPath(j$);
|
||||||
j$.MismatchTree = jRequire.MismatchTree(j$);
|
j$.MismatchTree = jRequire.MismatchTree(j$);
|
||||||
j$.GlobalErrors = jRequire.GlobalErrors(j$);
|
|
||||||
|
// zone.js tries to monkey patch GlobalErrors in a way that is either a
|
||||||
|
// no-op or causes Jasmine to crash, depending on whether it's done before
|
||||||
|
// or after env creation. Prevent that.
|
||||||
|
const GlobalErrors = jRequire.GlobalErrors(j$);
|
||||||
|
Object.defineProperty(j$, 'GlobalErrors', {
|
||||||
|
enumerable: true,
|
||||||
|
configurable: false,
|
||||||
|
get() {
|
||||||
|
return GlobalErrors;
|
||||||
|
},
|
||||||
|
set() {}
|
||||||
|
});
|
||||||
|
|
||||||
j$.Truthy = jRequire.Truthy(j$);
|
j$.Truthy = jRequire.Truthy(j$);
|
||||||
j$.Falsy = jRequire.Falsy(j$);
|
j$.Falsy = jRequire.Falsy(j$);
|
||||||
|
|||||||
@@ -168,6 +168,18 @@ getJasmineRequireObj().interface = function(jasmine, env) {
|
|||||||
return env.afterAll.apply(env, arguments);
|
return env.afterAll.apply(env, arguments);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a user-defined property as part of the properties field of {@link SpecResult}
|
||||||
|
* @name getSpecProperty
|
||||||
|
* @since 5.10.0
|
||||||
|
* @function
|
||||||
|
* @param {String} key The name of the property
|
||||||
|
* @returns {*} The value of the property
|
||||||
|
*/
|
||||||
|
getSpecProperty: function(key) {
|
||||||
|
return env.getSpecProperty(key);
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets a user-defined property that will be provided to reporters as part of the properties field of {@link SpecResult}
|
* Sets a user-defined property that will be provided to reporters as part of the properties field of {@link SpecResult}
|
||||||
* @name setSpecProperty
|
* @name setSpecProperty
|
||||||
|
|||||||
@@ -1,10 +1,6 @@
|
|||||||
getJasmineRequireObj().util = function(j$) {
|
getJasmineRequireObj().util = function(j$) {
|
||||||
const util = {};
|
const util = {};
|
||||||
|
|
||||||
util.isUndefined = function(obj) {
|
|
||||||
return obj === void 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
util.clone = function(obj) {
|
util.clone = function(obj) {
|
||||||
if (Object.prototype.toString.apply(obj) === '[object Array]') {
|
if (Object.prototype.toString.apply(obj) === '[object Array]') {
|
||||||
return obj.slice();
|
return obj.slice();
|
||||||
@@ -52,16 +48,9 @@ getJasmineRequireObj().util = function(j$) {
|
|||||||
return Object.prototype.hasOwnProperty.call(obj, key);
|
return Object.prototype.hasOwnProperty.call(obj, key);
|
||||||
};
|
};
|
||||||
|
|
||||||
util.errorWithStack = function errorWithStack() {
|
|
||||||
// Don't throw and catch. That makes it harder for users to debug their
|
|
||||||
// code with exception breakpoints, and it's unnecessary since all
|
|
||||||
// supported environments populate new Error().stack
|
|
||||||
return new Error();
|
|
||||||
};
|
|
||||||
|
|
||||||
function callerFile() {
|
function callerFile() {
|
||||||
const trace = new j$.StackTrace(util.errorWithStack());
|
const trace = new j$.StackTrace(new Error());
|
||||||
return trace.frames[2].file;
|
return trace.frames[1].file;
|
||||||
}
|
}
|
||||||
|
|
||||||
util.jasmineFile = (function() {
|
util.jasmineFile = (function() {
|
||||||
|
|||||||
@@ -47,6 +47,13 @@ jasmineRequire.HtmlReporter = function(j$) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @class HtmlReporter
|
||||||
|
* @classdesc Displays results and allows re-running individual specs and suites.
|
||||||
|
* @implements {Reporter}
|
||||||
|
* @param options Options object. See lib/jasmine-core/boot1.js for details.
|
||||||
|
* @since 1.2.0
|
||||||
|
*/
|
||||||
function HtmlReporter(options) {
|
function HtmlReporter(options) {
|
||||||
function config() {
|
function config() {
|
||||||
return (options.env && options.env.configuration()) || {};
|
return (options.env && options.env.configuration()) || {};
|
||||||
@@ -64,6 +71,11 @@ jasmineRequire.HtmlReporter = function(j$) {
|
|||||||
const deprecationWarnings = [];
|
const deprecationWarnings = [];
|
||||||
const failures = [];
|
const failures = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes the reporter. Should be called before {@link Env#execute}.
|
||||||
|
* @function
|
||||||
|
* @name HtmlReporter#initialize
|
||||||
|
*/
|
||||||
this.initialize = function() {
|
this.initialize = function() {
|
||||||
clearPrior();
|
clearPrior();
|
||||||
htmlReporterMain = createDom(
|
htmlReporterMain = createDom(
|
||||||
@@ -523,6 +535,11 @@ jasmineRequire.HtmlReporter = function(j$) {
|
|||||||
'a',
|
'a',
|
||||||
{ href: specHref(resultNode.result) },
|
{ href: specHref(resultNode.result) },
|
||||||
specDescription
|
specDescription
|
||||||
|
),
|
||||||
|
createDom(
|
||||||
|
'span',
|
||||||
|
{ className: 'jasmine-spec-duration' },
|
||||||
|
'(' + resultNode.result.duration + 'ms)'
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
jasmineRequire.HtmlSpecFilter = function() {
|
jasmineRequire.HtmlSpecFilter = function() {
|
||||||
|
// Legacy HTML spec filter, preserved for backward compatibility with
|
||||||
|
// boot files that predate HtmlExactSpecFilterV2
|
||||||
function HtmlSpecFilter(options) {
|
function HtmlSpecFilter(options) {
|
||||||
const filterString =
|
const filterString =
|
||||||
options &&
|
options &&
|
||||||
@@ -6,6 +8,13 @@ jasmineRequire.HtmlSpecFilter = function() {
|
|||||||
options.filterString().replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&');
|
options.filterString().replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&');
|
||||||
const filterPattern = new RegExp(filterString);
|
const filterPattern = new RegExp(filterString);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines whether the spec with the specified name should be executed.
|
||||||
|
* @name HtmlSpecFilter#matches
|
||||||
|
* @function
|
||||||
|
* @param {string} specName The full name of the spec
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
this.matches = function(specName) {
|
this.matches = function(specName) {
|
||||||
return filterPattern.test(specName);
|
return filterPattern.test(specName);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,36 +1,55 @@
|
|||||||
jasmineRequire.QueryString = function() {
|
jasmineRequire.QueryString = function() {
|
||||||
function QueryString(options) {
|
/**
|
||||||
this.navigateWithNewParam = function(key, value) {
|
* Reads and manipulates the query string.
|
||||||
options.getWindowLocation().search = this.fullStringWithNewParam(
|
* @since 2.0.0
|
||||||
|
*/
|
||||||
|
class QueryString {
|
||||||
|
#getWindowLocation;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param options Object with a getWindowLocation property, which should be
|
||||||
|
* a function returning the current value of window.location.
|
||||||
|
*/
|
||||||
|
constructor(options) {
|
||||||
|
this.#getWindowLocation = options.getWindowLocation;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the specified query parameter and navigates to the resulting URL.
|
||||||
|
* @param {string} key
|
||||||
|
* @param {string} value
|
||||||
|
*/
|
||||||
|
navigateWithNewParam(key, value) {
|
||||||
|
this.#getWindowLocation().search = this.fullStringWithNewParam(
|
||||||
key,
|
key,
|
||||||
value
|
value
|
||||||
);
|
);
|
||||||
};
|
|
||||||
|
|
||||||
this.fullStringWithNewParam = function(key, value) {
|
|
||||||
const paramMap = queryStringToParamMap();
|
|
||||||
paramMap[key] = value;
|
|
||||||
return toQueryString(paramMap);
|
|
||||||
};
|
|
||||||
|
|
||||||
this.getParam = function(key) {
|
|
||||||
return queryStringToParamMap()[key];
|
|
||||||
};
|
|
||||||
|
|
||||||
return this;
|
|
||||||
|
|
||||||
function toQueryString(paramMap) {
|
|
||||||
const qStrPairs = [];
|
|
||||||
for (const prop in paramMap) {
|
|
||||||
qStrPairs.push(
|
|
||||||
encodeURIComponent(prop) + '=' + encodeURIComponent(paramMap[prop])
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return '?' + qStrPairs.join('&');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function queryStringToParamMap() {
|
/**
|
||||||
const paramStr = options.getWindowLocation().search.substring(1);
|
* Returns a new URL based on the current location, with the specified
|
||||||
|
* query parameter set.
|
||||||
|
* @param {string} key
|
||||||
|
* @param {string} value
|
||||||
|
* @return {string}
|
||||||
|
*/
|
||||||
|
fullStringWithNewParam(key, value) {
|
||||||
|
const paramMap = this.#queryStringToParamMap();
|
||||||
|
paramMap[key] = value;
|
||||||
|
return toQueryString(paramMap);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the value of the specified query parameter.
|
||||||
|
* @param {string} key
|
||||||
|
* @return {string}
|
||||||
|
*/
|
||||||
|
getParam(key) {
|
||||||
|
return this.#queryStringToParamMap()[key];
|
||||||
|
}
|
||||||
|
|
||||||
|
#queryStringToParamMap() {
|
||||||
|
const paramStr = this.#getWindowLocation().search.substring(1);
|
||||||
let params = [];
|
let params = [];
|
||||||
const paramMap = {};
|
const paramMap = {};
|
||||||
|
|
||||||
@@ -50,5 +69,15 @@ jasmineRequire.QueryString = function() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function toQueryString(paramMap) {
|
||||||
|
const qStrPairs = [];
|
||||||
|
for (const prop in paramMap) {
|
||||||
|
qStrPairs.push(
|
||||||
|
encodeURIComponent(prop) + '=' + encodeURIComponent(paramMap[prop])
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return '?' + qStrPairs.join('&');
|
||||||
|
}
|
||||||
|
|
||||||
return QueryString;
|
return QueryString;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -331,6 +331,10 @@ body {
|
|||||||
&.jasmine-excluded a:before {
|
&.jasmine-excluded a:before {
|
||||||
content: $passing-mark + $space;
|
content: $passing-mark + $space;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.jasmine-spec-duration {
|
||||||
|
margin-left: 1em;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user