Compare commits

..

11 Commits

Author SHA1 Message Date
Steve Gravrock
e4f8669835 Bump version to 4.6.1 2024-05-25 10:16:04 -07:00
Steve Gravrock
26be252e70 Removed unnecessary throw when building stack trace
Since 4.0, all supported JS runtimes populate the stack property of
Error objects when the Error is instantiated, not when it's thrown.
2024-05-25 10:00:26 -07:00
Steve Gravrock
2a1b7a04f7 Fix Chrome on CI 2024-05-25 10:00:26 -07:00
Steve Gravrock
4c8a4f2b00 Built distribution 2024-05-24 17:19:18 -07:00
angrycat9000
8bc95ae2e3 Skip parsing cause if it is not an Error object
* Merges #2013 from @angrycat9000
    * Fixes #2011
2024-05-24 17:18:35 -07:00
Steve Gravrock
80b9d6c2e0 CI: Use a globally-unique Sauce tunnel ID
CIRCLE_BUILD_NUM is only unique per-repo, and we have multiple repos
that can concurrently run Sauce builds.
2024-05-24 17:15:34 -07:00
Steve Gravrock
66ffb5f7a3 Accessibility: Always provide a non-color indication that a spec is pending 2024-05-24 17:15:26 -07:00
Steve Gravrock
225194c343 Accessibility: Improved contrast of version number and inactive tab links 2024-05-24 17:15:19 -07:00
Steve Gravrock
7a75cc7643 Updated copyright date 2024-05-24 17:15:06 -07:00
Steve Gravrock
ec7a9e2416 Fixed formatting of copyright notice in README 2024-05-24 17:14:48 -07:00
Steve Gravrock
7896ae84e6 Updated copyright notices
The Pivotal copyright notice needs to be retained. That's the right
thing to do and the MIT license requires it. However, using it by itself
becomes more obviously incorrect with each passing year since Pivotal
ceased to exist. Moreover, Pivotal hasn't actually been the sole
copyright owner since the first external contribution was merged.
Contributors were not asked to sign a copyright assignment, so they
retain copyright to their contributions.

"Copyright (c) 2008-2019 Pivotal Labs" complies with the terms of the
license and acknowledges Pivotal's outsized role in developing Jasmine.
"Copyright (c) 2008-$YEAR The Jasmine developers" acknowledges all
authors and will remain correct in the future.
2024-05-24 17:14:32 -07:00
79 changed files with 1021 additions and 3421 deletions

View File

@@ -4,17 +4,25 @@
version: 2.1
executors:
node22:
docker:
- image: cimg/node:22.0.0
working_directory: ~/workspace
node20:
docker:
- image: cimg/node:20.0.0
working_directory: ~/workspace
node18:
docker:
- image: cimg/node:18.0.0
- image: cimg/node:18.0.0 # Latest 18.x
working_directory: ~/workspace
node16:
docker:
- image: cimg/node:16.14.2 # Latest 16.x
working_directory: ~/workspace
node14:
docker:
- image: cimg/node:14.17.4 # Latest 14.x
working_directory: ~/workspace
node12_latest:
docker:
- image: cimg/node:12.22.10 # Latest 12.x
working_directory: ~/workspace
node12_17:
docker:
- image: cimg/node:12.17.0 # Oldest version supported by Jasmine
working_directory: ~/workspace
jobs:
@@ -51,20 +59,8 @@ jobs:
name: Run tests
command: npm test
test_parallel: &test_parallel
parameters:
executor:
type: executor
executor: << parameters.executor >>
steps:
- attach_workspace:
at: .
- run:
name: Run tests in parallel
command: npx grunt execSpecsInParallel
test_browsers: &test_browsers
executor: node18
executor: node14
steps:
- attach_workspace:
at: .
@@ -98,48 +94,49 @@ workflows:
push:
jobs:
- build:
executor: node22
name: build_node_22
- build:
executor: node20
name: build_node_20
- build:
executor: node18
name: build_node_18
- test_node:
executor: node22
name: test_node_22
requires:
- build_node_22
- test_node:
executor: node20
name: test_node_20
requires:
- build_node_20
- build:
executor: node16
name: build_node_16
- build:
executor: node14
name: build_node_14
- build:
executor: node12_latest
name: build_node_12_latest
- build:
executor: node12_17
name: build_node_12_17
- test_node:
executor: node18
name: test_node_18
requires:
- build_node_18
- test_parallel:
executor: node18
name: test_parallel_node_18
- build_node_18
- test_node:
executor: node16
name: test_node_16
requires:
- build_node_18
- test_parallel:
executor: node22
name: test_parallel_node_22
- build_node_16
- test_node:
executor: node14
name: test_node_14
requires:
- build_node_22
- test_parallel:
executor: node20
name: test_parallel_node_20
- build_node_14
- test_node:
executor: node12_latest
name: test_node_12_latest
requires:
- build_node_20
- build_node_12_latest
- test_node:
executor: node12_17
name: test_node_12_17
requires:
- build_node_12_17
- test_browsers:
requires:
- build_node_18
- build_node_14
filters:
branches:
ignore: /pull\/.*/ # Don't run on pull requests.

View File

@@ -1,47 +0,0 @@
{
"extends": [
"plugin:compat/recommended"
],
"env": {
"browser": true,
"node": true,
"es2017": true
},
"parserOptions": {
"ecmaVersion": 2018
},
"rules": {
"curly": "error",
"quotes": [
"error",
"single",
{
"avoidEscape": true
}
],
"no-unused-vars": [
"error",
{
"args": "none"
}
],
"no-implicit-globals": "error",
"block-spacing": "error",
"func-call-spacing": [
"error",
"never"
],
"key-spacing": "error",
"no-tabs": "error",
"no-trailing-spaces": "error",
"no-whitespace-before-property": "error",
"semi": [
"error",
"always"
],
"space-before-blocks": "error",
"no-eval": "error",
"no-var": "error",
"no-debugger": "error"
}
}

View File

@@ -1,6 +1,6 @@
name: Bug Report
description: I think I've found a bug in Jasmine
labels: ["bug report"]
labels: ["unconfirmed bug"]
body:
- type: markdown
attributes:

View File

@@ -1,3 +0,0 @@
{
"singleQuote": true
}

View File

@@ -30,9 +30,9 @@ module.exports = function(grunt) {
function() {
verifyNoGlobals(() => require('./lib/jasmine-core.js').noGlobals());
const done = this.async(),
Jasmine = require('jasmine'),
jasmineCore = require('./lib/jasmine-core.js'),
jasmine = new Jasmine({jasmineCore: jasmineCore});
Jasmine = require('jasmine'),
jasmineCore = require('./lib/jasmine-core.js'),
jasmine = new Jasmine({jasmineCore: jasmineCore});
jasmine.loadConfigFile('./spec/support/jasmine.json');
jasmine.exitOnCompletion = false;
@@ -40,50 +40,12 @@ module.exports = function(grunt) {
result => done(result.overallStatus === 'passed'),
err => {
console.error(err);
done(false);
exit(1);
}
);
}
);
grunt.registerTask("execSpecsInParallel",
"Run Jasmine core specs in parallel in Node.js",
function() {
// Need to require this here rather than at the top of the file
// so that we don't break verifyNoGlobals above by loading jasmine-core
// too early
const ParallelRunner = require('jasmine/parallel');
let numWorkers = require('os').cpus().length;
if (process.env['CIRCLECI']) {
// On Circle CI, the above gives the number of CPU cores on the host
// computer, which is unrelated to the resources actually available
// to the container. 2 workers gives peak performance with our current
// configuration, but 4 might increase the odds of discovering any
// parallel-specific bugs.
numWorkers = 4;
}
const done = this.async();
const runner = new ParallelRunner({
jasmineCore: require('./lib/jasmine-core.js'),
numWorkers
});
runner.loadConfigFile('./spec/support/jasmine.json')
.then(() => {
runner.exitOnCompletion = false;
return runner.execute();
}).then(
jasmineDoneInfo => done(jasmineDoneInfo.overallStatus === 'passed'),
err => {
console.error(err);
done(false);
}
);
}
);
grunt.registerTask("execSpecsInNode:performance",
"Run Jasmine performance specs in Node.js",
function() {

View File

@@ -1,10 +1,13 @@
<a name="README">[<img src="https://rawgithub.com/jasmine/jasmine/main/images/jasmine-horizontal.svg" width="400px" />](http://jasmine.github.io)</a>
[![Build Status](https://circleci.com/gh/jasmine/jasmine.svg?style=shield)](https://circleci.com/gh/jasmine/jasmine)
[![Open Source Helpers](https://www.codetriage.com/jasmine/jasmine/badges/users.svg)](https://www.codetriage.com/jasmine/jasmine)
# A JavaScript Testing Framework
Jasmine is a Behavior Driven Development testing framework for JavaScript. It does not rely on browsers, DOM, or any JavaScript framework. Thus it's suited for websites, [Node.js](http://nodejs.org) projects, or anywhere that JavaScript can run.
Upgrading from Jasmine 4.x? Check out the [upgrade guide](https://jasmine.github.io/tutorials/upgrading_to_Jasmine_5.0).
Upgrading from Jasmine 3.x? Check out the [upgrade guide](https://jasmine.github.io/tutorials/upgrading_to_Jasmine_4.0).
## Contributing
@@ -27,13 +30,13 @@ for information on writing specs, and [the FAQ](https://jasmine.github.io/pages/
Jasmine tests itself across popular browsers (Safari, Chrome, Firefox, and
Microsoft Edge) as well as Node.
| Environment | Supported versions |
|-------------------|---------------------|
| Node | 18, 20, 22 |
| Safari | 15-17 |
| Chrome | Evergreen |
| Firefox | Evergreen, 102, 115 |
| Edge | Evergreen |
| Environment | Supported versions |
|-------------------|--------------------|
| Node | 12.17+, 14, 16, 18 |
| Safari | 14-16 |
| Chrome | Evergreen |
| Firefox | Evergreen, 91, 102 |
| Edge | Evergreen |
For evergreen browsers, each version of Jasmine is tested against the version of the browser that is available to us
at the time of release. Other browsers, as well as older & newer versions of some supported browsers, are likely to work.
@@ -56,5 +59,5 @@ To find out what environments work with a particular Jasmine release, see the [r
* Sheel Choksi
Copyright (c) 2008-2019 Pivotal Labs<br>
Copyright (c) 2008-2023 The Jasmine developers<br>
Copyright (c) 2008-2024 The Jasmine developers<br>
This software is licensed under the [MIT License](https://github.com/jasmine/jasmine/blob/main/LICENSE).

View File

@@ -11,7 +11,7 @@ module.exports = {
},
files: [
{ src: [ root("LICENSE") ] },
{ src: [ root("MIT.LICENSE") ] },
{
src: [ "jasmine_favicon.png"],
dest: standaloneLibDir,

View File

@@ -45,6 +45,10 @@ module.exports = {
src: ['src/boot/boot1.js'],
dest: 'lib/jasmine-core/boot1.js'
},
nodeBoot: {
src: ['src/boot/node_boot.js'],
dest: 'lib/jasmine-core/node_boot.js'
},
options: {
banner: license(),
process: {

View File

@@ -6,51 +6,36 @@
const jasmineRequire = require('./jasmine-core/jasmine.js');
module.exports = jasmineRequire;
const boot = (function() {
let jasmine, jasmineInterface;
return function bootWithoutGlobals(reinitialize) {
if (!jasmineInterface || reinitialize === true) {
jasmine = jasmineRequire.core(jasmineRequire);
const env = jasmine.getEnv({ suppressLoadErrors: true });
jasmineInterface = jasmineRequire.interface(jasmine, env);
}
return {jasmine, jasmineInterface};
};
}());
/**
* Boots a copy of Jasmine and returns an object as described in {@link jasmine}.
* If boot is called multiple times, the same object is returned every time
* unless true is passed.
* @param {boolean} [reinitialize=false] Whether to create a new copy of Jasmine if one already exists
* @type {function}
* @return {jasmine}
*/
module.exports.boot = function(reinitialize) {
const {jasmine, jasmineInterface} = boot(reinitialize);
for (const k in jasmineInterface) {
global[k] = jasmineInterface[k];
}
return jasmine;
};
module.exports.boot = require('./jasmine-core/node_boot.js');
/**
* Boots a copy of Jasmine and returns an object containing the properties
* that would normally be added to the global object. If noGlobals is called
* multiple times, the same object is returned every time unless true is passed.
* multiple times, the same object is returned every time.
*
* Do not call boot() if you also call noGlobals().
*
* @param {boolean} [reinitialize=false] Whether to create a new copy of Jasmine if one already exists
* @example
* const {describe, beforeEach, it, expect, jasmine} = require('jasmine-core').noGlobals();
*/
module.exports.noGlobals = function(reinitialize) {
const {jasmineInterface} = boot(reinitialize);
return jasmineInterface;
};
module.exports.noGlobals = (function() {
let jasmineInterface;
return function bootWithoutGlobals() {
if (!jasmineInterface) {
const jasmine = jasmineRequire.core(jasmineRequire);
const env = jasmine.getEnv({ suppressLoadErrors: true });
jasmineInterface = jasmineRequire.interface(jasmine, env);
}
return jasmineInterface;
};
}());
const path = require('path'),
fs = require('fs');
@@ -58,9 +43,10 @@ const path = require('path'),
const rootPath = path.join(__dirname, 'jasmine-core'),
bootFiles = ['boot0.js', 'boot1.js'],
legacyBootFiles = ['boot.js'],
nodeBootFiles = ['node_boot.js'],
cssFiles = [],
jsFiles = [],
jsFilesToSkip = ['jasmine.js'].concat(bootFiles, legacyBootFiles);
jsFilesToSkip = ['jasmine.js'].concat(bootFiles, legacyBootFiles, nodeBootFiles);
fs.readdirSync(rootPath).forEach(function(file) {
if(fs.statSync(path.join(rootPath, file)).isFile()) {
@@ -70,18 +56,18 @@ fs.readdirSync(rootPath).forEach(function(file) {
break;
case '.js':
if (jsFilesToSkip.indexOf(file) < 0) {
jsFiles.push(file);
}
jsFiles.push(file);
}
break;
}
}
});
module.exports.files = {
self: __filename,
path: rootPath,
bootDir: rootPath,
bootFiles: bootFiles,
nodeBootFiles: nodeBootFiles,
cssFiles: cssFiles,
jsFiles: ['jasmine.js'].concat(jsFiles),
imagesDir: path.join(__dirname, '../images')

View File

@@ -463,11 +463,7 @@ jasmineRequire.HtmlReporter = function(j$) {
'tr',
{},
createDom('td', {}, entry.timestamp.toString()),
createDom(
'td',
{ className: 'jasmine-debug-log-msg' },
entry.message
)
createDom('td', {}, entry.message)
)
);
});

View File

@@ -295,7 +295,4 @@ body {
}
.jasmine_html-reporter .jasmine-debug-log table, .jasmine_html-reporter .jasmine-debug-log th, .jasmine_html-reporter .jasmine-debug-log td {
border: 1px solid #ddd;
}
.jasmine_html-reporter .jasmine-debug-log .jasmine-debug-log-msg {
white-space: pre;
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,39 @@
/*
Copyright (c) 2008-2019 Pivotal Labs
Copyright (c) 2008-2024 The Jasmine developers
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
module.exports = function(jasmineRequire) {
const jasmine = jasmineRequire.core(jasmineRequire);
const env = jasmine.getEnv({ suppressLoadErrors: true });
const jasmineInterface = jasmineRequire.interface(jasmine, env);
extend(global, jasmineInterface);
function extend(destination, source) {
for (const property in source) destination[property] = source[property];
return destination;
}
return jasmine;
};

View File

@@ -1,7 +1,7 @@
{
"name": "jasmine-core",
"license": "MIT",
"version": "5.3.0",
"version": "4.6.1",
"repository": {
"type": "git",
"url": "https://github.com/jasmine/jasmine.git"
@@ -27,35 +27,84 @@
"homepage": "https://jasmine.github.io",
"main": "./lib/jasmine-core.js",
"files": [
"LICENSE",
"MIT.LICENSE",
"README.md",
"images/*.{png,svg}",
"lib/**/*.{js,css}",
"package.json"
],
"devDependencies": {
"eslint": "^8.36.0",
"eslint-plugin-compat": "^4.0.0",
"glob": "^10.2.3",
"grunt": "^1.0.4",
"eslint": "^7.32.0",
"eslint-plugin-compat": ">=4.0.0 <4.1.0",
"glob": "^7.2.0",
"grunt": ">=1.0.4 <1.6.0",
"grunt-cli": "^1.3.2",
"grunt-contrib-compress": "^2.0.0",
"grunt-contrib-concat": "^2.0.0",
"grunt-css-url-embed": "^1.11.1",
"grunt-sass": "^3.0.2",
"jasmine": "^5.0.0",
"jasmine-browser-runner": "github:jasmine/jasmine-browser-runner",
"jsdom": "^22.0.0",
"jasmine": "^4.1.0",
"jasmine-browser-runner": "^1.0.0",
"jsdom": "^19.0.0",
"load-grunt-tasks": "^5.1.0",
"prettier": "1.17.1",
"sass": "^1.58.3",
"sass": "1.58.3",
"shelljs": "^0.8.3",
"temp": "^0.9.0"
},
"prettier": {
"singleQuote": true
},
"eslintConfig": {
"extends": [
"plugin:compat/recommended"
],
"env": {
"browser": true,
"node": true,
"es2017": true
},
"parserOptions": {
"ecmaVersion": 2018
},
"rules": {
"quotes": [
"error",
"single",
{
"avoidEscape": true
}
],
"no-unused-vars": [
"error",
{
"args": "none"
}
],
"no-implicit-globals": "error",
"block-spacing": "error",
"func-call-spacing": [
"error",
"never"
],
"key-spacing": "error",
"no-tabs": "error",
"no-trailing-spaces": "error",
"no-whitespace-before-property": "error",
"semi": [
"error",
"always"
],
"space-before-blocks": "error",
"no-eval": "error",
"no-var": "error"
}
},
"browserslist": [
"Safari >= 15",
"Firefox >= 102",
"Safari >= 14",
"last 2 Chrome versions",
"last 2 Firefox versions",
"Firefox >= 91",
"last 2 Edge versions"
]
}

View File

@@ -1,51 +0,0 @@
# Jasmine Core 4.6.1 Release Notes
## Summary
This is a one-time backport of bug fixes from 5.x, for the benefit of Karma
users who may not be aware that they're still using 4.x.
No further 4.x releases are planned. If possible, you should upgrade to the
latest 5.x instead of 4.6.1. If you're using Karma, you can do this by
installing jasmine-core 5.x and adding an override to package.json:
```
{
// ...
"overrides": {
"karma-jasmine": {
"jasmine-core": "^5.0.0"
}
}
}
```
## Bug Fixes
* Removed unnecessary throw when building stack trace
* Fixed error when formatting Error object with non-Error cause property
Merges [#2013](https://github.com/jasmine/jasmine/pull/2013) from @angrycat9000.
Fixes [#2011](https://github.com/jasmine/jasmine/issues/2011).
* Accessibility: Always provide a non-color indication that a spec is pending
* Accessibility: Improved contrast of version number and inactive tab links
## Supported environments
jasmine-core 4.6.1 has been tested in the following environments.
| Environment | Supported versions |
|-------------------|--------------------|
| Node | 12.17+, 14, 16, 18 |
| Safari | 14-16 |
| Chrome | 125 |
| Firefox | 91, 102, 126 |
| Edge | 124 |
------
_Release Notes generated with _[Anchorman](http://github.com/infews/anchorman)_

View File

@@ -1,68 +0,0 @@
# Jasmine Core 5.0.0-alpha.0 Release Notes
## Summary
This release primarily adds support for parallel execution via the `jasmine`
package. Please see its release notes and the
[parallel documentation](https://jasmine.github.io/tutorials/running_specs_in_parallel)
for more information. Additionally, this release cleans up a few outdated
interfaces.
This is a pre-release for a major version. It contains breaking changes, and
there may be further breaking changes between this release and the final 5.0.0
release.
## Breaking changes
* Use addEventListener in browsers rather than setting window.onerror
This simplifies error handling in browsers, makes Jasmine's own integration
tests easier to debug, and provides stack traces for more unhandled
exceptions. However, some browsers will provide less error information when
the error comes from a file:// URL. Additionally, Jasmine will no longer
override existing onerror handlers, and setting window.onerror will no longer
override Jasmine's global error handling. (Use `jasmine.spyOnGlobalErrors`
instead.)
* Made Env#execute async
* Env#execute no longer takes a callback
* The `boot` function exported by the core module returns the same object
every time it's called.
* Removed node_boot.js. Use the exported `boot` function instead.
### Changes to supported environments
The following previously supported environments are no longer supported:
* Node <16.14
* Safari 14
* Firefox 91
Although this release may still work in some of those environments, we no
longer test against them and won't try to maintain compatibility with them in
future releases.
## New features
* Support for parallel execution in Node.js using the `jasmine` package
## Bug fixes
* The global error handler is uninstalled at the end of env execution.
## Supported environments
jasmine-core 5.0.0-alpha.0 has been tested in the following environments.
| Environment | Supported versions |
|-------------------|--------------------|
| Node | 16.14+, 18 |
| Safari | 15-16 |
| Chrome | 111 |
| Firefox | 102, 111 |
| Edge | 111 |
------
_Release Notes generated with _[Anchorman](http://github.com/infews/anchorman)_

View File

@@ -1,39 +0,0 @@
# Jasmine Core 5.0.0-alpha.1 Release Notes
## Summary
This release provides improved support for parallel execution via the `jasmine`
package. Please see its release notes and the
[parallel documentation](https://jasmine.github.io/tutorials/running_specs_in_parallel)
for more information.
## New features and bug fixes
* Parallel: Cleaner interface for reporter dispatching
* When Env#config is called from spec and helper files in parallel mode, throw
an error rather than behaving unpredictably.
## Documentation improvements
* API reference docs for parallel support APIs that jasmine-npm uses
## Internal improvements
* Updated dev dependencies
## Supported environments
jasmine-core 5.0.0-alpha.1 has been tested in the following environments.
| Environment | Supported versions |
|-------------------|--------------------|
| Node | 16.14+, 18 |
| Safari | 15-16 |
| Chrome | 112 |
| Firefox | 102, 111 |
| Edge | 111 |
------
_Release Notes generated with _[Anchorman](http://github.com/infews/anchorman)_

View File

@@ -1,28 +0,0 @@
# Jasmine Core 5.0.0-beta.0 Release Notes
This release supports the 5.0.0-beta-0 release of the `jasmine` package.
## Breaking changes
* Dropped support for Node 16
## New features
* Added support for Node 20
## Supported environments
jasmine-core 5.0.0-beta.0 has been tested in the following environments.
| Environment | Supported versions |
|-------------------|--------------------|
| Node | 18, 20 |
| Safari | 15-16 |
| Chrome | 112 |
| Firefox | 102, 112 |
| Edge | 112 |
------
_Release Notes generated with _[Anchorman](http://github.com/infews/anchorman)_

View File

@@ -1,55 +0,0 @@
# Jasmine Core 5.0.0 Release Notes
## Summary
This is a major release that includes breaking changes. It primarily adds
support for parallel execution in Node via the `jasmine` package. Most users
should be able to upgrade without changes, but please read the list of breaking
changes below and see the [migration guide](https://jasmine.github.io/tutorials/upgrading_to_Jasmine_5.0)
for more information.
## Breaking changes
* Dropped support for Node 12, 14, and 16
* Dropped support for Safari 14 and Firefox 91
* Made Env#execute async and removed the callback parameter
* Global errors are detected via addEventListener rather than setting window.onerror
* The `boot` function exported by the core module returns the same object
every time it's called.
* Removed node_boot.js. Use the exported `boot` function instead.
## New features
* Support for parallel execution in Node via the `jasmine` package
See the [parallel guide](https://jasmine.github.io/tutorials/running_specs_in_parallel)
for more information.
* Added Node 20 to supported environments
## Bug fixes
* Accessibility: Always provide a non-color indication that a spec is pending
* Accessibility: Improved contrast of version number and inactive tab links
* Uninstall the global error handler at the end of env execution
## Internal improvements
* Updated dev dependencies
* Dogfood parallel execution feature in CI
## Supported environments
jasmine-core 5.0.0 has been tested in the following environments.
| Environment | Supported versions |
|-------------------|--------------------|
| Node | 18, 20 |
| Safari | 15-16 |
| Chrome | 113 |
| Firefox | 102, 113 |
| Edge | 113 |
------
_Release Notes generated with _[Anchorman](http://github.com/infews/anchorman)_

View File

@@ -1,25 +0,0 @@
# Jasmine Core 5.0.1 Release Notes
## Changes
* Optionally restore the pre-5.0 behavior of boot() always creating a new instance
This is needed by jasmine-npm (and likely other tools like it) that may
need to create and use multiple envs in sequence.
## Supported environments
jasmine-core 5.0.1 has been tested in the following environments.
| Environment | Supported versions |
|-------------------|--------------------|
| Node | 18, 20 |
| Safari | 15-16 |
| Chrome | 114 |
| Firefox | 102, 113 |
| Edge | 113 |
------
_Release Notes generated with _[Anchorman](http://github.com/infews/anchorman)_

View File

@@ -1,39 +0,0 @@
# Jasmine Core 5.1.0 Release Notes
## Changes
* Exclude inherited Error properties from stack trace
* Fixed error when formatting Error object with non-Error cause property
Merges [#2013](https://github.com/jasmine/jasmine/pull/2013) from @angrycat9000.
Fixes [#2011](https://github.com/jasmine/jasmine/issues/2011).
* Added `throwUnless` and `throwUnlessAsync`
These are similar to `expect` and `expectAsync` except that they throw
exceptions rather than recording matcher failures as spec/suite failures.
They're intended to support using Jasmine matchers in [testing-library's](https://testing-library.com/)
`waitFor`, and also provide a way to integration-test custom matchers.
Fixes [#2003](https://github.com/jasmine/jasmine/issues/2003).
Fixes [#1980](https://github.com/jasmine/jasmine/issues/1980).
## Supported environments
jasmine-core 5.1.0 has been tested in the following environments.
| Environment | Supported versions |
|-------------------|--------------------|
| Node | 18, 20 |
| Safari | 15-16 |
| Chrome | 114 |
| Firefox | 102, 113 |
| Edge | 113 |
------
_Release Notes generated with _[Anchorman](http://github.com/infews/anchorman)_

View File

@@ -1,28 +0,0 @@
# Jasmine Core 5.1.1 Release Notes
## Bug Fixes
* Fixed global variable leak in the main process when running in parallel mode
* Removed unnecessary throw when building expectation results
## Documentation Improvements
* Improved jsdocs for originalFn argument to createSpy
* Link to 5.0 upgrade guide in README, not 4.0
## Supported environments
jasmine-core 5.1.1 has been tested in the following environments.
| Environment | Supported versions |
|-------------------|--------------------|
| Node | 18, 20 |
| Safari | 15-16 |
| Chrome | 116 |
| Firefox | 102, 116 |
| Edge | 115 |
------
_Release Notes generated with _[Anchorman](http://github.com/infews/anchorman)_

View File

@@ -1,27 +0,0 @@
# Jasmine Core 5.1.2 Release Notes
## Bug Fixes
* Fixed `throwUnlessAsync`
* Fixes [#2026](https://github.com/jasmine/jasmine/issues/2026)
# Documentation improvements
* Added Safari 17 to supported browsers
* Added Firefox 115 (current ESR) to supported browsers
## Supported environments
This version has been tested in the following environments.
| Environment | Supported versions |
|-------------------|--------------------|
| Node | 18, 20 |
| Safari | 15-17 |
| Chrome | 121 |
| Firefox | 102, 115, 122 |
| Edge | 121 |
------
_Release Notes generated with _[Anchorman](http://github.com/infews/anchorman)_

View File

@@ -1,35 +0,0 @@
# Jasmine Core 5.2.0 Release Notes
## Bug Fixes
* Fixed stack trace filtering in FF when the developer tools are open
* Fixed handling of browser `error` events with message but no error
## New Features
* Improved the error message of the toHaveSize matcher.
* Merges [#2033](https://github.com/jasmine/jasmine/pull/2033) from @stephanreiter
* HTML reporter: show debug logs with white-space: pre
## Documentation improvements
* Improved discoverability of asymmetric equality testers
* Added an example for withContext()
* Clarified spyOnGlobalErrorsAsync API docs
* Added Node 22 to supported environments
## Supported environments
This version has been tested in the following environments.
| Environment | Supported versions |
|-------------------|--------------------|
| Node | 18, 20, 22 |
| Safari | 15-17 |
| Chrome | 126 |
| Firefox | 102, 115, 128 |
| Edge | 126 |
------
_Release Notes generated with _[Anchorman](http://github.com/infews/anchorman)_

View File

@@ -1,35 +0,0 @@
# Jasmine Core 5.3.0 Release Notes
## Changes
* Improved performance in Safari
* Merges [#2040](https://github.com/jasmine/jasmine/pull/2040) from @dcsaszar
* Fixes [#2008](https://github.com/jasmine/jasmine/issues/2008)
* Improved performance in Playwright Webkit on Windows
* Merges [#2034](https://github.com/jasmine/jasmine/pull/2034) from @m-akinc
* Throw if spying has no effect, as when spying on localStorage methods in Firefox and Safari 17
* See [#2036](https://github.com/jasmine/jasmine/issues/2036) and [#2007](https://github.com/jasmine/jasmine/issues/2007)
## Documentation improvements
* Added API reference for reporter capabilities
## Supported environments
This version has been tested in the following environments.
| Environment | Supported versions |
|-------------------|--------------------|
| Node | 18, 20, 22 |
| Safari | 15-17 |
| Chrome | 128 |
| Firefox | 102, 115, 130 |
| Edge | 128 |
------
_Release Notes generated with _[Anchorman](http://github.com/infews/anchorman)_

View File

@@ -36,11 +36,11 @@ failfile=`mktemp -t jasmine-results.XXXXXX` || exit 1
run_browser chrome latest "macOS 12"
run_browser firefox latest
run_browser firefox 115
run_browser firefox 102
run_browser safari 17
run_browser firefox 91
run_browser safari 16
run_browser safari 15
run_browser safari 14
run_browser MicrosoftEdge latest
echo

View File

@@ -29,7 +29,7 @@ describe('AsyncExpectation', function() {
it('converts a fail to a pass', function() {
const addExpectationResult = jasmine.createSpy('addExpectationResult'),
actual = Promise.reject(new Error('nope')),
actual = Promise.reject(),
expectation = jasmineUnderTest.Expectation.asyncFactory({
matchersUtil: new jasmineUnderTest.MatchersUtil({
pp: function() {}
@@ -138,7 +138,7 @@ describe('AsyncExpectation', function() {
}
},
addExpectationResult = jasmine.createSpy('addExpectationResult'),
actual = Promise.reject(new Error('nope')),
actual = Promise.reject(),
expectation = jasmineUnderTest.Expectation.asyncFactory({
actual: actual,
addExpectationResult: addExpectationResult,

View File

@@ -20,47 +20,6 @@ describe('ClearStack', function() {
MessageChannel: fakeMessageChannel
};
});
it('uses MessageChannel to reduce setTimeout clamping', function() {
const fakeChannel = fakeMessageChannel();
spyOn(fakeChannel.port2, 'postMessage');
const queueMicrotask = jasmine.createSpy('queueMicrotask');
const global = {
navigator: {
userAgent:
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit/605.0.8 (KHTML, like Gecko) Version/15.1 Safari/605.0.8'
},
MessageChannel: function() {
return fakeChannel;
},
queueMicrotask
};
const clearStack = jasmineUnderTest.getClearStack(global);
for (let i = 0; i < 9; i++) {
clearStack(function() {});
}
expect(fakeChannel.port2.postMessage).not.toHaveBeenCalled();
clearStack(function() {});
expect(fakeChannel.port2.postMessage).toHaveBeenCalledTimes(1);
});
});
describe("in WebKit (Playwright's build for Windows)", function() {
usesQueueMicrotaskWithSetTimeout(function() {
return {
navigator: {
userAgent:
'Mozilla/5.0 (Windows NT 6.2; Win64; x64) AppleWebKit/605.1.15 (KHTML, like Gecko)'
},
// queueMicrotask should be used even though MessageChannel is present
MessageChannel: fakeMessageChannel
};
});
});
describe('in browsers other than Safari', function() {

View File

@@ -80,19 +80,6 @@ describe('Env', function() {
);
expect(suite.children[1].children[1].children[0].children).toBeFalsy();
});
it('throws if called in parallel mode', function() {
env.setParallelLoadingState('helpers');
check();
env.setParallelLoadingState('specs');
check();
function check() {
expect(function() {
env.topSuite();
}).toThrowError("'topSuite' is not available in parallel mode");
}
});
});
it('accepts its own current configureation', function() {
@@ -264,15 +251,6 @@ describe('Env', function() {
describe('#fdescribe', function() {
behavesLikeDescribe('fdescribe');
it('throws an error in parallel mode', function() {
env.setParallelLoadingState('specs');
expect(function() {
env.fdescribe('a suite', function() {
env.it('a spec');
});
}).toThrowError("'fdescribe' is not available in parallel mode");
});
});
describe('xdescribe', function() {
@@ -394,13 +372,6 @@ describe('Env', function() {
env.fit('huge timeout', function() {}, 2147483648);
}).toThrowError('Timeout value cannot be greater than 2147483647');
});
it('throws an error in parallel mode', function() {
env.setParallelLoadingState('specs');
expect(function() {
env.fit('a spec', function() {});
}).toThrowError("'fit' is not available in parallel mode");
});
});
describe('#beforeEach', function() {
@@ -423,28 +394,6 @@ describe('Env', function() {
env.beforeEach(function() {}, 2147483648);
}).toThrowError('Timeout value cannot be greater than 2147483647');
});
it('throws when called at the top level in a spec file in parallel mode', function() {
env.setParallelLoadingState('specs');
expect(function() {
env.beforeEach(function() {});
}).toThrowError(
'In parallel mode, beforeEach must be in a describe block or in a helper file'
);
});
it('does not throw when called at the top level in a helper file in parallel mode', function() {
env.setParallelLoadingState('helpers');
env.beforeEach(function() {});
});
it('does not throw when called in a describe in a spec file in parallel mode', function() {
env.setParallelLoadingState('specs');
env.describe('a suite', function() {
env.beforeEach(function() {});
env.it('a spec');
});
});
});
describe('#beforeAll', function() {
@@ -467,47 +416,6 @@ describe('Env', function() {
env.beforeAll(function() {}, 2147483648);
}).toThrowError('Timeout value cannot be greater than 2147483647');
});
describe('in parallel mode', function() {
it('throws an error when called at the top level', function() {
env.setParallelLoadingState('helpers');
check();
env.setParallelLoadingState('specs');
check();
function check() {
expect(function() {
env.beforeAll(function() {});
}).toThrowError(
"In parallel mode, 'beforeAll' must be in a describe block. " +
'Use the globalSetup config property for exactly-once setup in' +
' parallel mode.'
);
}
});
it('does not throw an error when called in a describe', function() {
env.setParallelLoadingState('helpers');
check();
env.setParallelLoadingState('specs');
check();
function check() {
let done = false;
env.describe('a suite', function() {
expect(function() {
env.it('a spec');
env.beforeAll(function() {});
}).not.toThrow();
done = true;
});
expect(done).toBeTrue();
}
});
});
});
describe('#afterEach', function() {
@@ -530,28 +438,6 @@ describe('Env', function() {
env.afterEach(function() {}, 2147483648);
}).toThrowError('Timeout value cannot be greater than 2147483647');
});
it('throws when called at the top level in a spec file in parallel mode', function() {
env.setParallelLoadingState('specs');
expect(function() {
env.afterEach(function() {});
}).toThrowError(
'In parallel mode, afterEach must be in a describe block or in a helper file'
);
});
it('does not throw when called at the top level in a helper file in parallel mode', function() {
env.setParallelLoadingState('helpers');
env.afterEach(function() {});
});
it('does not throw when called in a describe in a spec file in parallel mode', function() {
env.setParallelLoadingState('specs');
env.describe('a suite', function() {
env.afterEach(function() {});
env.it('a spec');
});
});
});
describe('#afterAll', function() {
@@ -574,47 +460,6 @@ describe('Env', function() {
env.afterAll(function() {}, 2147483648);
}).toThrowError('Timeout value cannot be greater than 2147483647');
});
describe('in parallel mode', function() {
it('throws an error when called at the top level', function() {
env.setParallelLoadingState('helpers');
check();
env.setParallelLoadingState('specs');
check();
function check() {
expect(function() {
env.afterAll(function() {});
}).toThrowError(
"In parallel mode, 'afterAll' must be in a describe block. " +
'Use the globalTeardown config property for exactly-once ' +
'teardown in parallel mode.'
);
}
});
it('does not throw an error when called in a describe', function() {
env.setParallelLoadingState('helpers');
check();
env.setParallelLoadingState('specs');
check();
function check() {
let done = false;
env.describe('a suite', function() {
expect(function() {
env.it('a spec');
env.afterAll(function() {});
}).not.toThrow();
done = true;
});
expect(done).toBeTrue();
}
});
});
});
describe('when not constructed with suppressLoadErrors: true', function() {
@@ -747,32 +592,6 @@ describe('Env', function() {
expect(id).toEqual(env.topSuite().id);
});
});
it('should not reset the topSuite if parallelReset was called since the last run', async function() {
await env.execute();
env.parallelReset();
spyOn(jasmineUnderTest.Suite.prototype, 'reset');
await env.execute();
expect(jasmineUnderTest.Suite.prototype.reset).not.toHaveBeenCalled();
});
describe('In parallel mode', function() {
it('rejects if random is set to false', async function() {
env.configure({ random: false });
env.setParallelLoadingState('specs');
await expectAsync(env.execute()).toBeRejectedWithError(
'Randomization cannot be disabled in parallel mode'
);
});
it('rejects if seed is set', async function() {
env.configure({ seed: 1234 });
env.setParallelLoadingState('specs');
await expectAsync(env.execute()).toBeRejectedWithError(
'Random seed cannot be set in parallel mode'
);
});
});
});
describe('#spyOnGlobalErrorsAsync', function() {
@@ -789,46 +608,4 @@ describe('Env', function() {
).toBeRejectedWithError(msg);
});
});
describe('#addReporter', function() {
it('throws when called in parallel mode', function() {
env.setParallelLoadingState('helpers');
expect(function() {
env.addReporter({});
}).toThrowError('Reporters cannot be added via Env in parallel mode');
env.setParallelLoadingState('specs');
expect(function() {
env.addReporter({});
}).toThrowError('Reporters cannot be added via Env in parallel mode');
});
});
describe('#clearReporters', function() {
it('throws when called in parallel mode', function() {
env.setParallelLoadingState('helpers');
expect(function() {
env.clearReporters();
}).toThrowError('Reporters cannot be removed via Env in parallel mode');
env.setParallelLoadingState('specs');
expect(function() {
env.clearReporters();
}).toThrowError('Reporters cannot be removed via Env in parallel mode');
});
});
describe('#configure', function() {
it('throws when called in parallel mode', function() {
env.setParallelLoadingState('helpers');
expect(function() {
env.configure({});
}).toThrowError('Jasmine cannot be configured via Env in parallel mode');
env.setParallelLoadingState('specs');
expect(function() {
env.configure({});
}).toThrowError('Jasmine cannot be configured via Env in parallel mode');
});
});
});

View File

@@ -153,33 +153,12 @@ describe('ExceptionFormatter', function() {
);
});
it('filters Jasmine stack frames with Firefox async annotations', function() {
const error = {
stack:
'http://localhost:8888/__spec__/core/UtilSpec.js:115:28\n' +
'promise callback*fn1@http://localhost:8888/__jasmine__/jasmine.js:4320:27\n' +
'setTimeout handler*fn2@http://localhost:8888/__jasmine__/jasmine.js:4320:27\n' +
'http://localhost:8888/__spec__/core/UtilSpec.js:115:28'
};
const subject = new jasmineUnderTest.ExceptionFormatter({
jasmineFile: 'http://localhost:8888/__jasmine__/jasmine.js'
});
const result = subject.stack(error);
expect(result).toEqual(
'http://localhost:8888/__spec__/core/UtilSpec.js:115:28\n' +
'<Jasmine>\n' +
'http://localhost:8888/__spec__/core/UtilSpec.js:115:28'
);
});
it('filters Jasmine stack frames in this environment', function() {
const error = new Error('an error');
const subject = new jasmineUnderTest.ExceptionFormatter({
jasmineFile: jasmine.util.jasmineFile()
});
const result = subject.stack(error);
jasmine.debugLog('Original stack trace: ' + error.stack);
jasmine.debugLog('Filtered stack trace: ' + result);
const lines = result.split('\n');
if (lines[0].match(/an error/)) {
@@ -218,7 +197,7 @@ describe('ExceptionFormatter', function() {
expect(new jasmineUnderTest.ExceptionFormatter().stack()).toBeNull();
});
it("includes the error's own properties in stack", function() {
it('includes error properties in stack', function() {
const error = new Error('an error');
error.someProperty = 'hello there';
@@ -227,19 +206,6 @@ describe('ExceptionFormatter', function() {
expect(result).toMatch(/error properties:.*someProperty.*hello there/);
});
it('does not include inherited error properties', function() {
function CustomError(msg) {
Error.call(this, msg);
}
CustomError.prototype = new Error();
CustomError.prototype.anInheritedProp = 'something';
const error = new CustomError('nope');
const result = new jasmineUnderTest.ExceptionFormatter().stack(error);
expect(result).not.toContain('anInheritedProp');
});
describe('When omitMessage is true', function() {
it('filters the message from V8-style stack traces', function() {
const error = {
@@ -291,7 +257,17 @@ describe('ExceptionFormatter', function() {
});
});
describe('when the error has a cause property', function() {
describe('In environments that support the cause property of Errors', function() {
beforeEach(function() {
const inner = new Error('inner');
const outer = new Error('outer', { cause: inner });
if (!outer.cause) {
// Currently: Node 12, Node 14, Safari 14
pending('Environment does not support error cause');
}
});
it('recursively includes the cause in the stack trace in this environment', function() {
const subject = new jasmineUnderTest.ExceptionFormatter();
const rootCause = new Error('root cause');

View File

@@ -1,42 +1,56 @@
describe('GlobalErrors', function() {
it('calls the added handler on error', function() {
const fakeGlobal = browserGlobal();
const fakeGlobal = minimalBrowserGlobal();
const handler = jasmine.createSpy('errorHandler');
const errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
errors.install();
errors.pushListener(handler);
const error = new Error('nope');
dispatchErrorEvent(fakeGlobal, { error });
fakeGlobal.onerror('foo');
expect(handler).toHaveBeenCalledWith(
jasmine.is(error),
jasmine.objectContaining({ error: jasmine.is(error) })
);
expect(handler).toHaveBeenCalledWith('foo');
});
it('is not affected by overriding global.onerror', function() {
const fakeGlobal = browserGlobal();
it('enables external interception of error by overriding global.onerror', function() {
const fakeGlobal = minimalBrowserGlobal();
const handler = jasmine.createSpy('errorHandler');
const hijackHandler = jasmine.createSpy('hijackErrorHandler');
const errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
errors.install();
errors.pushListener(handler);
fakeGlobal.onerror = () => {};
fakeGlobal.onerror = hijackHandler;
const error = new Error('nope');
dispatchErrorEvent(fakeGlobal, { error });
fakeGlobal.onerror('foo');
expect(hijackHandler).toHaveBeenCalledWith('foo');
expect(handler).not.toHaveBeenCalled();
});
it('calls the global error handler with all parameters', function() {
const fakeGlobal = minimalBrowserGlobal();
const handler = jasmine.createSpy('errorHandler');
const errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
const fooError = new Error('foo');
errors.install();
errors.pushListener(handler);
fakeGlobal.onerror(fooError.message, 'foo.js', 1, 1, fooError);
expect(handler).toHaveBeenCalledWith(
jasmine.is(error),
jasmine.objectContaining({ error: jasmine.is(error) })
fooError.message,
'foo.js',
1,
1,
fooError
);
});
it('only calls the most recent handler', function() {
const fakeGlobal = browserGlobal();
const fakeGlobal = minimalBrowserGlobal();
const handler1 = jasmine.createSpy('errorHandler1');
const handler2 = jasmine.createSpy('errorHandler2');
const errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
@@ -45,18 +59,14 @@ describe('GlobalErrors', function() {
errors.pushListener(handler1);
errors.pushListener(handler2);
const error = new Error('nope');
dispatchErrorEvent(fakeGlobal, { error });
fakeGlobal.onerror('foo');
expect(handler1).not.toHaveBeenCalled();
expect(handler2).toHaveBeenCalledWith(
jasmine.is(error),
jasmine.objectContaining({ error: jasmine.is(error) })
);
expect(handler2).toHaveBeenCalledWith('foo');
});
it('calls previous handlers when one is removed', function() {
const fakeGlobal = browserGlobal();
const fakeGlobal = minimalBrowserGlobal();
const handler1 = jasmine.createSpy('errorHandler1');
const handler2 = jasmine.createSpy('errorHandler2');
const errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
@@ -67,13 +77,9 @@ describe('GlobalErrors', function() {
errors.popListener(handler2);
const error = new Error('nope');
dispatchErrorEvent(fakeGlobal, { error });
fakeGlobal.onerror('foo');
expect(handler1).toHaveBeenCalledWith(
jasmine.is(error),
jasmine.objectContaining({ error: jasmine.is(error) })
);
expect(handler1).toHaveBeenCalledWith('foo');
expect(handler2).not.toHaveBeenCalled();
});
@@ -84,27 +90,34 @@ describe('GlobalErrors', function() {
}).toThrowError('popListener expects a listener');
});
it('uninstalls itself', function() {
const fakeGlobal = browserGlobal();
it('uninstalls itself, putting back a previous callback', function() {
const originalCallback = jasmine.createSpy('error');
const fakeGlobal = {
...minimalBrowserGlobal(),
onerror: originalCallback
};
const errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
function unrelatedListener() {}
expect(fakeGlobal.onerror).toBe(originalCallback);
errors.install();
fakeGlobal.addEventListener('error', unrelatedListener);
expect(fakeGlobal.onerror).not.toBe(originalCallback);
errors.uninstall();
expect(fakeGlobal.listeners_.error).toEqual([unrelatedListener]);
expect(fakeGlobal.onerror).toBe(originalCallback);
});
it('rethrows the original error when there is no handler', function() {
const fakeGlobal = browserGlobal();
const fakeGlobal = minimalBrowserGlobal();
const errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
const originalError = new Error('nope');
errors.install();
try {
dispatchErrorEvent(fakeGlobal, { error: originalError });
fakeGlobal.onerror(originalError);
} catch (e) {
expect(e).toBe(originalError);
}
@@ -276,117 +289,128 @@ describe('GlobalErrors', function() {
describe('Reporting unhandled promise rejections in the browser', function() {
it('subscribes and unsubscribes from the unhandledrejection event', function() {
const fakeGlobal = browserGlobal();
const errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
const fakeGlobal = jasmine.createSpyObj('globalErrors', [
'addEventListener',
'removeEventListener',
'onerror'
]),
errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
errors.install();
expect(fakeGlobal.listeners_.unhandledrejection).toEqual([
expect(fakeGlobal.addEventListener).toHaveBeenCalledWith(
'unhandledrejection',
jasmine.any(Function)
]);
);
const addedListener = fakeGlobal.addEventListener.calls.argsFor(0)[1];
errors.uninstall();
expect(fakeGlobal.listeners_.unhandledrejection).toEqual([]);
});
it('reports rejections whose reason is a string', function() {
const fakeGlobal = browserGlobal();
const handler = jasmine.createSpy('errorHandler');
const errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
errors.install();
errors.pushListener(handler);
const event = { reason: 'nope' };
dispatchUnhandledRejectionEvent(fakeGlobal, event);
expect(handler).toHaveBeenCalledWith(
'Unhandled promise rejection: nope',
event
expect(fakeGlobal.removeEventListener).toHaveBeenCalledWith(
'unhandledrejection',
addedListener
);
});
it('reports rejections whose reason is an Error', function() {
const fakeGlobal = browserGlobal();
const handler = jasmine.createSpy('errorHandler');
const errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
it('reports rejections whose reason is a string', function() {
const fakeGlobal = jasmine.createSpyObj('globalErrors', [
'addEventListener',
'removeEventListener',
'onerror'
]),
handler = jasmine.createSpy('errorHandler'),
errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
errors.install();
errors.pushListener(handler);
const addedListener = fakeGlobal.addEventListener.calls.argsFor(0)[1];
addedListener({ reason: 'nope' });
expect(handler).toHaveBeenCalledWith('Unhandled promise rejection: nope');
});
it('reports rejections whose reason is an Error', function() {
const fakeGlobal = jasmine.createSpyObj('globalErrors', [
'addEventListener',
'removeEventListener',
'onerror'
]),
handler = jasmine.createSpy('errorHandler'),
errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
errors.install();
errors.pushListener(handler);
const addedListener = fakeGlobal.addEventListener.calls.argsFor(0)[1];
const reason = new Error('bar');
const event = { reason };
dispatchUnhandledRejectionEvent(fakeGlobal, event);
addedListener({ reason: reason });
expect(handler).toHaveBeenCalledWith(
jasmine.objectContaining({
jasmineMessage: 'Unhandled promise rejection: Error: bar',
message: reason.message,
stack: reason.stack
}),
event
);
});
});
describe('Reporting uncaught exceptions in node.js', function() {
it('prepends a descriptive message when the error is not an `Error`', function() {
const fakeGlobal = {
process: {
on: jasmine.createSpy('process.on'),
removeListener: function() {},
listeners: function() {
return [];
},
removeAllListeners: function() {}
}
};
const handler = jasmine.createSpy('errorHandler');
const errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
errors.install();
errors.pushListener(handler);
uncaughtExceptionListener(fakeGlobal)(17);
expect(handler).toHaveBeenCalledWith(new Error('Uncaught exception: 17'));
});
it('substitutes a descriptive message when the error is falsy', function() {
const fakeGlobal = {
process: {
on: jasmine.createSpy('process.on'),
removeListener: function() {},
listeners: function() {
return [];
},
removeAllListeners: function() {}
}
};
const handler = jasmine.createSpy('errorHandler');
const errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
errors.install();
errors.pushListener(handler);
uncaughtExceptionListener(fakeGlobal)();
expect(handler).toHaveBeenCalledWith(
new Error('Uncaught exception with no error or message')
})
);
});
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('Enabling external interception of reported rejections by overriding global.onerror', function() {
it('overriding global.onerror intercepts rejections whose reason is a string', function() {
const fakeGlobal = jasmine.createSpyObj('globalErrors', [
'addEventListener'
]),
handler = jasmine.createSpy('errorHandler'),
hijackHandler = jasmine.createSpy('hijackErrorHandler'),
errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
errors.install();
errors.pushListener(handler);
fakeGlobal.onerror = hijackHandler;
const addedListener = fakeGlobal.addEventListener.calls.argsFor(0)[1];
addedListener({ reason: 'nope' });
expect(hijackHandler).toHaveBeenCalledWith(
'Unhandled promise rejection: nope'
);
expect(handler).not.toHaveBeenCalled();
});
it('overriding global.onerror intercepts rejections whose reason is an Error', function() {
const fakeGlobal = jasmine.createSpyObj('globalErrors', [
'addEventListener'
]),
handler = jasmine.createSpy('errorHandler'),
hijackHandler = jasmine.createSpy('hijackErrorHandler'),
errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
errors.install();
errors.pushListener(handler);
fakeGlobal.onerror = hijackHandler;
const addedListener = fakeGlobal.addEventListener.calls.argsFor(0)[1];
const reason = new Error('bar');
addedListener({ reason: reason });
expect(hijackHandler).toHaveBeenCalledWith(
jasmine.objectContaining({
jasmineMessage: 'Unhandled promise rejection: Error: bar',
message: reason.message,
stack: reason.stack
})
);
expect(handler).not.toHaveBeenCalled();
});
});
});
describe('#setOverrideListener', function() {
it('overrides the existing handlers in browsers until removed', function() {
const fakeGlobal = browserGlobal();
const fakeGlobal = minimalBrowserGlobal();
const handler0 = jasmine.createSpy('handler0');
const handler1 = jasmine.createSpy('handler1');
const overrideHandler = jasmine.createSpy('overrideHandler');
@@ -396,18 +420,19 @@ describe('GlobalErrors', function() {
errors.pushListener(handler0);
errors.setOverrideListener(overrideHandler, () => {});
errors.pushListener(handler1);
dispatchErrorEvent(fakeGlobal, { error: 'foo' });
fakeGlobal.onerror('foo');
fakeGlobal.onerror(null, null, null, null, new Error('bar'));
expect(overrideHandler).toHaveBeenCalledWith('foo');
expect(overrideHandler).toHaveBeenCalledWith(new Error('bar'));
expect(handler0).not.toHaveBeenCalled();
expect(handler1).not.toHaveBeenCalled();
errors.removeOverrideListener();
const event = { error: 'baz' };
dispatchErrorEvent(fakeGlobal, event);
fakeGlobal.onerror('baz');
expect(overrideHandler).not.toHaveBeenCalledWith('baz');
expect(handler1).toHaveBeenCalledWith('baz', event);
expect(handler1).toHaveBeenCalledWith('baz');
});
it('overrides the existing handlers in Node until removed', function() {
@@ -507,7 +532,7 @@ describe('GlobalErrors', function() {
});
it('throws if there is already an override handler', function() {
const errors = new jasmineUnderTest.GlobalErrors(browserGlobal());
const errors = new jasmineUnderTest.GlobalErrors(minimalBrowserGlobal());
errors.setOverrideListener(() => {}, () => {});
expect(function() {
@@ -519,7 +544,7 @@ describe('GlobalErrors', function() {
describe('#removeOverrideListener', function() {
it("calls the handler's onRemove callback", function() {
const onRemove = jasmine.createSpy('onRemove');
const errors = new jasmineUnderTest.GlobalErrors(browserGlobal());
const errors = new jasmineUnderTest.GlobalErrors(minimalBrowserGlobal());
errors.setOverrideListener(() => {}, onRemove);
errors.removeOverrideListener();
@@ -528,43 +553,17 @@ describe('GlobalErrors', function() {
});
it('does not throw if there is no handler', function() {
const errors = new jasmineUnderTest.GlobalErrors(browserGlobal());
const errors = new jasmineUnderTest.GlobalErrors(minimalBrowserGlobal());
expect(() => errors.removeOverrideListener()).not.toThrow();
});
});
function browserGlobal() {
function minimalBrowserGlobal() {
return {
listeners_: { error: [], unhandledrejection: [] },
addEventListener(eventName, listener) {
this.listeners_[eventName].push(listener);
},
removeEventListener(eventName, listener) {
this.listeners_[eventName] = this.listeners_[eventName].filter(
l => l !== listener
);
}
addEventListener() {},
removeEventListener() {},
onerror: null
};
}
function dispatchErrorEvent(global, event) {
expect(global.listeners_.error.length)
.withContext('number of error listeners')
.toBeGreaterThan(0);
for (const l of global.listeners_.error) {
l(event);
}
}
function dispatchUnhandledRejectionEvent(global, event) {
expect(global.listeners_.unhandledrejection.length)
.withContext('number of unhandledrejection listeners')
.toBeGreaterThan(0);
for (const l of global.listeners_.unhandledrejection) {
l(event);
}
}
});

View File

@@ -1,176 +0,0 @@
describe('ParallelReportDispatcher', function() {
it('dispatches the standard reporter events', async function() {
const subject = new jasmineUnderTest.ParallelReportDispatcher(() => {}, {
globalErrors: mockGlobalErrors()
});
const events = [
'jasmineStarted',
'jasmineDone',
'suiteStarted',
'suiteDone',
'specStarted',
'specDone'
];
const reporter = jasmine.createSpyObj('reporter', events);
subject.addReporter(reporter);
for (const eventName of events) {
const payload = { payloadFor: eventName };
await subject[eventName](payload);
expect(reporter[eventName]).toHaveBeenCalledWith(payload);
}
});
it('installs and uninstalls the global error handler', function() {
const globalErrors = mockGlobalErrors();
const subject = new jasmineUnderTest.ParallelReportDispatcher(() => {}, {
globalErrors
});
subject.installGlobalErrors();
expect(globalErrors.install).toHaveBeenCalled();
subject.uninstallGlobalErrors();
expect(globalErrors.uninstall).toHaveBeenCalled();
});
it('handles global errors from async reporters', async function() {
const globalErrors = mockGlobalErrors();
const onError = jasmine.createSpy('onError');
const subject = new jasmineUnderTest.ParallelReportDispatcher(onError, {
globalErrors
});
const reporter = jasmine.createSpyObj('reporter', [
'jasmineStarted',
'jasmineDone'
]);
let resolveStarted;
reporter.jasmineStarted.and.callFake(function() {
return new Promise(function(res) {
resolveStarted = res;
});
});
subject.addReporter(reporter);
const promise = subject.jasmineStarted({});
expect(globalErrors.pushListener).toHaveBeenCalled();
expect(globalErrors.popListener).not.toHaveBeenCalled();
const error = new Error('nope');
globalErrors.pushListener.calls.argsFor(0)[0](error);
expect(onError).toHaveBeenCalledWith(error);
resolveStarted();
await promise;
expect(globalErrors.popListener).toHaveBeenCalled();
});
it('handles done(error) from callback-style async reporters', function() {
const globalErrors = mockGlobalErrors();
const onError = jasmine.createSpy('onError');
const subject = new jasmineUnderTest.ParallelReportDispatcher(onError, {
globalErrors
});
const reporter = jasmine.createSpyObj('reporter', [
'jasmineStarted',
'jasmineDone'
]);
let callback;
reporter.jasmineStarted = function(event, cb) {
callback = cb;
};
subject.addReporter(reporter);
subject.jasmineStarted({});
expect(callback).toBeInstanceOf(Function);
const error = new Error('nope');
callback(error);
expect(onError).toHaveBeenCalledWith(error);
});
it('handles done.fail() from callback-style async reporters', function() {
const globalErrors = mockGlobalErrors();
const onError = jasmine.createSpy('onError');
const subject = new jasmineUnderTest.ParallelReportDispatcher(onError, {
globalErrors
});
const reporter = jasmine.createSpyObj('reporter', [
'jasmineStarted',
'jasmineDone'
]);
let callback;
reporter.jasmineStarted = function(event, cb) {
callback = cb;
};
subject.addReporter(reporter);
subject.jasmineStarted({});
expect(callback).toBeInstanceOf(Function);
const error = new Error('nope');
callback.fail(error);
expect(onError).toHaveBeenCalledWith(error);
onError.calls.reset();
callback.fail();
expect(onError).toHaveBeenCalledWith(
new Error('A reporter called done.fail()')
);
});
it('handles errors due to mixed async style in reporters', async function() {
const globalErrors = mockGlobalErrors();
const onError = jasmine.createSpy('onError');
const subject = new jasmineUnderTest.ParallelReportDispatcher(onError, {
globalErrors
});
subject.addReporter({
async jasmineStarted(event, done) {
done();
}
});
await subject.jasmineStarted({});
expect(onError).toHaveBeenCalledWith(
new Error(
'An asynchronous before/it/after function took a done callback but also returned a promise. Either remove the done callback (recommended) or change the function to not return a promise.'
)
);
});
it('handles errors due to multiple done calls in reporters', async function() {
const globalErrors = mockGlobalErrors();
const onError = jasmine.createSpy('onError');
const subject = new jasmineUnderTest.ParallelReportDispatcher(onError, {
globalErrors
});
subject.addReporter({
jasmineStarted(event, done) {
done();
done();
}
});
await subject.jasmineStarted({});
expect(onError).toHaveBeenCalledWith(
new Error(
"An asynchronous reporter callback called its 'done' callback more than once."
)
);
});
function mockGlobalErrors() {
const globalErrors = jasmine.createSpyObj('globalErrors', [
'install',
'pushListener',
'popListener'
]);
globalErrors.install.and.callFake(function() {
globalErrors.uninstall = jasmine.createSpy('globalErrors.uninstall');
});
return globalErrors;
}
});

View File

@@ -185,20 +185,43 @@ describe('QueueRunner', function() {
queueRunner.execute();
});
it('does not log a deprecation', function(done) {
const err = new Error('foo'),
queueableFn1 = {
fn: function() {
return Promise.resolve(err);
}
},
deprecated = jasmine.createSpy('deprecated'),
queueRunner = new jasmineUnderTest.QueueRunner({
queueableFns: [queueableFn1],
deprecated: deprecated,
onComplete: function() {
expect(deprecated).not.toHaveBeenCalled();
done();
}
});
queueRunner.execute();
});
});
describe('and the argument is not an Error', function() {
it('does not report a failure', function(done) {
it('does not log a deprecation or report a failure', function(done) {
const queueableFn1 = {
fn: function() {
return Promise.resolve('not an error');
}
},
failFn = jasmine.createSpy('fail'),
deprecated = jasmine.createSpy('deprecated'),
queueRunner = new jasmineUnderTest.QueueRunner({
queueableFns: [queueableFn1],
deprecated: deprecated,
fail: failFn,
onComplete: function() {
expect(deprecated).not.toHaveBeenCalled();
expect(failFn).not.toHaveBeenCalled();
done();
}
@@ -383,12 +406,17 @@ describe('QueueRunner', function() {
}
},
nextQueueableFn = { fn: jasmine.createSpy('nextFn') },
deprecated = jasmine.createSpy('deprecated'),
queueRunner = new jasmineUnderTest.QueueRunner({
deprecated: deprecated,
queueableFns: [queueableFn, nextQueueableFn]
});
queueRunner.execute();
jasmine.clock().tick(1);
expect(nextQueueableFn.fn.calls.count()).toEqual(1);
// Don't issue a deprecation. The error already tells the user that
// something went wrong.
expect(deprecated).not.toHaveBeenCalled();
});
it('should return a null when you call done', function() {
@@ -459,32 +487,6 @@ describe('QueueRunner', function() {
expect(nextQueueableFn.fn).toHaveBeenCalled();
});
it('handles a global error event with a message but no error', function() {
const queueableFn = {
// eslint-disable-next-line no-unused-vars
fn: function(done) {
const currentHandler = globalErrors.pushListener.calls.mostRecent()
.args[0];
currentHandler(undefined, { message: 'nope' });
},
timeout: 1
};
const onException = jasmine.createSpy('onException');
const globalErrors = {
pushListener: jasmine.createSpy('pushListener'),
popListener: jasmine.createSpy('popListener')
};
const queueRunner = new jasmineUnderTest.QueueRunner({
queueableFns: [queueableFn],
onException: onException,
globalErrors: globalErrors
});
queueRunner.execute();
expect(onException).toHaveBeenCalledWith('nope');
});
it('handles exceptions thrown while waiting for the stack to clear', function() {
const queueableFn = {
fn: function(done) {
@@ -518,40 +520,6 @@ describe('QueueRunner', function() {
clearStack.calls.argsFor(0)[0]();
expect(onException).toHaveBeenCalledWith(error);
});
it('handles a global error event with no error while waiting for the stack to clear', function() {
const queueableFn = {
fn: function(done) {
done();
}
};
const errorListeners = [];
const globalErrors = {
pushListener: function(f) {
errorListeners.push(f);
},
popListener: function() {
errorListeners.pop();
}
};
const clearStack = jasmine.createSpy('clearStack');
const onException = jasmine.createSpy('onException');
const queueRunner = new jasmineUnderTest.QueueRunner({
queueableFns: [queueableFn],
globalErrors: globalErrors,
clearStack: clearStack,
onException: onException
});
queueRunner.execute();
jasmine.clock().tick();
expect(clearStack).toHaveBeenCalled();
expect(errorListeners.length).toEqual(1);
errorListeners[0](undefined, { message: 'nope' });
clearStack.calls.argsFor(0)[0]();
expect(onException).toHaveBeenCalledWith('nope');
});
});
describe('with a function that returns a promise', function() {
@@ -655,13 +623,11 @@ describe('QueueRunner', function() {
queueRunner.execute();
expect(onException).toHaveBeenCalledWith(
new Error(
'An asynchronous ' +
'before/it/after function took a done callback but also returned a ' +
'promise. ' +
'Either remove the done callback (recommended) or change the function ' +
'to not return a promise.'
)
'An asynchronous ' +
'before/it/after function took a done callback but also returned a ' +
'promise. ' +
'Either remove the done callback (recommended) or change the function ' +
'to not return a promise.'
);
});
@@ -677,17 +643,15 @@ describe('QueueRunner', function() {
queueRunner.execute();
expect(onException).toHaveBeenCalledWith(
new Error(
'An asynchronous ' +
'before/it/after function was defined with the async keyword but ' +
'also took a done callback. Either remove the done callback ' +
'(recommended) or remove the async keyword.'
)
'An asynchronous ' +
'before/it/after function was defined with the async keyword but ' +
'also took a done callback. Either remove the done callback ' +
'(recommended) or remove the async keyword.'
);
});
});
it('passes final errors to exception handlers', function() {
it('passes the error instance to exception handlers in HTML browsers', function() {
const error = new Error('fake error'),
onExceptionCallback = jasmine.createSpy('on exception callback'),
queueRunner = new jasmineUnderTest.QueueRunner({
@@ -695,11 +659,24 @@ describe('QueueRunner', function() {
});
queueRunner.execute();
queueRunner.handleFinalError(error);
queueRunner.handleFinalError(error.message, 'fake.js', 1, 1, error);
expect(onExceptionCallback).toHaveBeenCalledWith(error);
});
it('passes the first argument to exception handlers for compatibility', function() {
const error = new Error('fake error'),
onExceptionCallback = jasmine.createSpy('on exception callback'),
queueRunner = new jasmineUnderTest.QueueRunner({
onException: onExceptionCallback
});
queueRunner.execute();
queueRunner.handleFinalError(error.message);
expect(onExceptionCallback).toHaveBeenCalledWith(error.message);
});
it('calls exception handlers when an exception is thrown in a fn', function() {
const queueableFn = {
type: 'queueable',

View File

@@ -94,30 +94,6 @@ describe('SpyRegistry', function() {
}).not.toThrowError(/is not declared writable or has no setter/);
});
it('throws if assigning to the property is a no-op', function() {
const scope = {};
function original() {
return 1;
}
Object.defineProperty(scope, 'myFunc', {
get() {
return original;
},
set() {}
});
const spyRegistry = new jasmineUnderTest.SpyRegistry({
createSpy: createSpy
});
expect(function() {
spyRegistry.spyOn(scope, 'myFunc');
}).toThrowError(
"<spyOn> : Can't spy on myFunc because assigning to it had no effect"
);
});
it('overrides the method on the object and returns the spy', function() {
const originalFunctionWasCalled = false,
spyRegistry = new jasmineUnderTest.SpyRegistry({

View File

@@ -175,91 +175,4 @@ describe('SuiteBuilder', function() {
}
};
}
describe('#parallelReset', function() {
it('resets the top suite result', function() {
jasmineUnderTest.Suite.prototype.handleException.and.callThrough();
const env = { configuration: () => ({}) };
const suiteBuilder = new jasmineUnderTest.SuiteBuilder({ env });
suiteBuilder.topSuite.handleException(new Error('nope'));
suiteBuilder.parallelReset();
expect(suiteBuilder.topSuite.result).toEqual({
id: suiteBuilder.topSuite.id,
description: 'Jasmine__TopLevel__Suite',
fullName: '',
failedExpectations: [],
deprecationWarnings: [],
duration: null,
properties: null,
parentSuiteId: null,
filename: undefined
});
});
it('removes children of the top suite', function() {
const env = { configuration: () => ({}) };
const suiteBuilder = new jasmineUnderTest.SuiteBuilder({ env });
suiteBuilder.describe('a suite', function() {
suiteBuilder.it('a nested spec');
});
suiteBuilder.it('a spec');
suiteBuilder.parallelReset();
expect(suiteBuilder.topSuite.children).toEqual([]);
});
it('preserves top suite befores and afters', function() {
const env = { configuration: () => ({}) };
const suiteBuilder = new jasmineUnderTest.SuiteBuilder({ env });
function beforeAll() {}
function beforeEach() {}
function afterEach() {}
function afterAll() {}
suiteBuilder.beforeAll(beforeAll);
suiteBuilder.beforeEach(beforeEach);
suiteBuilder.afterEach(afterEach);
suiteBuilder.afterAll(afterAll);
suiteBuilder.parallelReset();
expect(suiteBuilder.topSuite.beforeAllFns).toEqual([
jasmine.objectContaining({ fn: beforeAll })
]);
expect(suiteBuilder.topSuite.beforeFns).toEqual([
jasmine.objectContaining({ fn: beforeEach })
]);
expect(suiteBuilder.topSuite.afterFns).toEqual([
jasmine.objectContaining({ fn: afterEach })
]);
expect(suiteBuilder.topSuite.afterAllFns).toEqual([
jasmine.objectContaining({ fn: afterAll })
]);
});
it('resets totalSpecsDefined', function() {
const env = { configuration: () => ({}) };
const suiteBuilder = new jasmineUnderTest.SuiteBuilder({ env });
suiteBuilder.it('a spec');
suiteBuilder.parallelReset();
expect(suiteBuilder.totalSpecsDefined).toEqual(0);
});
it('resets focusedRunables', function() {
const env = { configuration: () => ({}) };
const suiteBuilder = new jasmineUnderTest.SuiteBuilder({ env });
suiteBuilder.fit('a spec', function() {});
suiteBuilder.parallelReset();
expect(suiteBuilder.focusedRunables).toEqual([]);
});
});
});

View File

@@ -145,7 +145,7 @@ describe('base helpers', function() {
});
it('returns a promise that resolves to false when the promise is rejected', function() {
const promise = Promise.reject(new Error('nope'));
const promise = Promise.reject();
return expectAsync(jasmineUnderTest.isPending_(promise)).toBeResolvedTo(
false
);
@@ -179,10 +179,7 @@ describe('base helpers', function() {
f2 = jasmine.createSpy('setTimeout callback for ' + (max + 1));
// Suppress printing of TimeoutOverflowWarning in node
if (typeof process !== 'undefined' && process.emitWarning) {
spyOn(process, 'emitWarning'); // Node 22
}
spyOn(console, 'error'); // Node <22
spyOn(console, 'error');
let id = setTimeout(f1, max);
setTimeout(function() {

View File

@@ -1,9 +1,8 @@
describe('Env integration', function() {
let env;
const isBrowser = typeof window !== 'undefined';
beforeEach(function() {
specHelpers.registerIntegrationMatchers();
jasmine.getEnv().registerIntegrationMatchers();
env = new jasmineUnderTest.Env();
});
@@ -456,7 +455,7 @@ describe('Env integration', function() {
env.describe('A suite', function() {
env.it('fails', function(specDone) {
setTimeout(function() {
dispatchErrorEvent(global, { error: 'fail' });
global.onerror('fail');
specDone();
});
});
@@ -510,14 +509,10 @@ describe('Env integration', function() {
},
specDone: function() {
clearStackCallbacks[clearStackCallCount + 1] = function() {
dispatchErrorEvent(global, {
error: 'fail at the end of the reporter queue'
});
global.onerror('fail at the end of the reporter queue');
};
clearStackCallbacks[clearStackCallCount + 2] = function() {
dispatchErrorEvent(global, {
error: 'fail at the end of the spec queue'
});
global.onerror('fail at the end of the spec queue');
};
}
});
@@ -564,7 +559,7 @@ describe('Env integration', function() {
specDone();
queueMicrotask(function() {
queueMicrotask(function() {
dispatchErrorEvent(global, { error: 'fail' });
global.onerror('fail');
});
});
});
@@ -627,14 +622,10 @@ describe('Env integration', function() {
if (result.description === 'A nested suite') {
clearStackCallbacks[clearStackCallCount + 1] = function() {
dispatchErrorEvent(global, {
error: 'fail at the end of the reporter queue'
});
global.onerror('fail at the end of the reporter queue');
};
clearStackCallbacks[clearStackCallCount + 2] = function() {
dispatchErrorEvent(global, {
error: 'fail at the end of the suite queue'
});
global.onerror('fail at the end of the suite queue');
};
}
}
@@ -677,7 +668,7 @@ describe('Env integration', function() {
env.addReporter({
jasmineDone: function() {
dispatchErrorEvent(global, { error: 'a very late error' });
global.onerror('a very late error');
}
});
@@ -729,7 +720,7 @@ describe('Env integration', function() {
expectedErrors.push(`${msg} thrown`);
}
dispatchErrorEvent(global, { error: msg });
global.onerror(msg);
realClearStack(fn);
});
spyOn(console, 'error');
@@ -1315,9 +1306,8 @@ describe('Env integration', function() {
'works with constructors when using callThrough spy strategy',
function() {
function MyClass(foo) {
if (!(this instanceof MyClass)) {
if (!(this instanceof MyClass))
throw new Error('You must use the new keyword.');
}
this.foo = foo;
}
const subject = { MyClass: MyClass };
@@ -1889,8 +1879,7 @@ describe('Env integration', function() {
expect(reporter.jasmineStarted).toHaveBeenCalledWith({
totalSpecsDefined: 1,
order: jasmine.any(jasmineUnderTest.Order),
parallel: false
order: jasmine.any(jasmineUnderTest.Order)
});
expect(reporter.specDone).toHaveBeenCalledWith(
@@ -1924,8 +1913,7 @@ describe('Env integration', function() {
expect(reporter.jasmineStarted).toHaveBeenCalledWith({
totalSpecsDefined: 1,
order: jasmine.any(jasmineUnderTest.Order),
parallel: false
order: jasmine.any(jasmineUnderTest.Order)
});
expect(reporter.specDone).toHaveBeenCalledWith(
@@ -1979,8 +1967,7 @@ describe('Env integration', function() {
expect(reporter.jasmineStarted).toHaveBeenCalledWith({
totalSpecsDefined: 6,
order: jasmine.any(jasmineUnderTest.Order),
parallel: false
order: jasmine.any(jasmineUnderTest.Order)
});
expect(reporter.specStarted.calls.count()).toBe(6);
@@ -2317,8 +2304,7 @@ describe('Env integration', function() {
expect(reporter.jasmineStarted).toHaveBeenCalledWith({
totalSpecsDefined: 1,
order: jasmine.any(jasmineUnderTest.Order),
parallel: false
order: jasmine.any(jasmineUnderTest.Order)
});
expect(reporter.specDone).toHaveBeenCalledWith(
@@ -2722,24 +2708,15 @@ describe('Env integration', function() {
);
});
await jasmine.spyOnGlobalErrorsAsync(async function(globalErrorSpy) {
await env.execute();
if (isBrowser) {
// Verify that there were no unexpected errors
expect(globalErrorSpy).toHaveBeenCalledTimes(2);
expect(globalErrorSpy).toHaveBeenCalledWith(new Error('suite'));
expect(globalErrorSpy).toHaveBeenCalledWith(new Error('spec'));
}
});
await env.execute();
expect(reporter.suiteDone).toHaveFailedExpectationsForRunnable(
'async suite',
[/Error: suite/]
[/^(((Uncaught )?(exception: )?Error: suite( thrown)?)|(suite thrown))$/]
);
expect(reporter.specDone).toHaveFailedExpectationsForRunnable(
'suite async spec',
[/Error: spec/]
[/^(((Uncaught )?(exception: )?Error: spec( thrown)?)|(spec thrown))$/]
);
});
@@ -2873,14 +2850,14 @@ describe('Env integration', function() {
]);
env.addReporter(reporter);
dispatchErrorEvent(global, {
message: 'Uncaught SyntaxError: Unexpected end of input',
error: undefined,
filename: 'borkenSpec.js',
lineno: 42
});
const error = new Error('ENOCHEESE');
dispatchErrorEvent(global, { error });
global.onerror(
'Uncaught SyntaxError: Unexpected end of input',
'borkenSpec.js',
42,
undefined,
{ stack: 'a stack' }
);
global.onerror('Uncaught Error: ENOCHEESE');
await env.execute();
@@ -2890,15 +2867,15 @@ describe('Env integration', function() {
passed: false,
globalErrorType: 'load',
message: 'Uncaught SyntaxError: Unexpected end of input',
stack: undefined,
stack: 'a stack',
filename: 'borkenSpec.js',
lineno: 42
},
{
passed: false,
globalErrorType: 'load',
message: 'ENOCHEESE',
stack: error.stack,
message: 'Uncaught Error: ENOCHEESE',
stack: undefined,
filename: undefined,
lineno: undefined
}
@@ -3130,7 +3107,7 @@ describe('Env integration', function() {
env.addReporter(reporter);
env.it('passes', function() {});
dispatchErrorEvent(global, { error: 'ENOCHEESE' });
global.onerror('Uncaught Error: ENOCHEESE');
await env.execute();
expect(reporter.jasmineDone).toHaveBeenCalled();
@@ -3151,7 +3128,6 @@ describe('Env integration', function() {
const e = reporter.jasmineDone.calls.argsFor(0)[0];
expect(e.overallStatus).toEqual('incomplete');
expect(e.incompleteReason).toEqual('No specs found');
expect(e.incompleteCode).toEqual('noSpecsFound');
});
});
@@ -3170,7 +3146,6 @@ describe('Env integration', function() {
const e = reporter.jasmineDone.calls.argsFor(0)[0];
expect(e.overallStatus).toEqual('incomplete');
expect(e.incompleteReason).toEqual('fit() or fdescribe() was found');
expect(e.incompleteCode).toEqual('focused');
});
});
@@ -3191,7 +3166,6 @@ describe('Env integration', function() {
const e = reporter.jasmineDone.calls.argsFor(0)[0];
expect(e.overallStatus).toEqual('incomplete');
expect(e.incompleteReason).toEqual('fit() or fdescribe() was found');
expect(e.incompleteCode).toEqual('focused');
});
});
@@ -3212,7 +3186,6 @@ describe('Env integration', function() {
const e = reporter.jasmineDone.calls.argsFor(0)[0];
expect(e.overallStatus).toEqual('failed');
expect(e.incompleteReason).toBeUndefined();
expect(e.incompleteCode).toBeUndefined();
});
});
});
@@ -3863,6 +3836,16 @@ describe('Env integration', function() {
expect(failedExpectations).toEqual([]);
});
it('calls the optional done callback when finished', function(done) {
const reporter = jasmine.createSpyObj('reporter', ['jasmineDone']);
env.addReporter(reporter);
env.execute(null, function() {
expect(reporter.jasmineDone).toHaveBeenCalled();
done();
});
});
describe('#spyOnGlobalErrorsAsync', function() {
const leftInstalledMessage =
'Global error spy was not uninstalled. ' +
@@ -3903,16 +3886,7 @@ describe('Env integration', function() {
const reporter = jasmine.createSpyObj('reporter', ['specDone']);
env.addReporter(reporter);
await jasmine.spyOnGlobalErrorsAsync(async function(globalErrorSpy) {
await env.execute();
if (isBrowser) {
// Verify that there were no unexpected errors
expect(globalErrorSpy).toHaveBeenCalledTimes(2);
expect(globalErrorSpy).toHaveBeenCalledWith(new Error('nope'));
expect(globalErrorSpy).toHaveBeenCalledWith(new Error('yep'));
}
});
await env.execute();
const passingResult = resultForRunable(
reporter.specDone,
@@ -3959,17 +3933,7 @@ describe('Env integration', function() {
'suiteDone'
]);
env.addReporter(reporter);
await jasmine.spyOnGlobalErrorsAsync(async function(globalErrorSpy) {
await env.execute();
if (isBrowser) {
// Verify that there were no unexpected errors
expect(globalErrorSpy).toHaveBeenCalledTimes(1);
expect(globalErrorSpy).toHaveBeenCalledWith(
new Error('should fail the spec')
);
}
});
await env.execute();
const suiteResult = resultForRunable(reporter.suiteDone, 'Suite 1');
expect(suiteResult.status).toEqual('failed');
@@ -4014,17 +3978,7 @@ describe('Env integration', function() {
'suiteDone'
]);
env.addReporter(reporter);
await jasmine.spyOnGlobalErrorsAsync(async function(globalErrorSpy) {
await env.execute();
if (isBrowser) {
// Verify that there were no unexpected errors
expect(globalErrorSpy).toHaveBeenCalledTimes(1);
expect(globalErrorSpy).toHaveBeenCalledWith(
new Error('should fail the spec')
);
}
});
await env.execute();
expect(reporter.suiteDone).toHaveFailedExpectationsForRunnable(
'Suite 1',
@@ -4074,18 +4028,7 @@ describe('Env integration', function() {
'suiteDone'
]);
env.addReporter(reporter);
await jasmine.spyOnGlobalErrorsAsync(async function(globalErrorSpy) {
await env.execute();
if (isBrowser) {
// Verify that there were no unexpected errors
expect(globalErrorSpy).toHaveBeenCalledTimes(1);
expect(globalErrorSpy).toHaveBeenCalledWith(
new Error('should fail the spec')
);
}
});
await env.execute();
const spec1Result = resultForRunable(reporter.specDone, 'Suite 1 a spec');
expect(spec1Result.status).toEqual('failed');
@@ -4121,17 +4064,7 @@ describe('Env integration', function() {
const reporter = jasmine.createSpyObj('reporter', ['specDone']);
env.addReporter(reporter);
await jasmine.spyOnGlobalErrorsAsync(async function(globalErrorSpy) {
await env.execute();
if (isBrowser) {
// Verify that there were no unexpected errors
expect(globalErrorSpy).toHaveBeenCalledTimes(1);
expect(globalErrorSpy).toHaveBeenCalledWith(
new Error('should fail the spec')
);
}
});
await env.execute();
const spec1Result = resultForRunable(reporter.specDone, 'spec 1');
expect(spec1Result.status).toEqual('failed');
@@ -4176,17 +4109,7 @@ describe('Env integration', function() {
'suiteDone'
]);
env.addReporter(reporter);
await jasmine.spyOnGlobalErrorsAsync(async function(globalErrorSpy) {
await env.execute();
if (isBrowser) {
// Verify that there were no unexpected errors
expect(globalErrorSpy).toHaveBeenCalledTimes(1);
expect(globalErrorSpy).toHaveBeenCalledWith(
new Error('should fail the spec')
);
}
});
await env.execute();
const spec1Result = resultForRunable(reporter.specDone, 'Suite 1 a spec');
expect(spec1Result.status).toEqual('failed');
@@ -4335,136 +4258,10 @@ describe('Env integration', function() {
}
});
describe('throwUnless', function() {
it('throws when the matcher fails', async function() {
let thrown;
env.it('a spec', function() {
try {
env.throwUnless(1).toEqual(2);
} catch (e) {
thrown = e;
}
});
await env.execute();
expect(thrown).toBeInstanceOf(Error);
expect(thrown.passed).toEqual(false);
expect(thrown.matcherName).toEqual('toEqual');
expect(thrown.message).toEqual('Expected 1 to equal 2.');
expect(thrown.actual).toEqual(1);
expect(thrown.expected).toEqual(2);
});
it('does not throw when the matcher passes', async function() {
let threw = false;
env.it('a spec', function() {
try {
env.throwUnless(1).toEqual(1);
} catch (e) {
threw = true;
}
});
await env.execute();
expect(threw).toBe(false);
});
it('does not cause a failure if the error does not propagate back to jasmine', async function() {
env.it('a spec', function() {
try {
env.throwUnless(1).toEqual(2);
} catch (e) {}
});
const reporter = jasmine.createSpyObj('reporter', ['specDone']);
env.addReporter(reporter);
await env.execute();
expect(reporter.specDone).toHaveBeenCalledWith(
jasmine.objectContaining({ status: 'passed' })
);
});
});
describe('throwUnlessAsync', function() {
it('throws when the matcher fails', async function() {
const promise = Promise.resolve('a');
let thrown;
env.it('a spec', async function() {
try {
await env.throwUnlessAsync(promise).toBeResolvedTo('b');
} catch (e) {
thrown = e;
}
});
await env.execute();
expect(thrown).toBeInstanceOf(Error);
expect(thrown.passed).toEqual(false);
expect(thrown.matcherName).toEqual('toBeResolvedTo');
expect(thrown.message).toEqual(
"Expected a promise to be resolved to 'b' but it was resolved to 'a'."
);
expect(thrown.actual).toBe(promise);
expect(thrown.expected).toEqual('b');
});
it('does not throw when the matcher passes', async function() {
let threw = false;
env.it('a spec', async function() {
try {
await env.throwUnlessAsync(Promise.resolve()).toBeResolved();
} catch (e) {
threw = true;
}
});
await env.execute();
expect(threw).toBe(false);
});
it('does not cause a failure if the error does not propagate back to jasmine', async function() {
env.it('a spec', async function() {
try {
await env.throwUnlessAsync(Promise.resolve()).toBeRejected();
} catch (e) {}
});
const reporter = jasmine.createSpyObj('reporter', ['specDone']);
env.addReporter(reporter);
await env.execute();
expect(reporter.specDone).toHaveBeenCalledWith(
jasmine.objectContaining({ status: 'passed' })
);
});
});
function browserEventMethods() {
return {
listeners_: { error: [], unhandledrejection: [] },
addEventListener(eventName, listener) {
this.listeners_[eventName].push(listener);
},
removeEventListener(eventName, listener) {
this.listeners_[eventName] = this.listeners_[eventName].filter(
l => l !== listener
);
}
addEventListener() {},
removeEventListener() {}
};
}
function dispatchErrorEvent(global, event) {
expect(global.listeners_.error.length)
.withContext('number of error listeners')
.toBeGreaterThan(0);
for (const l of global.listeners_.error) {
l(event);
}
}
});

View File

@@ -349,7 +349,7 @@ describe('Matchers (Integration)', function() {
});
verifyFailsAsync(function(env) {
return env.expectAsync(Promise.reject(new Error('nope'))).toBeResolved();
return env.expectAsync(Promise.reject()).toBeResolved();
});
});
@@ -610,13 +610,19 @@ describe('Matchers (Integration)', function() {
});
describe('toHaveClass', function() {
beforeEach(function() {
this.domHelpers = jasmine.getEnv().domHelpers();
});
verifyPasses(function(env) {
const el = specHelpers.domHelpers.createElementWithClassName('foo');
const domHelpers = jasmine.getEnv().domHelpers();
const el = domHelpers.createElementWithClassName('foo');
env.expect(el).toHaveClass('foo');
});
verifyFails(function(env) {
const el = specHelpers.domHelpers.createElementWithClassName('foo');
const domHelpers = jasmine.getEnv().domHelpers();
const el = domHelpers.createElementWithClassName('foo');
env.expect(el).toHaveClass('bar');
});
});

View File

@@ -1,162 +0,0 @@
describe('Support for parallel execution', function() {
let env;
beforeEach(function() {
env = new jasmineUnderTest.Env();
});
afterEach(function() {
env.cleanup_();
});
it('removes specs and suites from previous batches', async function() {
env.describe('a suite', function() {
env.it('a spec', function() {});
});
env.it('a spec', function() {});
await env.execute();
env.parallelReset();
env.describe('a suite in a new batch', function() {
env.it('a spec in a new batch', function() {});
});
const reporter = jasmine.createSpyObj('reporter', [
'suiteDone',
'specDone'
]);
env.addReporter(reporter);
await env.execute();
expect(reporter.suiteDone).toHaveBeenCalledOnceWith(
jasmine.objectContaining({
fullName: 'a suite in a new batch'
})
);
expect(reporter.specDone).toHaveBeenCalledOnceWith(
jasmine.objectContaining({
fullName: 'a suite in a new batch a spec in a new batch'
})
);
});
it('preserves top-level before and after fns from previous batches', async function() {
const beforeAll = jasmine.createSpy('beforeAll');
const beforeEach = jasmine.createSpy('beforeEach');
const afterEach = jasmine.createSpy('afterEach');
const afterAll = jasmine.createSpy('afterAll');
env.beforeAll(beforeAll);
env.beforeEach(beforeEach);
env.afterEach(afterEach);
env.afterAll(afterAll);
env.parallelReset();
env.it('a spec', function() {});
await env.execute();
expect(beforeAll).toHaveBeenCalled();
expect(beforeEach).toHaveBeenCalled();
expect(afterEach).toHaveBeenCalled();
expect(afterAll).toHaveBeenCalled();
});
it('does not remember focused runables from previous batches', async function() {
env.fit('a focused spec', function() {});
env.parallelReset();
env.it('a spec', function() {});
const reporter = jasmine.createSpyObj('reporter', [
'specDone',
'jasmineDone'
]);
env.addReporter(reporter);
await env.execute();
expect(reporter.specDone).toHaveBeenCalledOnceWith(
jasmine.objectContaining({
fullName: 'a spec',
status: 'passed'
})
);
expect(reporter.jasmineDone).toHaveBeenCalledWith(
jasmine.objectContaining({ overallStatus: 'passed' })
);
});
it('does not remember failures from previous batches', async function() {
env.it('a failing spec', function() {
env.expect(true).toBe(false);
});
await env.execute();
env.parallelReset();
env.it('a spec', function() {});
const reporter = jasmine.createSpyObj('reporter', [
'specDone',
'jasmineDone'
]);
env.addReporter(reporter);
await env.execute();
expect(reporter.jasmineDone).toHaveBeenCalledWith(
jasmine.objectContaining({ overallStatus: 'passed' })
);
});
it('reports errors thrown from describe', async function() {
const reporter = jasmine.createSpyObj('reporter', ['suiteDone']);
env.addReporter(reporter);
env.describe('borken', function() {
throw new Error('nope');
});
await env.execute();
expect(reporter.suiteDone).toHaveBeenCalledWith(
jasmine.objectContaining({
description: 'borken',
status: 'failed',
failedExpectations: [
jasmine.objectContaining({
message: jasmine.stringContaining('Error: nope')
})
]
})
);
// Errors in subsequent suites should also be reported
reporter.suiteDone.calls.reset();
env.parallelReset();
env.describe('zarro boogs', function() {
throw new Error('nor that either');
});
await env.execute();
expect(reporter.suiteDone).toHaveBeenCalledOnceWith(
jasmine.objectContaining({
description: 'zarro boogs',
status: 'failed',
failedExpectations: [
jasmine.objectContaining({
message: jasmine.stringContaining('Error: nor that either')
})
]
})
);
// Failure state should not persist across resets
reporter.suiteDone.calls.reset();
env.parallelReset();
env.describe('actually works', function() {
env.it('a spec', function() {});
});
await env.execute();
expect(reporter.suiteDone).toHaveBeenCalledOnceWith(
jasmine.objectContaining({
description: 'actually works',
status: 'passed',
failedExpectations: []
})
);
});
});

View File

@@ -2,7 +2,7 @@ describe('spec running', function() {
let env;
beforeEach(function() {
specHelpers.registerIntegrationMatchers();
jasmine.getEnv().registerIntegrationMatchers();
env = new jasmineUnderTest.Env();
env.configure({ random: false });
});
@@ -626,7 +626,7 @@ describe('spec running', function() {
expect(actions).toEqual(['spec2', 'spec3', 'spec1']);
});
it('refuses to re-enter suites with a beforeAll', async function() {
it('refuses to re-enter suites with a beforeAll', function() {
const actions = [];
let spec1;
let spec2;
@@ -648,12 +648,13 @@ describe('spec running', function() {
actions.push('spec3');
});
const promise = env.execute([spec2.id, spec3.id, spec1.id]);
await expectAsync(promise).toBeRejectedWithError(/beforeAll/);
expect(function() {
env.execute([spec2.id, spec3.id, spec1.id]);
}).toThrowError(/beforeAll/);
expect(actions).toEqual([]);
});
it('refuses to re-enter suites with a afterAll', async function() {
it('refuses to re-enter suites with a afterAll', function() {
const actions = [];
let spec1;
let spec2;
@@ -675,8 +676,9 @@ describe('spec running', function() {
actions.push('spec3');
});
const promise = env.execute([spec2.id, spec3.id, spec1.id]);
await expectAsync(promise).toBeRejectedWithError(/afterAll/);
expect(function() {
env.execute([spec2.id, spec3.id, spec1.id]);
}).toThrowError(/afterAll/);
expect(actions).toEqual([]);
});

View File

@@ -1,8 +1,12 @@
describe('toHaveClass', function() {
beforeEach(function() {
this.domHelpers = jasmine.getEnv().domHelpers();
});
it('fails for a DOM element that lacks the expected class', function() {
const matcher = jasmineUnderTest.matchers.toHaveClass(),
result = matcher.compare(
specHelpers.domHelpers.createElementWithClassName(''),
this.domHelpers.createElementWithClassName(''),
'foo'
);
@@ -11,7 +15,7 @@ describe('toHaveClass', function() {
it('passes for a DOM element that has the expected class', function() {
const matcher = jasmineUnderTest.matchers.toHaveClass(),
el = specHelpers.domHelpers.createElementWithClassName('foo bar baz');
el = this.domHelpers.createElementWithClassName('foo bar baz');
expect(matcher.compare(el, 'foo').pass).toBe(true);
expect(matcher.compare(el, 'bar').pass).toBe(true);
@@ -20,7 +24,7 @@ describe('toHaveClass', function() {
it('fails for a DOM element that only has other classes', function() {
const matcher = jasmineUnderTest.matchers.toHaveClass(),
el = specHelpers.domHelpers.createElementWithClassName('foo bar');
el = this.domHelpers.createElementWithClassName('foo bar');
expect(matcher.compare(el, 'fo').pass).toBe(false);
});
@@ -38,7 +42,7 @@ describe('toHaveClass', function() {
matcher.compare(undefined, 'foo');
}).toThrowError('undefined is not a DOM element');
const textNode = specHelpers.domHelpers.document.createTextNode('');
const textNode = this.domHelpers.document.createTextNode('');
expect(function() {
matcher.compare(textNode, 'foo');
}).toThrowError('HTMLNode is not a DOM element');

View File

@@ -15,17 +15,6 @@ describe('toHaveSize', function() {
expect(result.pass).toBe(false);
});
it('informs about the size of an array whose length does not match', function() {
const matcher = jasmineUnderTest.matchers.toHaveSize({
pp: jasmineUnderTest.makePrettyPrinter()
}),
result = matcher.compare([1, 2, 3], 2);
expect(result.message()).toEqual(
'Expected [ 1, 2, 3 ] with size 3 to have size 2.'
);
});
it('passes for an object with the proper number of keys', function() {
const matcher = jasmineUnderTest.matchers.toHaveSize(),
result = matcher.compare({ a: 1, b: 2 }, 2);

View File

@@ -1,4 +1,4 @@
(function() {
(function(env) {
function browserVersion(matchFn) {
const userAgent = jasmine.getGlobal().navigator.userAgent;
if (!userAgent) {
@@ -10,7 +10,7 @@
return match ? parseFloat(match[1]) : void 0;
}
specHelpers.firefoxVersion = browserVersion(function(userAgent) {
env.firefoxVersion = browserVersion(function(userAgent) {
return /Firefox\/([0-9]{0,})/.exec(userAgent);
});
})();
})(jasmine.getEnv());

View File

@@ -1,20 +1,24 @@
(function() {
let doc;
(function(env) {
function domHelpers() {
let doc;
if (typeof document !== 'undefined') {
doc = document;
} else {
const JSDOM = require('jsdom').JSDOM;
const dom = new JSDOM();
doc = dom.window.document;
if (typeof document !== 'undefined') {
doc = document;
} else {
const JSDOM = require('jsdom').JSDOM;
const dom = new JSDOM();
doc = dom.window.document;
}
return {
document: doc,
createElementWithClassName: function(className) {
const el = this.document.createElement('div');
el.className = className;
return el;
}
};
}
specHelpers.domHelpers = {
document: doc,
createElementWithClassName(className) {
const el = this.document.createElement('div');
el.className = className;
return el;
}
};
})();
env.domHelpers = domHelpers;
})(jasmine.getEnv());

View File

@@ -1 +0,0 @@
globalThis.specHelpers = {};

View File

@@ -1,5 +1,5 @@
(function() {
specHelpers.registerIntegrationMatchers = function() {
(function(env) {
env.registerIntegrationMatchers = function() {
jasmine.addMatchers({
toHaveFailedExpectationsForRunnable: function() {
return {
@@ -51,4 +51,4 @@
}
});
};
})();
})(jasmine.getEnv());

View File

@@ -12,12 +12,14 @@
};
function getSourceFiles() {
const globs = ['../../src/core/**/*.js', '../../src/version.js'];
const srcFiles = globs.flatMap(g => glob.sync(g, { cwd: __dirname }));
const src_files = ['core/**/*.js', 'version.js'].map(function(file) {
return path.join(__dirname, '../../', 'src/', file);
});
for (const file of srcFiles) {
require(file);
}
const files = src_files.flatMap(g => glob.sync(g));
files.forEach(function(resolvedFile) {
require(resolvedFile);
});
}
getSourceFiles();

View File

@@ -22,7 +22,7 @@ describe('PrettyPrinter (HTML Dependent)', function() {
});
it("should print Firefox's wrapped native objects correctly", function() {
if (specHelpers.firefoxVersion) {
if (jasmine.getEnv().firefoxVersion) {
const pp = jasmineUnderTest.makePrettyPrinter();
let err;
try {

View File

@@ -78,10 +78,15 @@ describe('npm package', function() {
it('has bootFiles', function() {
expect(this.packagedCore.files.bootFiles).toEqual(['boot0.js', 'boot1.js']);
expect(this.packagedCore.files.nodeBootFiles).toEqual(['node_boot.js']);
for (const fileName of this.packagedCore.files.bootFiles) {
expect(fileName).toExistInPath(this.packagedCore.files.bootDir);
}
for (const fileName of this.packagedCore.files.nodeBootFiles) {
expect(fileName).toExistInPath(this.packagedCore.files.bootDir);
}
});
it('has an imagesDir', function() {
@@ -113,7 +118,7 @@ describe('npm package', function() {
const files = fs.readdirSync(path.resolve(this.tmpDir, 'package'));
files.sort();
expect(files).toEqual([
'LICENSE',
'MIT.LICENSE',
'README.md',
'images',
'lib',

View File

@@ -18,7 +18,6 @@ module.exports = {
specDir: 'spec',
specFiles: ['**/*[Ss]pec.js', '!npmPackage/**/*'],
helpers: [
'helpers/init.js',
'helpers/generator.js',
'helpers/BrowserFlags.js',
'helpers/domHelpers.js',
@@ -29,19 +28,16 @@ module.exports = {
random: true,
browser: {
name: process.env.JASMINE_BROWSER || 'firefox',
useRemoteSeleniumGrid: process.env.USE_SAUCE === 'true',
remoteSeleniumGrid: {
url: 'https://ondemand.saucelabs.com/wd/hub',
useSauce: process.env.USE_SAUCE === 'true',
sauce: {
name: `jasmine-core ${new Date().toISOString()}`,
os: process.env.SAUCE_OS,
browserVersion: process.env.SAUCE_BROWSER_VERSION,
platformName: process.env.SAUCE_OS,
'sauce:options': {
name: `jasmine-core ${new Date().toISOString()}`,
build: `Core ${process.env.CIRCLE_BUILD_NUM || 'Ran locally'}`,
tags: ['Jasmine-Core'],
tunnelIdentifier: process.env.SAUCE_TUNNEL_IDENTIFIER,
username: process.env.SAUCE_USERNAME,
accessKey: process.env.SAUCE_ACCESS_KEY
}
build: `Core ${process.env.TRAVIS_BUILD_NUMBER || 'Ran locally'}`,
tags: ['Jasmine-Core'],
tunnelIdentifier: process.env.SAUCE_TUNNEL_IDENTIFIER,
username: process.env.SAUCE_USERNAME,
accessKey: process.env.SAUCE_ACCESS_KEY
}
}
};

View File

@@ -5,7 +5,6 @@
"npmPackage/**/*[Ss]pec.js"
],
"helpers": [
"helpers/init.js",
"helpers/domHelpers.js",
"helpers/integrationMatchers.js",
"helpers/overrideConsoleLogForCircleCi.js",

16
src/boot/node_boot.js Normal file
View File

@@ -0,0 +1,16 @@
module.exports = function(jasmineRequire) {
const jasmine = jasmineRequire.core(jasmineRequire);
const env = jasmine.getEnv({ suppressLoadErrors: true });
const jasmineInterface = jasmineRequire.interface(jasmine, env);
extend(global, jasmineInterface);
function extend(destination, source) {
for (const property in source) destination[property] = source[property];
return destination;
}
return jasmine;
};

View File

@@ -2,8 +2,7 @@ getJasmineRequireObj().clearStack = function(j$) {
const maxInlineCallCount = 10;
function browserQueueMicrotaskImpl(global) {
const unclampedSetTimeout = getUnclampedSetTimeout(global);
const { queueMicrotask } = global;
const { setTimeout, queueMicrotask } = global;
let currentCallCount = 0;
return function clearStack(fn) {
currentCallCount++;
@@ -12,7 +11,7 @@ getJasmineRequireObj().clearStack = function(j$) {
queueMicrotask(fn);
} else {
currentCallCount = 0;
unclampedSetTimeout(fn);
setTimeout(fn);
}
};
}
@@ -26,37 +25,6 @@ getJasmineRequireObj().clearStack = function(j$) {
}
function messageChannelImpl(global) {
const { setTimeout } = global;
const postMessage = getPostMessage(global);
let currentCallCount = 0;
return function clearStack(fn) {
currentCallCount++;
if (currentCallCount < maxInlineCallCount) {
postMessage(fn);
} else {
currentCallCount = 0;
setTimeout(fn);
}
};
}
function getUnclampedSetTimeout(global) {
const { setTimeout } = global;
if (j$.util.isUndefined(global.MessageChannel)) {
return setTimeout;
}
const postMessage = getPostMessage(global);
return function unclampedSetTimeout(fn) {
postMessage(function() {
setTimeout(fn);
});
};
}
function getPostMessage(global) {
const { MessageChannel, setTimeout } = global;
const channel = new MessageChannel();
let head = {};
@@ -80,9 +48,17 @@ getJasmineRequireObj().clearStack = function(j$) {
}
};
return function postMessage(fn) {
tail = tail.next = { task: fn };
channel.port2.postMessage(0);
let currentCallCount = 0;
return function clearStack(fn) {
currentCallCount++;
if (currentCallCount < maxInlineCallCount) {
tail = tail.next = { task: fn };
channel.port2.postMessage(0);
} else {
currentCallCount = 0;
setTimeout(fn);
}
};
}
@@ -92,25 +68,20 @@ getJasmineRequireObj().clearStack = function(j$) {
global.process.versions &&
typeof global.process.versions.node === 'string';
// Windows builds of WebKit have a fairly generic user agent string when no application name is provided:
// e.g. "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/605.1.15 (KHTML, like Gecko)"
const SAFARI_OR_WIN_WEBKIT =
const SAFARI =
global.navigator &&
/(^((?!chrome|android).)*safari)|(Win64; x64\) AppleWebKit\/[0-9.]+ \(KHTML, like Gecko\)$)/i.test(
global.navigator.userAgent
);
/^((?!chrome|android).)*safari/i.test(global.navigator.userAgent);
if (NODE_JS) {
// Unlike browsers, Node doesn't require us to do a periodic setTimeout
// so we avoid the overhead.
return nodeQueueMicrotaskImpl(global);
} else if (
SAFARI_OR_WIN_WEBKIT ||
SAFARI ||
j$.util.isUndefined(global.MessageChannel) /* tests */
) {
// queueMicrotask is dramatically faster than MessageChannel in Safari
// and other WebKit-based browsers, such as the one distributed by Playwright
// to test Safari-like behavior on Windows.
// queueMicrotask is dramatically faster than MessageChannel in Safari,
// at least through version 16.
// Some of our own integration tests provide a mock queueMicrotask in all
// environments because it's simpler to mock than MessageChannel.
return browserQueueMicrotaskImpl(global);

View File

@@ -1,6 +1,3 @@
// Warning: don't add "use strict" to this file. Doing so potentially changes
// the behavior of user code that does things like setTimeout("var x = 1;")
// while the mock clock is installed.
getJasmineRequireObj().DelayedFunctionScheduler = function(j$) {
function DelayedFunctionScheduler() {
this.scheduledLookup_ = [];
@@ -26,9 +23,6 @@ getJasmineRequireObj().DelayedFunctionScheduler = function(j$) {
) {
let f;
if (typeof funcToCall === 'string') {
// setTimeout("some code") and setInterval("some code") are legal, if
// not recommended. We don't do that ourselves, but user code might.
// This allows such code to work when the mock clock is installed.
f = function() {
// eslint-disable-next-line no-eval
return eval(funcToCall);

View File

@@ -46,7 +46,6 @@ getJasmineRequireObj().Env = function(j$) {
let reporter;
let topSuite;
let runner;
let parallelLoadingState = null; // 'specs', 'helpers', or null for non-parallel
/**
* This represents the available options to configure Jasmine.
@@ -75,10 +74,6 @@ getJasmineRequireObj().Env = function(j$) {
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
@@ -154,14 +149,20 @@ getJasmineRequireObj().Env = function(j$) {
if (!options.suppressLoadErrors) {
installGlobalErrors();
globalErrors.pushListener(function loadtimeErrorHandler(error, event) {
globalErrors.pushListener(function loadtimeErrorHandler(
message,
filename,
lineno,
colNo,
err
) {
topSuite.result.failedExpectations.push({
passed: false,
globalErrorType: 'load',
message: error ? error.message : event.message,
stack: error && error.stack,
filename: event && event.filename,
lineno: event && event.lineno
message: message,
stack: err && err.stack,
filename: filename,
lineno: lineno
});
});
}
@@ -174,12 +175,6 @@ getJasmineRequireObj().Env = function(j$) {
* @function
*/
this.configure = function(configuration) {
if (parallelLoadingState) {
throw new Error(
'Jasmine cannot be configured via Env in parallel mode'
);
}
const booleanProps = [
'random',
'failSpecWithNoExpectations',
@@ -264,49 +259,6 @@ getJasmineRequireObj().Env = function(j$) {
}
};
const handleThrowUnlessFailure = function(passed, result) {
if (!passed) {
/**
* @interface
* @name ThrowUnlessFailure
* @extends Error
* @description Represents a failure of an expectation evaluated with
* {@link throwUnless}. Properties of this error are a subset of the
* properties of {@link Expectation} and have the same values.
* @property {String} matcherName - The name of the matcher that was executed for this expectation.
* @property {String} message - The failure message for the expectation.
* @property {Boolean} passed - Whether the expectation passed or failed.
* @property {Object} expected - If the expectation failed, what was the expected value.
* @property {Object} actual - If the expectation failed, what actual value was produced.
*/
const error = new Error(result.message);
error.passed = result.passed;
error.message = result.message;
error.expected = result.expected;
error.actual = result.actual;
error.matcherName = result.matcherName;
throw error;
}
};
const throwUnlessFactory = function(actual, spec) {
return j$.Expectation.factory({
matchersUtil: runableResources.makeMatchersUtil(),
customMatchers: runableResources.customMatchers(),
actual: actual,
addExpectationResult: handleThrowUnlessFailure
});
};
const throwUnlessAsyncFactory = function(actual, spec) {
return j$.Expectation.asyncFactory({
matchersUtil: runableResources.makeMatchersUtil(),
customAsyncMatchers: runableResources.customAsyncMatchers(),
actual: actual,
addExpectationResult: handleThrowUnlessFailure
});
};
// TODO: Unify recordLateError with recordLateExpectation? The extra
// diagnostic info added by the latter is probably useful in most cases.
function recordLateError(error) {
@@ -428,6 +380,7 @@ getJasmineRequireObj().Env = function(j$) {
function(e) {
(runner.currentRunable() || topSuite).handleException(e);
};
options.deprecated = self.deprecated;
new j$.QueueRunner(options).execute();
}
@@ -453,7 +406,6 @@ getJasmineRequireObj().Env = function(j$) {
* @since 2.0.0
*/
this.topSuite = function() {
ensureNonParallel('topSuite');
return topSuite.metadata;
};
@@ -463,7 +415,72 @@ getJasmineRequireObj().Env = function(j$) {
* @see custom_reporter
*/
reporter = new j$.ReportDispatcher(
j$.reporterEvents,
[
/**
* `jasmineStarted` is called after all of the specs have been loaded, but just before execution starts.
* @function
* @name Reporter#jasmineStarted
* @param {JasmineStartedInfo} suiteInfo Information about the full Jasmine suite that is being run
* @param {Function} [done] Used to specify to Jasmine that this callback is asynchronous and Jasmine should wait until it has been called before moving on.
* @returns {} Optionally return a Promise instead of using `done` to cause Jasmine to wait for completion.
* @see async
*/
'jasmineStarted',
/**
* When the entire suite has finished execution `jasmineDone` is called
* @function
* @name Reporter#jasmineDone
* @param {JasmineDoneInfo} suiteInfo Information about the full Jasmine suite that just finished running.
* @param {Function} [done] Used to specify to Jasmine that this callback is asynchronous and Jasmine should wait until it has been called before moving on.
* @returns {} Optionally return a Promise instead of using `done` to cause Jasmine to wait for completion.
* @see async
*/
'jasmineDone',
/**
* `suiteStarted` is invoked when a `describe` starts to run
* @function
* @name Reporter#suiteStarted
* @param {SuiteResult} result Information about the individual {@link describe} being run
* @param {Function} [done] Used to specify to Jasmine that this callback is asynchronous and Jasmine should wait until it has been called before moving on.
* @returns {} Optionally return a Promise instead of using `done` to cause Jasmine to wait for completion.
* @see async
*/
'suiteStarted',
/**
* `suiteDone` is invoked when all of the child specs and suites for a given suite have been run
*
* While jasmine doesn't require any specific functions, not defining a `suiteDone` will make it impossible for a reporter to know when a suite has failures in an `afterAll`.
* @function
* @name Reporter#suiteDone
* @param {SuiteResult} result
* @param {Function} [done] Used to specify to Jasmine that this callback is asynchronous and Jasmine should wait until it has been called before moving on.
* @returns {} Optionally return a Promise instead of using `done` to cause Jasmine to wait for completion.
* @see async
*/
'suiteDone',
/**
* `specStarted` is invoked when an `it` starts to run (including associated `beforeEach` functions)
* @function
* @name Reporter#specStarted
* @param {SpecResult} result Information about the individual {@link it} being run
* @param {Function} [done] Used to specify to Jasmine that this callback is asynchronous and Jasmine should wait until it has been called before moving on.
* @returns {} Optionally return a Promise instead of using `done` to cause Jasmine to wait for completion.
* @see async
*/
'specStarted',
/**
* `specDone` is invoked when an `it` and its associated `beforeEach` and `afterEach` functions have been run.
*
* While jasmine doesn't require any specific functions, not defining a `specDone` will make it impossible for a reporter to know when a spec has failed.
* @function
* @name Reporter#specDone
* @param {SpecResult} result
* @param {Function} [done] Used to specify to Jasmine that this callback is asynchronous and Jasmine should wait until it has been called before moving on.
* @returns {} Optionally return a Promise instead of using `done` to cause Jasmine to wait for completion.
* @see async
*/
'specDone'
],
function(options) {
options.SkipPolicy = j$.NeverSkipPolicy;
return queueRunnerFactory(options);
@@ -482,24 +499,21 @@ getJasmineRequireObj().Env = function(j$) {
reportSpecDone
});
this.setParallelLoadingState = function(state) {
parallelLoadingState = state;
};
this.parallelReset = function() {
suiteBuilder.parallelReset();
runner.parallelReset();
};
/**
* Executes the specs.
*
* If called with no parameter or with a falsy parameter,
* If called with no parameters or with a falsy value as the first parameter,
* all specs will be executed except those that are excluded by a
* [spec filter]{@link Configuration#specFilter} or other mechanism. If the
* parameter is a list of spec/suite IDs, only those specs/suites will
* first parameter is a list of spec/suite IDs, only those specs/suites will
* be run.
*
* Both parameters are optional, but a completion callback is only valid as
* the second parameter. To specify a completion callback but not a list of
* specs/suites to run, pass null or undefined as the first parameter. The
* completion callback is supported for backward compatibility. In most
* cases it will be more convenient to use the returned promise instead.
*
* execute should not be called more than once unless the env has been
* configured with `{autoCleanClosures: false}`.
*
@@ -507,26 +521,25 @@ getJasmineRequireObj().Env = function(j$) {
* {@link JasmineDoneInfo|overall result} that's passed to a reporter's
* `jasmineDone` method, even if the suite did not pass. To determine
* whether the suite passed, check the value that the promise resolves to
* or use a {@link Reporter}. The promise will be rejected in the case of
* certain serious errors that prevent execution from starting.
* or use a {@link Reporter}.
*
* @name Env#execute
* @since 2.0.0
* @function
* @async
* @param {(string[])=} runablesToRun IDs of suites and/or specs to run
* @param {Function=} onComplete Function that will be called after all specs have run
* @return {Promise<JasmineDoneInfo>}
*/
this.execute = async function(runablesToRun) {
this.execute = function(runablesToRun, onComplete) {
installGlobalErrors();
if (parallelLoadingState) {
validateConfigForParallel();
}
return runner.execute(runablesToRun).then(function(jasmineDoneInfo) {
if (onComplete) {
onComplete();
}
const result = await runner.execute(runablesToRun);
this.cleanup_();
return result;
return jasmineDoneInfo;
});
};
/**
@@ -538,10 +551,6 @@ getJasmineRequireObj().Env = function(j$) {
* @see custom_reporter
*/
this.addReporter = function(reporterToAdd) {
if (parallelLoadingState) {
throw new Error('Reporters cannot be added via Env in parallel mode');
}
reporter.addReporter(reporterToAdd);
};
@@ -564,10 +573,6 @@ getJasmineRequireObj().Env = function(j$) {
* @function
*/
this.clearReporters = function() {
if (parallelLoadingState) {
throw new Error('Reporters cannot be removed via Env in parallel mode');
}
reporter.clearReporters();
};
@@ -667,38 +672,6 @@ getJasmineRequireObj().Env = function(j$) {
}
}
function ensureNonParallel(method) {
if (parallelLoadingState) {
throw new Error(`'${method}' is not available in parallel mode`);
}
}
function ensureNonParallelOrInDescribe(msg) {
if (parallelLoadingState && !suiteBuilder.inDescribe()) {
throw new Error(msg);
}
}
function ensureNonParallelOrInHelperOrInDescribe(method) {
if (parallelLoadingState === 'specs' && !suiteBuilder.inDescribe()) {
throw new Error(
'In parallel mode, ' +
method +
' must be in a describe block or in a helper file'
);
}
}
function validateConfigForParallel() {
if (!config.random) {
throw new Error('Randomization cannot be disabled in parallel mode');
}
if (config.seed !== null && config.seed !== undefined) {
throw new Error('Random seed cannot be set in parallel mode');
}
}
this.describe = function(description, definitionFn) {
ensureIsNotNested('describe');
const filename = callerCallerFilename();
@@ -715,7 +688,6 @@ getJasmineRequireObj().Env = function(j$) {
this.fdescribe = function(description, definitionFn) {
ensureIsNotNested('fdescribe');
ensureNonParallel('fdescribe');
const filename = callerCallerFilename();
return suiteBuilder.fdescribe(description, definitionFn, filename)
.metadata;
@@ -757,7 +729,6 @@ getJasmineRequireObj().Env = function(j$) {
this.fit = function(description, fn, timeout) {
ensureIsNotNested('fit');
ensureNonParallel('fit');
const filename = callerCallerFilename();
return suiteBuilder.fit(description, fn, timeout, filename).metadata;
};
@@ -810,72 +781,42 @@ getJasmineRequireObj().Env = function(j$) {
};
this.expect = function(actual) {
const runable = runner.currentRunable();
if (!runable) {
if (!runner.currentRunable()) {
throw new Error(
"'expect' was used when there was no current spec, this could be because an asynchronous test timed out"
);
}
return runable.expectationFactory(actual, runable);
return runner.currentRunable().expect(actual);
};
this.expectAsync = function(actual) {
const runable = runner.currentRunable();
if (!runable) {
if (!runner.currentRunable()) {
throw new Error(
"'expectAsync' was used when there was no current spec, this could be because an asynchronous test timed out"
);
}
return runable.asyncExpectationFactory(actual, runable);
};
this.throwUnless = function(actual) {
const runable = runner.currentRunable();
return throwUnlessFactory(actual, runable);
};
this.throwUnlessAsync = function(actual) {
const runable = runner.currentRunable();
return throwUnlessAsyncFactory(actual, runable);
return runner.currentRunable().expectAsync(actual);
};
this.beforeEach = function(beforeEachFunction, timeout) {
ensureIsNotNested('beforeEach');
ensureNonParallelOrInHelperOrInDescribe('beforeEach');
suiteBuilder.beforeEach(beforeEachFunction, timeout);
};
this.beforeAll = function(beforeAllFunction, timeout) {
ensureIsNotNested('beforeAll');
// This message is -npm-specific, but currently parallel operation is
// only supported via -npm.
ensureNonParallelOrInDescribe(
"In parallel mode, 'beforeAll' " +
'must be in a describe block. Use the globalSetup config ' +
'property for exactly-once setup in parallel mode.'
);
suiteBuilder.beforeAll(beforeAllFunction, timeout);
};
this.afterEach = function(afterEachFunction, timeout) {
ensureIsNotNested('afterEach');
ensureNonParallelOrInHelperOrInDescribe('afterEach');
suiteBuilder.afterEach(afterEachFunction, timeout);
};
this.afterAll = function(afterAllFunction, timeout) {
ensureIsNotNested('afterAll');
// This message is -npm-specific, but currently parallel operation is
// only supported via -npm.
ensureNonParallelOrInDescribe(
"In parallel mode, 'afterAll' " +
'must be in a describe block. Use the globalTeardown config ' +
'property for exactly-once teardown in parallel mode.'
);
suiteBuilder.afterAll(afterAllFunction, timeout);
};
@@ -930,10 +871,7 @@ getJasmineRequireObj().Env = function(j$) {
}
function callerCallerFilename() {
const frames = new j$.StackTrace(new Error()).frames;
// frames[3] should always exist except in Jasmine's own tests, which bypass
// the global it/describe layer, but don't crash if it doesn't.
return frames[3] && frames[3].file;
return new j$.StackTrace(new Error()).frames[3].file;
}
return Env;

View File

@@ -102,7 +102,7 @@ getJasmineRequireObj().ExceptionFormatter = function(j$) {
const result = {};
let empty = true;
for (const prop of Object.keys(error)) {
for (const prop in error) {
if (ignoredProperties.includes(prop)) {
continue;
}

View File

@@ -16,24 +16,19 @@ getJasmineRequireObj().Expectation = function(j$) {
}
/**
* Add some context to be included in matcher failures for an
* {@link expect|expectation}, so that it can be more easily distinguished
* from similar expectations.
* Add some context for an {@link expect}
* @function
* @name matchers#withContext
* @since 3.3.0
* @param {String} message - Additional context to show when the matcher fails
* @return {matchers}
* @example
* expect(things[0]).withContext('thing 0').toEqual('a');
* expect(things[1]).withContext('thing 1').toEqual('b');
*/
Expectation.prototype.withContext = function withContext(message) {
return addFilter(this, new ContextAddingFilter(message));
};
/**
* Invert the matcher following this {@link expect|expectation}
* Invert the matcher following this {@link expect}
* @member
* @name matchers#not
* @since 1.3.0

View File

@@ -6,23 +6,18 @@ getJasmineRequireObj().GlobalErrors = function(j$) {
let overrideHandler = null,
onRemoveOverrideHandler = null;
function onBrowserError(event) {
dispatchBrowserError(event.error, event);
}
function dispatchBrowserError(error, event) {
function onerror(message, source, lineno, colno, error) {
if (overrideHandler) {
// See discussion of spyOnGlobalErrorsAsync in base.js
overrideHandler(error);
overrideHandler(error || message);
return;
}
const handler = handlers[handlers.length - 1];
if (handler) {
handler(error, event);
handler.apply(null, Array.prototype.slice.call(arguments, 0));
} else {
throw error;
throw arguments[0];
}
}
@@ -56,7 +51,6 @@ getJasmineRequireObj().GlobalErrors = function(j$) {
const handler = handlers[handlers.length - 1];
if (overrideHandler) {
// See discussion of spyOnGlobalErrorsAsync in base.js
overrideHandler(error);
return;
}
@@ -100,7 +94,8 @@ getJasmineRequireObj().GlobalErrors = function(j$) {
this.installOne_('uncaughtException', 'Uncaught exception');
this.installOne_('unhandledRejection', 'Unhandled promise rejection');
} else {
global.addEventListener('error', onBrowserError);
const originalHandler = global.onerror;
global.onerror = onerror;
const browserRejectionHandler = function browserRejectionHandler(
event
@@ -108,19 +103,16 @@ getJasmineRequireObj().GlobalErrors = function(j$) {
if (j$.isError_(event.reason)) {
event.reason.jasmineMessage =
'Unhandled promise rejection: ' + event.reason;
dispatchBrowserError(event.reason, event);
global.onerror(event.reason);
} else {
dispatchBrowserError(
'Unhandled promise rejection: ' + event.reason,
event
);
global.onerror('Unhandled promise rejection: ' + event.reason);
}
};
global.addEventListener('unhandledrejection', browserRejectionHandler);
this.uninstall = function uninstall() {
global.removeEventListener('error', onBrowserError);
global.onerror = originalHandler;
global.removeEventListener(
'unhandledrejection',
browserRejectionHandler
@@ -129,13 +121,6 @@ getJasmineRequireObj().GlobalErrors = function(j$) {
}
};
// The listener at the top of the stack will be called with two arguments:
// the error and the event. Either of them may be falsy.
// The error will normally be provided, but will be falsy in the case of
// some browser load-time errors. The event will normally be provided in
// browsers but will be falsy in Node.
// Listeners that are pushed after spec files have been loaded should be
// able to just use the error parameter.
this.pushListener = function pushListener(listener) {
handlers.push(listener);
};

View File

@@ -1,95 +0,0 @@
getJasmineRequireObj().ParallelReportDispatcher = function(j$) {
'use strict';
/**
* @class ParallelReportDispatcher
* @implements Reporter
* @classdesc A report dispatcher packaged for convenient use from outside jasmine-core.
*
* This is intended to help packages like `jasmine` (the Jasmine runner for
* Node.js) do their own report dispatching in order to support parallel
* execution. If you aren't implementing a runner package that supports
* parallel execution, this class probably isn't what you're looking for.
*
* Warning: Do not use ParallelReportDispatcher in the same process that
* Jasmine specs run in. Doing so will break Jasmine's error handling.
* @param onError {function} Function called when an unhandled exception, unhandled promise rejection, or explicit reporter failure occurs
*/
function ParallelReportDispatcher(onError, deps = {}) {
const ReportDispatcher = deps.ReportDispatcher || j$.ReportDispatcher;
const QueueRunner = deps.QueueRunner || j$.QueueRunner;
const globalErrors = deps.globalErrors || new j$.GlobalErrors();
const dispatcher = new ReportDispatcher(
j$.reporterEvents,
function(queueRunnerOptions) {
queueRunnerOptions = {
...queueRunnerOptions,
globalErrors,
timeout: { setTimeout, clearTimeout },
fail: function(error) {
// A callback-style async reporter called either done.fail()
// or done(anError).
if (!error) {
error = new Error('A reporter called done.fail()');
}
onError(error);
},
onException: function(error) {
// A reporter method threw an exception or returned a rejected
// promise, or there was an unhandled exception or unhandled promise
// rejection while an asynchronous reporter method was running.
onError(error);
}
};
new QueueRunner(queueRunnerOptions).execute();
},
function(error) {
// A reporter called done() more than once.
onError(error);
}
);
const self = {
/**
* Adds a reporter to the list of reporters that events will be dispatched to.
* @function
* @name ParallelReportDispatcher#addReporter
* @param {Reporter} reporterToAdd The reporter to be added.
* @see custom_reporter
*/
addReporter: dispatcher.addReporter.bind(dispatcher),
/**
* Clears all registered reporters.
* @function
* @name ParallelReportDispatcher#clearReporters
*/
clearReporters: dispatcher.clearReporters.bind(dispatcher),
/**
* Installs a global error handler. After this method is called, any
* unhandled exceptions or unhandled promise rejections will be passed to
* the onError callback that was passed to the constructor.
* @function
* @name ParallelReportDispatcher#installGlobalErrors
*/
installGlobalErrors: globalErrors.install.bind(globalErrors),
/**
* Uninstalls the global error handler.
* @function
* @name ParallelReportDispatcher#uninstallGlobalErrors
*/
uninstallGlobalErrors: function() {
// late-bind uninstall because it doesn't exist until install is called
globalErrors.uninstall(globalErrors);
}
};
for (const eventName of j$.reporterEvents) {
self[eventName] = dispatcher[eventName].bind(dispatcher);
}
return self;
}
return ParallelReportDispatcher;
};

View File

@@ -62,11 +62,15 @@ getJasmineRequireObj().QueueRunner = function(j$) {
if (typeof this.onComplete !== 'function') {
throw new Error('invalid onComplete ' + JSON.stringify(this.onComplete));
}
this.deprecated = attrs.deprecated;
}
QueueRunner.prototype.execute = function() {
this.handleFinalError = (error, event) => {
this.onException(errorOrMsgForGlobalError(error, event));
this.handleFinalError = (message, source, lineno, colno, error) => {
// Older browsers would send the error as the first parameter. HTML5
// specifies the the five parameters above. The error instance should
// be preffered, otherwise the call stack would get lost.
this.onException(error || message);
};
this.globalErrors.pushListener(this.handleFinalError);
this.run(0);
@@ -96,8 +100,10 @@ getJasmineRequireObj().QueueRunner = function(j$) {
this.recordError_(iterativeIndex);
};
function handleError(error, event) {
onException(errorOrMsgForGlobalError(error, event));
function handleError(error) {
// TODO probably shouldn't next() right away here.
// That makes debugging async failures much more confusing.
onException(error);
}
const cleanup = once(() => {
if (timeoutId !== void 0) {
@@ -253,21 +259,17 @@ getJasmineRequireObj().QueueRunner = function(j$) {
// on the stack at this point.
if (j$.isAsyncFunction_(fn)) {
this.onException(
new Error(
'An asynchronous before/it/after ' +
'function was defined with the async keyword but also took a ' +
'done callback. Either remove the done callback (recommended) or ' +
'remove the async keyword.'
)
'An asynchronous before/it/after ' +
'function was defined with the async keyword but also took a ' +
'done callback. Either remove the done callback (recommended) or ' +
'remove the async keyword.'
);
} else {
this.onException(
new Error(
'An asynchronous before/it/after ' +
'function took a done callback but also returned a promise. ' +
'Either remove the done callback (recommended) or change the ' +
'function to not return a promise.'
)
'An asynchronous before/it/after ' +
'function took a done callback but also returned a promise. ' +
'Either remove the done callback (recommended) or change the ' +
'function to not return a promise.'
);
}
}
@@ -283,16 +285,5 @@ getJasmineRequireObj().QueueRunner = function(j$) {
};
}
function errorOrMsgForGlobalError(error, event) {
// TODO: In cases where error is a string or undefined, the error message
// that gets sent to reporters will be `${message} thrown`, which could
// be improved to not say "thrown" when the cause wasn't necessarily
// an exception or to provide hints about throwing Errors rather than
// strings.
return (
error || (event && event.message) || 'Global error event with no message'
);
}
return QueueRunner;
};

View File

@@ -1,6 +1,4 @@
getJasmineRequireObj().ReportDispatcher = function(j$) {
'use strict';
function ReportDispatcher(methods, queueRunnerFactory, onLateError) {
const dispatchedMethods = methods || [];

View File

@@ -2,7 +2,6 @@ getJasmineRequireObj().Runner = function(j$) {
class Runner {
constructor(options) {
this.topSuite_ = options.topSuite;
// TODO use names that read like getters
this.totalSpecsDefined_ = options.totalSpecsDefined;
this.focusedRunables_ = options.focusedRunables;
this.runableResources_ = options.runableResources;
@@ -27,11 +26,11 @@ getJasmineRequireObj().Runner = function(j$) {
];
}
parallelReset() {
this.executedBefore_ = false;
}
async execute(runablesToRun) {
// Although execute returns a promise, it isn't async for backwards
// compatibility: The "Invalid order" exception needs to be propagated
// synchronously from Env#execute.
// TODO: make this and Env#execute async in the next major release
execute(runablesToRun) {
if (this.executedBefore_) {
this.topSuite_.reset();
}
@@ -127,17 +126,13 @@ getJasmineRequireObj().Runner = function(j$) {
/**
* Information passed to the {@link Reporter#jasmineStarted} event.
* @typedef JasmineStartedInfo
* @property {Int} totalSpecsDefined - The total number of specs defined in this suite. Note that this property is not present when Jasmine is run in parallel mode.
* @property {Order} order - Information about the ordering (random or not) of this execution of the suite. Note that this property is not present when Jasmine is run in parallel mode.
* @property {Boolean} parallel - Whether Jasmine is being run in parallel mode.
* @property {Int} totalSpecsDefined - The total number of specs defined in this suite.
* @property {Order} order - Information about the ordering (random or not) of this execution of the suite.
* @since 2.0.0
*/
await this.reporter_.jasmineStarted({
// In parallel mode, the jasmineStarted event is separately dispatched
// by jasmine-npm. This event only reaches reporters in non-parallel.
totalSpecsDefined,
order: order,
parallel: false
order: order
});
this.currentlyExecutingSuites_.push(this.topSuite_);
@@ -149,7 +144,7 @@ getJasmineRequireObj().Runner = function(j$) {
this.runableResources_.clearForRunable(this.topSuite_.id);
this.currentlyExecutingSuites_.pop();
let overallStatus, incompleteReason, incompleteCode;
let overallStatus, incompleteReason;
if (
this.hasFailures ||
@@ -159,11 +154,9 @@ getJasmineRequireObj().Runner = function(j$) {
} else if (this.focusedRunables_().length > 0) {
overallStatus = 'incomplete';
incompleteReason = 'fit() or fdescribe() was found';
incompleteCode = 'focused';
} else if (totalSpecsDefined === 0) {
overallStatus = 'incomplete';
incompleteReason = 'No specs found';
incompleteCode = 'noSpecsFound';
} else {
overallStatus = 'passed';
}
@@ -173,10 +166,8 @@ getJasmineRequireObj().Runner = function(j$) {
* @typedef JasmineDoneInfo
* @property {OverallStatus} overallStatus - The overall result of the suite: 'passed', 'failed', or 'incomplete'.
* @property {Int} totalTime - The total time (in ms) that it took to execute the suite
* @property {String} incompleteReason - Human-readable explanation of why the suite was incomplete.
* @property {String} incompleteCode - Machine-readable explanation of why the suite was incomplete: 'focused', 'noSpecsFound', or undefined.
* @property {Order} order - Information about the ordering (random or not) of this execution of the suite. Note that this property is not present when Jasmine is run in parallel mode.
* @property {Int} numWorkers - Number of parallel workers. Note that this property is only present when Jasmine is run in parallel mode.
* @property {IncompleteReason} incompleteReason - Explanation of why the suite was incomplete.
* @property {Order} order - Information about the ordering (random or not) of this execution of the suite.
* @property {Expectation[]} failedExpectations - List of expectations that failed in an {@link afterAll} at the global level.
* @property {Expectation[]} deprecationWarnings - List of deprecation warnings that occurred at the global level.
* @since 2.4.0
@@ -185,7 +176,6 @@ getJasmineRequireObj().Runner = function(j$) {
overallStatus: overallStatus,
totalTime: jasmineTimer.elapsed(),
incompleteReason: incompleteReason,
incompleteCode: incompleteCode,
order: order,
failedExpectations: this.topSuite_.result.failedExpectations,
deprecationWarnings: this.topSuite_.result.deprecationWarnings

View File

@@ -70,6 +70,14 @@ getJasmineRequireObj().Spec = function(j$) {
this.result.properties[key] = value;
};
Spec.prototype.expect = function(actual) {
return this.expectationFactory(actual, this);
};
Spec.prototype.expectAsync = function(actual) {
return this.asyncExpectationFactory(actual, this);
};
Spec.prototype.execute = function(
queueRunnerFactory,
onComplete,

View File

@@ -84,16 +84,6 @@ getJasmineRequireObj().SpyRegistry = function(j$) {
obj[methodName] = spiedMethod;
// Check if setting the property actually worked. Some objects, such as
// localStorage in Firefox and later Safari versions, have no-op setters.
if (obj[methodName] !== spiedMethod) {
throw new Error(
j$.formatErrorMsg('<spyOn>')(
`Can't spy on ${methodName} because assigning to it had no effect`
)
);
}
return spiedMethod;
};

View File

@@ -32,7 +32,7 @@ getJasmineRequireObj().StackTrace = function(j$) {
// e.g. " at /some/path:4320:20
{ re: /\s*at (.+)$/, fileLineColIx: 1, style: 'v8' },
// Safari, most Firefox stack frames
// PhantomJS on OS X, Safari, Firefox
// e.g. "run@http://localhost:8888/__jasmine__/jasmine.js:4320:27"
// or "http://localhost:8888/__jasmine__/jasmine.js:4320:27"
{
@@ -40,15 +40,6 @@ getJasmineRequireObj().StackTrace = function(j$) {
fnIx: 2,
fileLineColIx: 3,
style: 'webkit'
},
// Some Firefox stack frames when the developer tools are open
// e.g. "promise callback*specStarted@http://localhost:8888/__jasmine__/jasmine.js:1880:41"
{
re: /^^(?:((?:promise callback|[^\s]+ handler)\*([^@\s]+)@)|@)?([^\s]+)$/,
fnIx: 2,
fileLineColIx: 3,
style: 'webkit'
}
];

View File

@@ -28,6 +28,14 @@ getJasmineRequireObj().Suite = function(j$) {
this.result.properties[key] = value;
};
Suite.prototype.expect = function(actual) {
return this.expectationFactory(actual, this);
};
Suite.prototype.expectAsync = function(actual) {
return this.asyncExpectationFactory(actual, this);
};
Suite.prototype.getFullName = function() {
const fullName = [];
for (
@@ -130,10 +138,6 @@ getJasmineRequireObj().Suite = function(j$) {
this.reportedDone = false;
};
Suite.prototype.removeChildren = function() {
this.children = [];
};
Suite.prototype.addChild = function(child) {
this.children.push(child);
};

View File

@@ -22,17 +22,6 @@ getJasmineRequireObj().SuiteBuilder = function(j$) {
this.focusedRunables = [];
}
inDescribe() {
return this.currentDeclarationSuite_ !== this.topSuite;
}
parallelReset() {
this.topSuite.removeChildren();
this.topSuite.reset();
this.totalSpecsDefined = 0;
this.focusedRunables = [];
}
describe(description, definitionFn, filename) {
ensureIsFunction(definitionFn, 'describe');
const suite = this.suiteFactory_(description, filename);

View File

@@ -5,35 +5,16 @@ getJasmineRequireObj().Timer = function() {
};
})(Date);
/**
* @class Timer
* @classdesc Tracks elapsed time
* @example
* const timer = new jasmine.Timer();
* timer.start();
* const elapsed = timer.elapsed()
*/
function Timer(options) {
options = options || {};
const now = options.now || defaultNow;
let startTime;
/**
* Starts the timer.
* @function
* @name Timer#start
*/
this.start = function() {
startTime = now();
};
/**
* Determines the time since the timer was started.
* @function
* @name Timer#elapsed
* @returns {number} Elapsed time in milliseconds, or NaN if the timer has not been started
*/
this.elapsed = function() {
return now() - startTime;
};

View File

@@ -11,9 +11,7 @@ getJasmineRequireObj().MapContaining = function(j$) {
}
MapContaining.prototype.asymmetricMatch = function(other, matchersUtil) {
if (!j$.isMap(other)) {
return false;
}
if (!j$.isMap(other)) return false;
for (const [key, value] of this.sample) {
// for each key/value pair in `sample`

View File

@@ -11,9 +11,7 @@ getJasmineRequireObj().SetContaining = function(j$) {
}
SetContaining.prototype.asymmetricMatch = function(other, matchersUtil) {
if (!j$.isSet(other)) {
return false;
}
if (!j$.isSet(other)) return false;
for (const item of this.sample) {
// for each item in `sample` there should be at least one matching item in `other`

View File

@@ -224,10 +224,9 @@ getJasmineRequireObj().base = function(j$, jasmineGlobal) {
};
/**
* Get an {@link AsymmetricEqualityTester} that will succeed if the actual
* value being compared is an instance of the specified class/constructor.
* @name asymmetricEqualityTesters.any
* @emittedName jasmine.any
* Get an {@link AsymmetricEqualityTester}, usable in any {@link matchers|matcher} that uses Jasmine's equality (e.g. {@link matchers#toEqual|toEqual}, {@link matchers#toContain|toContain}, or {@link matchers#toHaveBeenCalledWith|toHaveBeenCalledWith}),
* that will succeed if the actual value being compared is an instance of the specified class/constructor.
* @name jasmine.any
* @since 1.3.0
* @function
* @param {Constructor} clazz - The constructor to check against.
@@ -237,10 +236,9 @@ getJasmineRequireObj().base = function(j$, jasmineGlobal) {
};
/**
* Get an {@link AsymmetricEqualityTester} that will succeed if the actual
* value being compared is not `null` and not `undefined`.
* @name asymmetricEqualityTesters.anything
* @emittedName jasmine.anything
* Get an {@link AsymmetricEqualityTester}, usable in any {@link matchers|matcher} that uses Jasmine's equality (e.g. {@link matchers#toEqual|toEqual}, {@link matchers#toContain|toContain}, or {@link matchers#toHaveBeenCalledWith|toHaveBeenCalledWith}),
* that will succeed if the actual value being compared is not `null` and not `undefined`.
* @name jasmine.anything
* @since 2.2.0
* @function
*/
@@ -249,10 +247,9 @@ getJasmineRequireObj().base = function(j$, jasmineGlobal) {
};
/**
* Get an {@link AsymmetricEqualityTester} that will succeed if the actual
* value being compared is `true` or anything truthy.
* @name asymmetricEqualityTesters.truthy
* @emittedName jasmine.truthy
* Get an {@link AsymmetricEqualityTester}, usable in any {@link matchers|matcher} that uses Jasmine's equality (e.g. {@link matchers#toEqual|toEqual}, {@link matchers#toContain|toContain}, or {@link matchers#toHaveBeenCalledWith|toHaveBeenCalledWith}),
* that will succeed if the actual value being compared is `true` or anything truthy.
* @name jasmine.truthy
* @since 3.1.0
* @function
*/
@@ -261,11 +258,9 @@ getJasmineRequireObj().base = function(j$, jasmineGlobal) {
};
/**
* Get an {@link AsymmetricEqualityTester} that will succeed if the actual
* value being compared is `null`, `undefined`, `0`, `false` or anything
* falsy.
* @name asymmetricEqualityTesters.falsy
* @emittedName jasmine.falsy
* Get an {@link AsymmetricEqualityTester}, usable in any {@link matchers|matcher} that uses Jasmine's equality (e.g. {@link matchers#toEqual|toEqual}, {@link matchers#toContain|toContain}, or {@link matchers#toHaveBeenCalledWith|toHaveBeenCalledWith}),
* that will succeed if the actual value being compared is `null`, `undefined`, `0`, `false` or anything falsey.
* @name jasmine.falsy
* @since 3.1.0
* @function
*/
@@ -274,10 +269,9 @@ getJasmineRequireObj().base = function(j$, jasmineGlobal) {
};
/**
* Get an {@link AsymmetricEqualityTester} that will succeed if the actual
* value being compared is empty.
* @name asymmetricEqualityTesters.empty
* @emittedName jasmine.empty
* Get an {@link AsymmetricEqualityTester}, usable in any {@link matchers|matcher} that uses Jasmine's equality (e.g. {@link matchers#toEqual|toEqual}, {@link matchers#toContain|toContain}, or {@link matchers#toHaveBeenCalledWith|toHaveBeenCalledWith}),
* that will succeed if the actual value being compared is empty.
* @name jasmine.empty
* @since 3.1.0
* @function
*/
@@ -286,10 +280,10 @@ getJasmineRequireObj().base = function(j$, jasmineGlobal) {
};
/**
* Get an {@link AsymmetricEqualityTester} that passes if the actual value is
* the same as the sample as determined by the `===` operator.
* @name asymmetricEqualityTesters.is
* @emittedName jasmine.is
* Get an {@link AsymmetricEqualityTester}, usable in any {@link matchers|matcher}
* that passes if the actual value is the same as the sample as determined
* by the `===` operator.
* @name jasmine.is
* @function
* @param {Object} sample - The value to compare the actual to.
*/
@@ -298,10 +292,9 @@ getJasmineRequireObj().base = function(j$, jasmineGlobal) {
};
/**
* Get an {@link AsymmetricEqualityTester} that will succeed if the actual
* value being compared is not empty.
* @name asymmetricEqualityTesters.notEmpty
* @emittedName jasmine.notEmpty
* Get an {@link AsymmetricEqualityTester}, usable in any {@link matchers|matcher} that uses Jasmine's equality (e.g. {@link matchers#toEqual|toEqual}, {@link matchers#toContain|toContain}, or {@link matchers#toHaveBeenCalledWith|toHaveBeenCalledWith}),
* that will succeed if the actual value being compared is not empty.
* @name jasmine.notEmpty
* @since 3.1.0
* @function
*/
@@ -310,10 +303,9 @@ getJasmineRequireObj().base = function(j$, jasmineGlobal) {
};
/**
* Get an {@link AsymmetricEqualityTester} that will succeed if the actual
* value being compared contains at least the specified keys and values.
* @name asymmetricEqualityTesters.objectContaining
* @emittedName jasmine.objectContaining
* Get an {@link AsymmetricEqualityTester}, usable in any {@link matchers|matcher} that uses Jasmine's equality (e.g. {@link matchers#toEqual|toEqual}, {@link matchers#toContain|toContain}, or {@link matchers#toHaveBeenCalledWith|toHaveBeenCalledWith}),
* that will succeed if the actual value being compared contains at least the keys and values.
* @name jasmine.objectContaining
* @since 1.3.0
* @function
* @param {Object} sample - The subset of properties that _must_ be in the actual.
@@ -323,10 +315,9 @@ getJasmineRequireObj().base = function(j$, jasmineGlobal) {
};
/**
* Get an {@link AsymmetricEqualityTester} that will succeed if the actual
* value is a `String` that matches the `RegExp` or `String`.
* @name asymmetricEqualityTesters.stringMatching
* @emittedName jasmine.stringMatching
* Get an {@link AsymmetricEqualityTester}, usable in any {@link matchers|matcher} that uses Jasmine's equality (e.g. {@link matchers#toEqual|toEqual}, {@link matchers#toContain|toContain}, or {@link matchers#toHaveBeenCalledWith|toHaveBeenCalledWith}),
* that will succeed if the actual value is a `String` that matches the `RegExp` or `String`.
* @name jasmine.stringMatching
* @since 2.2.0
* @function
* @param {RegExp|String} expected
@@ -336,10 +327,9 @@ getJasmineRequireObj().base = function(j$, jasmineGlobal) {
};
/**
* Get an {@link AsymmetricEqualityTester} that will succeed if the actual
* value is a `String` that contains the specified `String`.
* @name asymmetricEqualityTesters.stringContaining
* @emittedName jasmine.stringContaining
* Get an {@link AsymmetricEqualityTester}, usable in any {@link matchers|matcher} that uses Jasmine's equality (e.g. {@link matchers#toEqual|toEqual}, {@link matchers#toContain|toContain}, or {@link matchers#toHaveBeenCalledWith|toHaveBeenCalledWith}),
* that will succeed if the actual value is a `String` that contains the specified `String`.
* @name jasmine.stringContaining
* @since 3.10.0
* @function
* @param {String} expected
@@ -349,10 +339,9 @@ getJasmineRequireObj().base = function(j$, jasmineGlobal) {
};
/**
* Get an {@link AsymmetricEqualityTester} that will succeed if the actual
* value is an `Array` that contains at least the elements in the sample.
* @name asymmetricEqualityTesters.arrayContaining
* @emittedName jasmine.arrayContaining
* Get an {@link AsymmetricEqualityTester}, usable in any {@link matchers|matcher} that uses Jasmine's equality (e.g. {@link matchers#toEqual|toEqual}, {@link matchers#toContain|toContain}, or {@link matchers#toHaveBeenCalledWith|toHaveBeenCalledWith}),
* that will succeed if the actual value is an `Array` that contains at least the elements in the sample.
* @name jasmine.arrayContaining
* @since 2.2.0
* @function
* @param {Array} sample
@@ -362,11 +351,9 @@ getJasmineRequireObj().base = function(j$, jasmineGlobal) {
};
/**
* Get an {@link AsymmetricEqualityTester} that will succeed if the actual
* value is an `Array` that contains all of the elements in the sample in
* any order.
* @name asymmetricEqualityTesters.arrayWithExactContents
* @emittedName jasmine.arrayWithExactContents
* Get an {@link AsymmetricEqualityTester}, usable in any {@link matchers|matcher} that uses Jasmine's equality (e.g. {@link matchers#toEqual|toEqual}, {@link matchers#toContain|toContain}, or {@link matchers#toHaveBeenCalledWith|toHaveBeenCalledWith}),
* that will succeed if the actual value is an `Array` that contains all of the elements in the sample in any order.
* @name jasmine.arrayWithExactContents
* @since 2.8.0
* @function
* @param {Array} sample
@@ -376,11 +363,10 @@ getJasmineRequireObj().base = function(j$, jasmineGlobal) {
};
/**
* Get an {@link AsymmetricEqualityTester} that will succeed if every
* key/value pair in the sample passes the deep equality comparison
* Get an {@link AsymmetricEqualityTester}, usable in any {@link matchers|matcher} that uses Jasmine's equality (e.g. {@link matchers#toEqual|toEqual}, {@link matchers#toContain|toContain}, or {@link matchers#toHaveBeenCalledWith|toHaveBeenCalledWith}),
* that will succeed if every key/value pair in the sample passes the deep equality comparison
* with at least one key/value pair in the actual value being compared
* @name asymmetricEqualityTesters.mapContaining
* @emittedName jasmine.mapContaining
* @name jasmine.mapContaining
* @since 3.5.0
* @function
* @param {Map} sample - The subset of items that _must_ be in the actual.
@@ -390,11 +376,10 @@ getJasmineRequireObj().base = function(j$, jasmineGlobal) {
};
/**
* Get an {@link AsymmetricEqualityTester} that will succeed if every item
* in the sample passes the deep equality comparison
* Get an {@link AsymmetricEqualityTester}, usable in any {@link matchers|matcher} that uses Jasmine's equality (e.g. {@link matchers#toEqual|toEqual}, {@link matchers#toContain|toContain}, or {@link matchers#toHaveBeenCalledWith|toHaveBeenCalledWith}),
* that will succeed if every item in the sample passes the deep equality comparison
* with at least one item in the actual value being compared
* @name asymmetricEqualityTesters.setContaining
* @emittedName jasmine.setContaining
* @name jasmine.setContaining
* @since 3.5.0
* @function
* @param {Set} sample - The subset of items that _must_ be in the actual.
@@ -450,26 +435,14 @@ getJasmineRequireObj().base = function(j$, jasmineGlobal) {
* handling will be restored when the promise returned from the callback is
* settled.
*
* When the JavaScript runtime reports an uncaught error or unhandled rejection,
* the spy will be called with a single parameter representing Jasmine's best
* effort at describing the error. This parameter may be of any type, because
* JavaScript allows anything to be thrown or used as the reason for a
* rejected promise, but Error instances and strings are most common.
*
* Note: The JavaScript runtime may deliver uncaught error events and unhandled
* rejection events asynchronously, especially in browsers. If the event
* occurs after the promise returned from the callback is settled, it won't
* be routed to the spy even if the underlying error occurred previously.
* It's up to you to ensure that all of the error/rejection events that you
* want to handle have occurred before you resolve the promise returned from
* the callback.
* It's up to you to ensure that the returned promise isn't resolved until
* all of the error/rejection events that you want to handle have occurred.
*
* You must ensure that the `it`/`beforeEach`/etc fn that called
* `spyOnGlobalErrorsAsync` does not signal completion until after the
* promise returned by `spyOnGlobalErrorsAsync` is resolved. Normally this is
* done by `await`ing the returned promise. Leaving the global error spy
* installed after the `it`/`beforeEach`/etc fn that installed it signals
* completion is likely to cause problems and is not supported.
* You must await the return value of spyOnGlobalErrorsAsync.
* @name jasmine.spyOnGlobalErrorsAsync
* @function
* @async

View File

@@ -9,7 +9,7 @@ getJasmineRequireObj().toHaveSize = function(j$) {
* array = [1,2];
* expect(array).toHaveSize(2);
*/
function toHaveSize(matchersUtil) {
function toHaveSize() {
return {
compare: function(actual, expected) {
const result = {
@@ -24,29 +24,12 @@ getJasmineRequireObj().toHaveSize = function(j$) {
throw new Error('Cannot get size of ' + actual + '.');
}
let actualSize;
if (j$.isSet(actual) || j$.isMap(actual)) {
actualSize = actual.size;
result.pass = actual.size === expected;
} else if (isLength(actual.length)) {
actualSize = actual.length;
result.pass = actual.length === expected;
} else {
actualSize = Object.keys(actual).length;
}
result.pass = actualSize === expected;
if (!result.pass) {
result.message = function() {
return (
'Expected ' +
matchersUtil.pp(actual) +
' with size ' +
actualSize +
' to have size ' +
expected +
'.'
);
};
result.pass = Object.keys(actual).length === expected;
}
return result;

View File

@@ -32,10 +32,7 @@ getJasmineRequireObj().toHaveSpyInteractions = function(j$) {
let hasSpy = false;
const calledSpies = [];
for (const spy of Object.values(actual)) {
if (!j$.isSpy(spy)) {
continue;
}
if (!j$.isSpy(spy)) continue;
hasSpy = true;
if (spy.calls.any()) {

View File

@@ -1,96 +0,0 @@
getJasmineRequireObj().reporterEvents = function() {
/**
* Used to tell Jasmine what optional or uncommonly implemented features
* the reporter supports. If not specified, the defaults described in
* {@link ReporterCapabilities} will apply.
* @name Reporter#reporterCapabilities
* @type ReporterCapabilities | undefined
* @since 5.0
*/
/**
* Used to tell Jasmine what optional or uncommonly implemented features
* the reporter supports.
* @interface ReporterCapabilities
* @see Reporter#reporterCapabilities
* @since 5.0
*/
/**
* Indicates whether the reporter supports parallel execution. Jasmine will
* not allow parallel execution unless all reporters that are in use set this
* capability to true.
* @name ReporterCapabilities#parallel
* @type boolean | undefined
* @default false
* @see running_specs_in_parallel
* @since 5.0
*/
const events = [
/**
* `jasmineStarted` is called after all of the specs have been loaded, but just before execution starts.
* @function
* @name Reporter#jasmineStarted
* @param {JasmineStartedInfo} suiteInfo Information about the full Jasmine suite that is being run
* @param {Function} [done] Used to specify to Jasmine that this callback is asynchronous and Jasmine should wait until it has been called before moving on.
* @returns {} Optionally return a Promise instead of using `done` to cause Jasmine to wait for completion.
* @see async
*/
'jasmineStarted',
/**
* When the entire suite has finished execution `jasmineDone` is called
* @function
* @name Reporter#jasmineDone
* @param {JasmineDoneInfo} suiteInfo Information about the full Jasmine suite that just finished running.
* @param {Function} [done] Used to specify to Jasmine that this callback is asynchronous and Jasmine should wait until it has been called before moving on.
* @returns {} Optionally return a Promise instead of using `done` to cause Jasmine to wait for completion.
* @see async
*/
'jasmineDone',
/**
* `suiteStarted` is invoked when a `describe` starts to run
* @function
* @name Reporter#suiteStarted
* @param {SuiteResult} result Information about the individual {@link describe} being run
* @param {Function} [done] Used to specify to Jasmine that this callback is asynchronous and Jasmine should wait until it has been called before moving on.
* @returns {} Optionally return a Promise instead of using `done` to cause Jasmine to wait for completion.
* @see async
*/
'suiteStarted',
/**
* `suiteDone` is invoked when all of the child specs and suites for a given suite have been run
*
* While jasmine doesn't require any specific functions, not defining a `suiteDone` will make it impossible for a reporter to know when a suite has failures in an `afterAll`.
* @function
* @name Reporter#suiteDone
* @param {SuiteResult} result
* @param {Function} [done] Used to specify to Jasmine that this callback is asynchronous and Jasmine should wait until it has been called before moving on.
* @returns {} Optionally return a Promise instead of using `done` to cause Jasmine to wait for completion.
* @see async
*/
'suiteDone',
/**
* `specStarted` is invoked when an `it` starts to run (including associated `beforeEach` functions)
* @function
* @name Reporter#specStarted
* @param {SpecResult} result Information about the individual {@link it} being run
* @param {Function} [done] Used to specify to Jasmine that this callback is asynchronous and Jasmine should wait until it has been called before moving on.
* @returns {} Optionally return a Promise instead of using `done` to cause Jasmine to wait for completion.
* @see async
*/
'specStarted',
/**
* `specDone` is invoked when an `it` and its associated `beforeEach` and `afterEach` functions have been run.
*
* While jasmine doesn't require any specific functions, not defining a `specDone` will make it impossible for a reporter to know when a spec has failed.
* @function
* @name Reporter#specDone
* @param {SpecResult} result
* @param {Function} [done] Used to specify to Jasmine that this callback is asynchronous and Jasmine should wait until it has been called before moving on.
* @returns {} Optionally return a Promise instead of using `done` to cause Jasmine to wait for completion.
* @see async
*/
'specDone'
];
Object.freeze(events);
return events;
};

View File

@@ -67,9 +67,7 @@ var getJasmineRequireObj = (function(jasmineGlobal) {
j$.CompleteOnFirstErrorSkipPolicy = jRequire.CompleteOnFirstErrorSkipPolicy(
j$
);
j$.reporterEvents = jRequire.reporterEvents(j$);
j$.ReportDispatcher = jRequire.ReportDispatcher(j$);
j$.ParallelReportDispatcher = jRequire.ParallelReportDispatcher(j$);
j$.RunableResources = jRequire.RunableResources(j$);
j$.Runner = jRequire.Runner(j$);
j$.Spec = jRequire.Spec(j$);

View File

@@ -225,54 +225,6 @@ getJasmineRequireObj().interface = function(jasmine, env) {
return env.expectAsync(actual);
},
/**
* Create an asynchronous expectation for a spec and throw an error if it fails.
*
* This is intended to allow Jasmine matchers to be used with tools like
* testing-library's `waitFor`, which expect matcher failures to throw
* exceptions and not trigger a spec failure if the exception is caught.
* It can also be used to integration-test custom matchers.
*
* If the resulting expectation fails, a {@link ThrowUnlessFailure} will be
* thrown. A failed expectation will not result in a spec failure unless the
* exception propagates back to Jasmine, either via the call stack or via
* the global unhandled exception/unhandled promise rejection events.
* @name throwUnlessAsync
* @since 5.1.0
* @function
* @param actual
* @global
* @param {Object} actual - Actual computed value to test expectations against.
* @return {matchers}
*/
throwUnlessAsync: function(actual) {
return env.throwUnlessAsync(actual);
},
/**
* Create an expectation for a spec and throw an error if it fails.
*
* This is intended to allow Jasmine matchers to be used with tools like
* testing-library's `waitFor`, which expect matcher failures to throw
* exceptions and not trigger a spec failure if the exception is caught.
* It can also be used to integration-test custom matchers.
*
* If the resulting expectation fails, a {@link ThrowUnlessFailure} will be
* thrown. A failed expectation will not result in a spec failure unless the
* exception propagates back to Jasmine, either via the call stack or via
* the global unhandled exception/unhandled promise rejection events.
* @name throwUnless
* @since 5.1.0
* @function
* @param actual
* @global
* @param {Object} actual - Actual computed value to test expectations against.
* @return {matchers}
*/
throwUnless: function(actual) {
return env.throwUnless(actual);
},
/**
* Mark a spec as pending, expectation results will be ignored.
* @name pending
@@ -345,12 +297,6 @@ getJasmineRequireObj().interface = function(jasmine, env) {
}),
/**
* <p>Members of the jasmine global.</p>
* <p>Note: The members of the
* {@link asymmetricEqualityTesters|asymmetricEqualityTesters namespace}
* are also accessed via the jasmine global, but due to jsdoc limitations
* they are not listed here.</p>
*
* @namespace jasmine
*/
jasmine: jasmine
@@ -429,11 +375,7 @@ getJasmineRequireObj().interface = function(jasmine, env) {
* @since 1.3.0
* @function
* @param {String} [name] - Name to give the spy. This will be displayed in failure messages.
* @param {Function} [originalFn] - The "real" function. This will
* be used for subsequent calls to the spy after you call
* `mySpy.and.callThrough()`. In most cases you should omit this parameter.
* The usual way to supply an original function is to call {@link spyOn}
* instead of createSpy.
* @param {Function} [originalFn] - Function to act as the real implementation.
* @return {Spy}
*/
jasmine.createSpy = function(name, originalFn) {
@@ -484,27 +426,5 @@ getJasmineRequireObj().interface = function(jasmine, env) {
return env.setDefaultSpyStrategy(defaultStrategyFn);
};
/**
* {@link AsymmetricEqualityTester|Asymmetric equality testers} allow for
* non-exact matching in matchers that use Jasmine's deep value equality
* semantics, such as {@link matchers#toEqual|toEqual},
* {@link matchers#toContain|toContain}, and
* {@link matchers#toHaveBeenCalledWith|toHaveBeenCalledWith}.
*
* @example
* const someComplexObject = {
* foo: 'bar',
* baz: 'a string that contains "something"',
* qux: 'whatever'
* };
* // Passes.
* expect(someComplexObject).toEqual(jasmine.objectContaining({
* foo: 'bar',
* baz: jasmine.stringContaining('something')
* });
*
* @namespace asymmetricEqualityTesters
*/
return jasmineInterface;
};

View File

@@ -430,11 +430,7 @@ jasmineRequire.HtmlReporter = function(j$) {
'tr',
{},
createDom('td', {}, entry.timestamp.toString()),
createDom(
'td',
{ className: 'jasmine-debug-log-msg' },
entry.message
)
createDom('td', {}, entry.message)
)
);
});

View File

@@ -424,9 +424,5 @@ body {
table, th, td {
border: 1px solid #ddd;
}
.jasmine-debug-log-msg {
white-space: pre;
}
}
}