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
161 changed files with 3009 additions and 7711 deletions

View File

@@ -4,21 +4,25 @@
version: 2.1
executors:
node24:
docker:
- image: cimg/node:24.0.0
working_directory: ~/workspace
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.20.5
- 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:
@@ -55,34 +59,20 @@ jobs:
name: Run tests
command: npm test
test_parallel: &test_parallel
parameters:
executor:
type: executor
executor: << parameters.executor >>
steps:
- attach_workspace:
at: .
- run:
name: Run tests in parallel
command: npm run test:parallel
test_browsers: &test_browsers
executor: node18
executor: node14
steps:
- attach_workspace:
at: .
- run:
name: Install Sauce Connect
command: |
tmpdir=$(mktemp -d)
cd "$tmpdir"
curl https://saucelabs.com/downloads/sauce-connect/5.2.2/sauce-connect-5.2.2_linux.x86_64.tar.gz | tar zxf -
chmod +x sc
cd /tmp
curl https://saucelabs.com/downloads/sc-4.7.1-linux.tar.gz | tar zxf -
chmod +x sc-4.7.1-linux/bin/sc
mkdir ~/workspace/bin
cp sc ~/workspace/bin
echo "Sauce Connect version info:"
~/workspace/bin/sc version
cp sc-4.7.1-linux/bin/sc ~/workspace/bin
~/workspace/bin/sc --version
- run:
name: Run tests
command: |
@@ -90,13 +80,13 @@ jobs:
# cleanly if we kill it from a different step than it started in.
export PATH=$PATH:$HOME/workspace/bin
export SAUCE_TUNNEL_NAME=$CIRCLE_WORKFLOW_JOB_ID
scripts/start-sauce-connect
export SAUCE_TUNNEL_IDENTIFIER=$CIRCLE_WORKFLOW_JOB_ID
scripts/start-sauce-connect sauce-pidfile
set +o errexit
scripts/run-all-browsers
exitcode=$?
set -o errexit
scripts/stop-sauce-connect
scripts/stop-sauce-connect $(cat sauce-pidfile)
exit $exitcode
workflows:
@@ -104,56 +94,49 @@ workflows:
push:
jobs:
- build:
executor: node24
name: build_node_24
- 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: node24
name: test_parallel_node_24
- build_node_18
- test_node:
executor: node16
name: test_node_16
requires:
- build_node_24
- 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
- test_parallel:
executor: node18
name: test_parallel_node_18
- build_node_12_latest
- test_node:
executor: node12_17
name: test_node_12_17
requires:
- build_node_18
- 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

@@ -3,6 +3,6 @@ charset = utf-8
end_of_line = lf
insert_final_newline = true
[*.{js,mjs,json,sh,yml}]
[*.{js, json, sh, yml}]
indent_style = space
indent_size = 2

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:
@@ -13,7 +13,7 @@ body:
Check the [FAQ](https://jasmine.github.io/pages/faq.html) and any other relevant [documentation](https://jasmine.github.io/pages/docs_home.html) to see if your issue has already been addressed.
## Special troubleshooting steps for asynchronous scenarios
If the issue has to do with testing asynchronous code, please read the [async tutorial](https://jasmine.github.io/tutorials/async) and the [async section of the FAQ](https://jasmine.github.io/pages/faq.html#async). In particular, check for the following common errors:
If the issue has to do with testing asynchronous code, please read the [async tutorial](https://jasmine.github.io/tutorials/async) and the async section of the FAQ. In particular, check for the following common errors:
* Are you trying to write a synchronous test for asynchronous code?
* Does the test signal completion before the code under test finishes?
@@ -22,22 +22,19 @@ body:
## Try the latest version of Jasmine
If at all possible, upgrade to the latest versions of `jasmine-core` and any other relevant packages (e.g. `jasmine`, `jasmine-browser-runner`). If you can't do that, please check the [release notes](https://github.com/jasmine/jasmine/tree/main/release_notes) for all newer versions to make sure that the bug hasn't already been fixed.
## Explain how to reproduce the bug
**Working steps to reproduce are required for all bug reports.** Please help us help you by creating complete but minimal instructions for reproducing the bug.
The steps to reproduce could be:
* A code snippet that reproduces the problem when run by itself in a newly generated empty `jasmine` or `jasmine-browser-runner` project, or in the standalone distribution.
* A set of steps that reproduce the problem when followed exactly as they're written in an empty directory.
* A link to a Git repository or zip/tar file containing a [minimal, reproducible example](https://stackoverflow.com/help/minimal-reproducible-example). This option is required for all bugs that can only be reproduced with third-party libraries, including Angular and Karma.
## Put together a [minimal, reproducible example](https://stackoverflow.com/help/minimal-reproducible-example)
Please help us help you by creating a minimal but complete setup that demonstrates the problem. Remove any code and libraries that aren't absolutely necessary, but make sure it doesn't depend on any code you haven't included. In many cases a simple code snippet is enough. In cases involving external libraries, *especially* Karma or Angular, we're likely to need a runable Git repository or jsbin/stackblitz/etc.
**If we can't reproduce it, we can't fix it. Bug reports without a minimal, reproducible example are very likely to be closed.**
Please **test your steps** by starting with an empty directory and following them exactly as they're written. Bug reports with steps to reproduce that are unclear, don't work, or include an unreasonable amount of extraneous code will likely be closed.
- type: textarea
id: steps-to-reproduce
attributes:
label: Steps to Reproduce
placeholder: |
Example steps:
1. Paste the example code below into `mySpec.js`.
2. Run `npx jasmine@<some version> mySpec.js`
validations:
required: true
- type: textarea
@@ -54,6 +51,14 @@ body:
description: What happened instead?
validations:
required: true
- type: textarea
id: code-sample
attributes:
label: Example code that reproduces the problem
description: Please include either a code snippet that reproduces the problem or a link to a repository or jsbin/stackblitz/etc containing a minimal, reproducible example as described above.
render: JavaScript
validations:
required: true
- type: textarea
id: possible-solution
attributes:
@@ -77,11 +82,10 @@ body:
placeholder: |
jasmine-browser-runner 1.2.0
fancy-reporter 132.4.8
- type: input
id: browser-or-node-version
attributes:
label: Node.js and/or browser version
label: Node.js or browser version
placeholder: E.g. "node 16.2.0" or "Safari 15"
validations:
required: true
@@ -92,4 +96,3 @@ body:
placeholder: E.g. "Windows 10", "MacOS 12.5", "MCC Interim Linux 0.99.p8"
validations:
required: true

View File

@@ -1,8 +1,5 @@
blank_issues_enabled: false
contact_links:
- name: Questions, requests for help, etc
url: https://github.com/jasmine/jasmine/discussions/new/choose
about: Please start a discussion.
- name: Issues with the `jasmine` CLI
url: https://github.com/jasmine/jasmine-npm/issues
about: Please create issues related to the `jasmine` package in its repository.

View File

@@ -0,0 +1,73 @@
name: Question or Support Request
description: I need help using Jasmine
labels: ["question"]
body:
- type: markdown
attributes:
value: |
Jasmine is supported by volunteers working in their free time. Although we're generally willing to help, we're going to ask you to put in some effort first to help us help you.
## Troubleshooting
Please take the time to rule out problems with your code or third party libraries before opening an issue. If you're running into an error, try to determine whether the error is coming from Jasmine, another library, or your own code.
Check the [FAQ](https://jasmine.github.io/pages/faq.html) and any other relevant [documentation](https://jasmine.github.io/pages/docs_home.html) to see if your question has already been answered. Consider searching [Stack Overflow](https://stackoverflow.com/questions/tagged/jasmine) and past issues in this repository for related questions as well.
## Special troubleshooting steps for asynchronous scenarios
If the issue has to do with testing asynchronous code, please read the [async tutorial](https://jasmine.github.io/tutorials/async) and the async section of the FAQ. In particular, check for the following common errors:
* Are you trying to write a synchronous test for asynchronous code?
* Does the test signal completion before the code under test finishes?
* Do expectations run before the code that they're trying to verify?
## Consider asking Angular questions in an Angular forum
Questions like "how do I test this Angular service" are mostly about Angular, not Jasmine. You'll likely get better responses in an Angular forum. Here's a rule of thumb: If you can't demonstrate the problem without Angular, you probably have an Angular question.
## Try the latest version of Jasmine
If at all possible, upgrade to the latest versions of `jasmine-core` and any other relevant packages (e.g. `jasmine`, `jasmine-browser-runner`).
## Put together a [minimal, reproducible example](https://stackoverflow.com/help/minimal-reproducible-example)
Please help us help you by creating a minimal but complete setup that demonstrates the problem. Remove any code and libraries that aren't absolutely necessary, but make sure it doesn't depend on any code you haven't included. In many cases a simple code snippet is enough. In cases involving external libraries, *especially* Karma or Angular, we're likely to need a runable Git repository or jsbin/stackblitz/etc.
- type: textarea
id: question
attributes:
label: Your question
description: Clearly describe what you'd like help with.
validations:
required: true
- type: textarea
id: code-sample
attributes:
label: Example code that demonstrates the problem
description: Please include either a code snippet that demonstrates the problem or a link to a repository or jsbin/stackblitz/etc containing a minimal, reproducible example as described above.
render: JavaScript
validations:
required: true
- type: input
id: jasmine-core-version
attributes:
label: jasmine-core version
validations:
required: true
- type: textarea
id: other-versions
attributes:
label: Versions of other relevant packages
placeholder: |
jasmine-browser-runner 1.2.0
fancy-reporter 132.4.8
- type: input
id: browser-or-node-version
attributes:
label: Node.js or browser version
placeholder: E.g. "node 16.2.0" or "Safari 15"
validations:
required: true
- type: input
id: os
attributes:
label: Operating System
placeholder: E.g. "Windows 10", "MacOS 12.5", "MCC Interim Linux 0.99.p8"
validations:
required: true

View File

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

66
Gruntfile.js Normal file
View File

@@ -0,0 +1,66 @@
module.exports = function(grunt) {
var pkg = require("./package.json");
global.jasmineVersion = pkg.version;
grunt.initConfig({
pkg: pkg,
concat: require('./grunt/config/concat.js'),
sass: require('./grunt/config/sass.js'),
compress: require('./grunt/config/compress.js'),
cssUrlEmbed: require('./grunt/config/cssUrlEmbed.js')
});
require('load-grunt-tasks')(grunt);
grunt.loadTasks('grunt/tasks');
grunt.registerTask('default', ['sass:dist', "cssUrlEmbed"]);
grunt.registerTask('buildDistribution',
'Builds and lints jasmine.js, jasmine-html.js, jasmine.css',
[
'sass:dist',
"cssUrlEmbed",
'concat'
]
);
grunt.registerTask("execSpecsInNode",
"Run Jasmine core specs in Node.js",
function() {
verifyNoGlobals(() => require('./lib/jasmine-core.js').noGlobals());
const done = this.async(),
Jasmine = require('jasmine'),
jasmineCore = require('./lib/jasmine-core.js'),
jasmine = new Jasmine({jasmineCore: jasmineCore});
jasmine.loadConfigFile('./spec/support/jasmine.json');
jasmine.exitOnCompletion = false;
jasmine.execute().then(
result => done(result.overallStatus === 'passed'),
err => {
console.error(err);
exit(1);
}
);
}
);
grunt.registerTask("execSpecsInNode:performance",
"Run Jasmine performance specs in Node.js",
function() {
require("shelljs").exec("node_modules/.bin/jasmine JASMINE_CONFIG_PATH=spec/support/jasmine-performance.json");
}
);
};
function verifyNoGlobals(fn) {
const initialGlobals = Object.keys(global);
fn();
const extras = Object.keys(global).filter(k => !initialGlobals.includes(k));
if (extras.length !== 0) {
throw new Error('Globals were unexpectedly created: ' + extras.join(', '));
}
}

View File

@@ -1,5 +1,5 @@
Copyright (c) 2008-2019 Pivotal Labs
Copyright (c) 2008-2025 The Jasmine developers
Copyright (c) 2008-2023 The Jasmine developers
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the

View File

@@ -1,10 +1,13 @@
<a name="README"><img src="https://raw.githubusercontent.com/jasmine/jasmine/main/images/jasmine-horizontal.svg" width="400px" alt="Jasmine"></a>
<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,22 +30,18 @@ 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.5+*, 20, 22, 24 |
| Safari | 15*, 16*, 17* |
| Chrome | Evergreen |
| Firefox | Evergreen, 102*, 115*, 128 |
| 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.
However, Jasmine isn't tested against them and they aren't actively supported.
\* Supported on a best-effort basis. Support for these versions may be dropped
if it becomes impractical, and bugs affecting only these versions may not be
treated as release blockers.
To find out what environments work with a particular Jasmine release, see the [release notes](https://github.com/jasmine/jasmine/tree/main/release_notes).
## Maintainers
@@ -60,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-2025 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

@@ -41,13 +41,13 @@ When ready to release - specs are all green and the stories are done:
### Build standalone distribution
1. Build the standalone distribution with `npm run buildStandaloneDist`
1. Build the standalone distribution with `grunt buildStandaloneDist`
1. This will generate `dist/jasmine-standalone-<version>.zip`, which you will upload later (see "Finally" below).
### Release the core NPM module
1. `npm login` to save your credentials locally
2. `npm publish .` to publish what's in `package.json`
1. `npm adduser` to save your credentials locally
1. `npm publish .` to publish what's in `package.json`
### Release the docs
@@ -55,6 +55,11 @@ Probably only need to do this when releasing a minor version, and not a patch
version. See [the README file in the docs repo](https://github.com/jasmine/jasmine.github.io/blob/master/README.md)
for instructions.
1. `rake update_edge_jasmine`
1. `npm run jsdoc`
1. `rake release[${version}]` to copy the current edge docs to the new version
1. Commit and push.
### Release the `jasmine` NPM package
See <https://github.com/jasmine/jasmine-npm/blob/main/RELEASE.md>.

View File

@@ -1,56 +0,0 @@
import { defineConfig } from "eslint/config";
import globals from "globals";
import path from "node:path";
import { fileURLToPath } from "node:url";
import js from "@eslint/js";
import { FlatCompat } from "@eslint/eslintrc";
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const compat = new FlatCompat({
baseDirectory: __dirname,
recommendedConfig: js.configs.recommended,
allConfig: js.configs.all
});
export default defineConfig([{
extends: compat.extends("plugin:compat/recommended"),
languageOptions: {
globals: {
...globals.browser,
...globals.node,
},
// 2022 isn't exactly right, but it's the earliest version that allows
// private properties.
ecmaVersion: 2022,
sourceType: "commonjs",
},
rules: {
curly: "error",
quotes: ["error", "single", {
avoidEscape: true,
}],
"no-unused-vars": ["error", {
args: "none",
}],
"no-implicit-globals": "error",
"block-spacing": "error",
"func-call-spacing": ["error", "never"],
"key-spacing": "error",
"no-tabs": "error",
"no-trailing-spaces": "error",
"no-whitespace-before-property": "error",
semi: ["error", "always"],
"space-before-blocks": "error",
"no-eval": "error",
"no-var": "error",
"no-debugger": "error",
"no-console": "error",
},
}]);

57
grunt/config/compress.js Normal file
View File

@@ -0,0 +1,57 @@
var standaloneLibDir = "lib/jasmine-" + jasmineVersion;
function root(path) { return "./" + path; }
function libJasmineCore(path) { return root("lib/jasmine-core/" + path); }
function dist(path) { return root("dist/" + path); }
module.exports = {
standalone: {
options: {
archive: root("dist/jasmine-standalone-" + global.jasmineVersion + ".zip")
},
files: [
{ src: [ root("MIT.LICENSE") ] },
{
src: [ "jasmine_favicon.png"],
dest: standaloneLibDir,
expand: true,
cwd: root("images")
},
{
src: [
"jasmine.js",
"jasmine-html.js",
"jasmine.css"
],
dest: standaloneLibDir,
expand: true,
cwd: libJasmineCore("")
},
{
src: [ "boot0.js", "boot1.js" ],
dest: standaloneLibDir,
expand: true,
cwd: libJasmineCore("")
},
{
src: [ "SpecRunner.html" ],
dest: root(""),
expand: true,
cwd: dist("tmp")
},
{
src: [ "*.js" ],
dest: "src",
expand: true,
cwd: libJasmineCore("example/src/")
},
{
src: [ "*.js" ],
dest: "spec",
expand: true,
cwd: libJasmineCore("example/spec/")
}
]
}
};

60
grunt/config/concat.js Normal file
View File

@@ -0,0 +1,60 @@
var grunt = require('grunt');
function license() {
var currentYear = "" + new Date(Date.now()).getFullYear();
return grunt.template.process(
grunt.file.read("grunt/templates/licenseBanner.js.jst"),
{ data: { currentYear: currentYear}});
}
module.exports = {
'jasmine-html': {
src: [
'src/html/requireHtml.js',
'src/html/HtmlReporter.js',
'src/html/HtmlSpecFilter.js',
'src/html/ResultsNode.js',
'src/html/QueryString.js',
'src/html/**/*.js'
],
dest: 'lib/jasmine-core/jasmine-html.js'
},
jasmine: {
src: [
'src/core/requireCore.js',
'src/core/matchers/requireMatchers.js',
'src/core/base.js',
'src/core/util.js',
'src/core/Spec.js',
'src/core/Order.js',
'src/core/Env.js',
'src/core/JsApiReporter.js',
'src/core/PrettyPrinter',
'src/core/Suite',
'src/core/**/*.js',
'src/version.js'
],
dest: 'lib/jasmine-core/jasmine.js'
},
boot0: {
src: ['src/boot/boot0.js'],
dest: 'lib/jasmine-core/boot0.js'
},
boot1: {
src: ['src/boot/boot1.js'],
dest: 'lib/jasmine-core/boot1.js'
},
nodeBoot: {
src: ['src/boot/node_boot.js'],
dest: 'lib/jasmine-core/node_boot.js'
},
options: {
banner: license(),
process: {
data: {
version: global.jasmineVersion
}
}
}
};

View File

@@ -0,0 +1,7 @@
module.exports = {
encodeWithBaseDir: {
files: {
"lib/jasmine-core/jasmine.css": ["lib/jasmine-core/jasmine.css"]
}
}
};

13
grunt/config/sass.js Normal file
View File

@@ -0,0 +1,13 @@
const sass = require('sass');
module.exports = {
options: {
implementation: sass,
sourceComments: false
},
dist: {
files: {
"lib/jasmine-core/jasmine.css": "src/html/jasmine.scss"
}
}
};

View File

@@ -0,0 +1,31 @@
var grunt = require("grunt");
function standaloneTmpDir(path) { return "dist/tmp/" + path; }
grunt.registerTask("build:compileSpecRunner",
"Processes the spec runner template and writes to a tmp file",
function() {
var runnerHtml = grunt.template.process(
grunt.file.read("grunt/templates/SpecRunner.html.jst"),
{ data: { jasmineVersion: global.jasmineVersion }});
grunt.file.write(standaloneTmpDir("SpecRunner.html"), runnerHtml);
}
);
grunt.registerTask("build:cleanSpecRunner",
"Deletes the tmp spec runner file",
function() {
grunt.file.delete(standaloneTmpDir(""));
}
);
grunt.registerTask("buildStandaloneDist",
"Builds a standalone distribution",
[
"buildDistribution",
"build:compileSpecRunner",
"compress:standalone",
"build:cleanSpecRunner"
]
);

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

@@ -1,6 +1,6 @@
/*
Copyright (c) 2008-2019 Pivotal Labs
Copyright (c) 2008-2025 The Jasmine developers
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
@@ -21,7 +21,6 @@ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/**
This file starts the process of "booting" Jasmine. It initializes Jasmine,
makes its globals available, and creates the env. This file should be loaded

View File

@@ -1,6 +1,6 @@
/*
Copyright (c) 2008-2019 Pivotal Labs
Copyright (c) 2008-2025 The Jasmine developers
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
@@ -21,7 +21,6 @@ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/**
This file finishes 'booting' Jasmine, performing all of the necessary
initialization before executing the loaded environment and all of a project's

View File

@@ -1,6 +1,6 @@
/*
Copyright (c) 2008-2019 Pivotal Labs
Copyright (c) 2008-2025 The Jasmine developers
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
@@ -21,7 +21,6 @@ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
// eslint-disable-next-line no-var
var jasmineRequire = window.jasmineRequire || require('./jasmine.js');
@@ -156,10 +155,8 @@ jasmineRequire.HtmlReporter = function(j$) {
if (noExpectations(result)) {
const noSpecMsg = "Spec '" + result.fullName + "' has no expectations.";
if (result.status === 'failed') {
// eslint-disable-next-line no-console
console.error(noSpecMsg);
} else {
// eslint-disable-next-line no-console
console.warn(noSpecMsg);
}
}
@@ -466,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.9.0",
"version": "4.6.1",
"repository": {
"type": "git",
"url": "https://github.com/jasmine/jasmine.git"
@@ -15,11 +15,9 @@
],
"scripts": {
"posttest": "eslint \"src/**/*.js\" \"spec/**/*.js\" && prettier --check \"src/**/*.js\" \"spec/**/*.js\"",
"test": "node scripts/runSpecsInNode.js",
"test:parallel": "node scripts/runSpecsInParallel.js",
"test": "grunt --stack execSpecsInNode",
"cleanup": "prettier --write \"src/**/*.js\" \"spec/**/*.js\"",
"build": "node scripts/buildDistribution.js",
"buildStandaloneDist": "node scripts/buildStandaloneDist.js",
"build": "grunt buildDistribution",
"serve": "node spec/support/localJasmineBrowser.js",
"serve:performance": "node spec/support/localJasmineBrowser.js jasmine-browser-performance.json",
"ci": "node spec/support/ci.js",
@@ -29,32 +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/eslintrc": "^3.3.1",
"@eslint/js": "^9.24.0",
"archiver": "^7.0.1",
"css-url-embed": "^0.1.0",
"ejs": "^3.1.10",
"eslint": "^9.24.0",
"eslint-plugin-compat": "^6.0.2",
"glob": "^10.2.3",
"globals": "^16.0.0",
"jasmine": "^5.0.0",
"jasmine-browser-runner": "github:jasmine/jasmine-browser-runner",
"jsdom": "^26.0.0",
"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": "^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

@@ -1,39 +0,0 @@
# Jasmine Core 5.4.0 Release Notes
## Changes
* Fixed de-duplication of exception messages containing blank lines on Node and Chrome
This is particularly helpful when reporting testing-library errors, which
have messages that contain blank lines and can be hundreds or even thousands
of lines long.
* Document that the expected and actual properties of expectation results are deprecated
The values of these properties are not reliable in configurations where
reporter messages are JSON serialized. They appear to have been seldom if ever
used. They will be removed in the next major release.
* Added Firefox 128 (current ESR) to supported browsers
## Supported environments
This version has been tested in the following environments.
| Environment | Supported versions |
|-------------------|-------------------------|
| Node | 18, 20, 22 |
| Safari | 15-17 |
| Chrome | 129* |
| Firefox | 102**, 115**, 128, 131* |
| Edge | 129* |
\* Evergreen browser. Each version of Jasmine is tested against the latest
version available at release time.<br>
\** Environments that are past end of life are supported on a best-effort basis.
They may be dropped in a future minor release of Jasmine if continued support
becomes impractical.
------
_Release Notes generated with _[Anchorman](http://github.com/infews/anchorman)_

View File

@@ -1,34 +0,0 @@
# Jasmine Core 5.5.0 Release Notes
## Changes
* Optionally enforce uniqueness of spec and suite names
This is off by default for backwards compatibility but can be enabled
by setting the `forbidDuplicateNames` env config property to true.
Fixes [#1633](https://github.com/jasmine/jasmine/issues/1633).
* Include property value mismatches in diffs even when there are missing or
extra properties
## Supported environments
This version has been tested in the following environments.
| Environment | Supported versions |
|-------------------|-------------------------|
| Node | 18, 20, 22 |
| Safari | 15-17 |
| Chrome | 131* |
| Firefox | 102**, 115**, 128, 132* |
| Edge | 131* |
\* Evergreen browser. Each version of Jasmine is tested against the latest
version available at release time.<br>
\** Environments that are past end of life are supported on a best-effort basis.
They may be dropped in a future minor release of Jasmine if continued support
becomes impractical.
------
_Release Notes generated with _[Anchorman](http://github.com/infews/anchorman)_

View File

@@ -1,51 +0,0 @@
# Jasmine Core 5.6.0 Release Notes
## Changes
* Added [toHaveNoOtherSpyInteractions](https://jasmine.github.io/api/5.6/matchers.html#toHaveNoOtherSpyInteractions) matcher
* Merges [#2051](https://github.com/jasmine/jasmine/pull/2051) from @Eradev
* Fixes [#1991](https://github.com/jasmine/jasmine/issues/1991)
* Added [toBeNullish](https://jasmine.github.io/api/5.6/matchers.html#toBeNullish) matcher
* Merges [#2045](https://github.com/jasmine/jasmine/pull/2045) from @MattMcCherry
* Improved error messages when non-promises are passed to built-in async matchers
* Merges [#2049](https://github.com/jasmine/jasmine/pull/2049) from @andiz2
* Fixes [#2037](https://github.com/jasmine/jasmine/issues/2037)
* Added [toHaveClasses](https://jasmine.github.io/api/5.6/matchers.html#toHaveClasses) matcher
* Merges [#2046](https://github.com/jasmine/jasmine/pull/2046) from @aYorky
## Documentation updates
* Demoted Safari to best-effort support
Due to limited availability of Safari versions for contributors and maintainers
as well as in CI, Safari will be supported on the same best-effort basis as
environments that are past end of life, such as previous Firefox ESR versions.
See [this discussion](https://github.com/jasmine/jasmine/discussions/2050) for
more information about why this change was made and what to expect.
## Supported environments
This version has been tested in the following environments.
| Environment | Supported versions |
|-------------------|-------------------------|
| Node | 18, 20, 22 |
| Safari | 15**, 16**, 17** |
| Chrome | 133* |
| Firefox | 102**, 115**, 128, 135* |
| Edge | 132* |
\* Evergreen browser. Each version of Jasmine is tested against the latest
version available at release time.<br>
\** Supported on a best-effort basis. Support for these versions may be dropped
if it becomes impractical, and bugs affecting only these versions may not be
treated as release blockers.
------
_Release Notes generated with _[Anchorman](http://github.com/infews/anchorman)_

View File

@@ -1,62 +0,0 @@
# Jasmine Core 5.7.0 Release Notes
## New features
* Added [Clock#autoTick](https://jasmine.github.io/api/5.7/Clock.html#autoTick)
to automatically tick the clock asynchronously
* Merges #2042 from @atscott and @stephenfarrar
* Fixes #1725
* Expose [spec path](https://jasmine.github.io/api/5.7/Spec.html#getPath) as an
array of names in addition to the existing concatenated name
This is meant to support tools like IDE integrations that need to filter a run
to an exact set of suites/specs.
## Documentation improvements
* Documented that [SpecResult#filename](https://jasmine.github.io/api/5.7/global.html#SpecResult)
and [SuiteResult#filename](https://jasmine.github.io/api/5.7/global.html#SuiteResult)
are wrong when zone.js is present and in some cases where it/describe/etc are
replaced
* Updated docs for expected and actual properties of
[expectation results](https://jasmine.github.io/api/5.7/global.html#ExpectationResult)
## Internal improvements
* Rewrote the build system to not use Grunt
Although Grunt has served Jasmine well over the years, it was keeping us tied
to an aging and increasingly questionable set of dev dependencies.
* Updated to eslint 9
* Removed mostly-unmaintained dev dependency 'temp'
* Updated most other dev dependencies to latest versions
* Fixed sass deprecation warning
* Updated to Sauce Connect 5
* Made stop-sauce-connect script more robust
## Supported environments
This version has been tested in the following environments.
| Environment | Supported versions |
|-------------------|-------------------------|
| Node | 18**, 20, 22 |
| Safari | 15**, 16**, 17** |
| Chrome | 135* |
| Firefox | 102**, 115**, 128, 137* |
| Edge | 135* |
\* Evergreen browser. Each version of Jasmine is tested against the latest
version available at release time.<br>
\** Supported on a best-effort basis. Support for these versions may be dropped
if it becomes impractical, and bugs affecting only these versions may not be
treated as release blockers.
------
_Release Notes generated with _[Anchorman](http://github.com/infews/anchorman)_

View File

@@ -1,28 +0,0 @@
# Jasmine Core 5.7.1 Release Notes
## Bug fixes
* Ensure that uninstalling the clock also stops auto tick
* Merges #2057 from @atscott
## Supported environments
This version has been tested in the following environments.
| Environment | Supported versions |
|-------------------|-------------------------|
| Node | 18**, 20, 22 |
| Safari | 15**, 16**, 17** |
| Chrome | 136* |
| Firefox | 102**, 115**, 128, 138* |
| Edge | 135* |
\* Evergreen browser. Each version of Jasmine is tested against the latest
version available at release time.<br>
\** Supported on a best-effort basis. Support for these versions may be dropped
if it becomes impractical, and bugs affecting only these versions may not be
treated as release blockers.
------
_Release Notes generated with _[Anchorman](http://github.com/infews/anchorman)_

View File

@@ -1,44 +0,0 @@
# Jasmine Core 5.8.0 Release Notes
## New Features
* Allow passing a function to `saveArgumentsByValue` to customize how arguments
are saved
* Merges [#2062](https://github.com/jasmine/jasmine/pull/2062) from @evanwaslh
* Fixes [#1886](https://github.com/jasmine/jasmine/issues/1886)
* Use custom object formatters in spy strategy mismatch errors
* Include function names in pretty printer output
* Improve performance of autoTick in Node
* Merges [#2058](https://github.com/jasmine/jasmine/pull/2058) from @atscott
## Bug Fixes
* Fix diff building when only one side has a custom object formatter
* Fixes [#2061](https://github.com/jasmine/jasmine/issues/2061)
## Documentation improvements
* Added Node 24 to supported environments
## Supported environments
This version has been tested in the following environments.
| Environment | Supported versions |
|-------------------|-------------------------|
| Node | 18**, 20, 22, 24 |
| Safari | 15**, 16**, 17** |
| Chrome | 137* |
| Firefox | 102**, 115**, 128, 139* |
| Edge | 137* |
\* Evergreen browser. Each version of Jasmine is tested against the latest
version available at release time.<br>
\** Supported on a best-effort basis. Support for these versions may be dropped
if it becomes impractical, and bugs affecting only these versions may not be
treated as release blockers.
------
_Release Notes generated with _[Anchorman](http://github.com/infews/anchorman)_

View File

@@ -1,56 +0,0 @@
# Jasmine Core 5.9.0 Release Notes
## Bug fixes
* Avoid generating mock clock timer IDs that conflict with native ones
* Fixes [#2068](https://github.com/jasmine/jasmine/issues/2068)
* Merges [#2069](https://github.com/jasmine/jasmine/pull/2069) from @atscott
## Deprecations and changes to platform support
* Node versions before 18.20.5 are no longer supported
Older 18.x versions might still work, but Jasmine is no longer tested in them
prior to release.
* Document that the filename properties of suite and spec results are deprecated
These properties are incorrect in many configurations. They'll be removed in
the next major release unless there is enough user interest in fixing them.
See <https://github.com/jasmine/jasmine/issues/2065>.
## Internal improvements
* Extensive GlobalErrors refactoring
* Removed many of the error dispatching differences between browsers and Node
* Split into portable and platform-specific parts
* Converted to ES6 classes
* Removed unnecessary errorWithStack helper
Jasmine no longer runs on platforms that create errors without stack traces.
* Removed protections against user code redefining undefined
Jasmine no longer runs on platforms that allow redefining undefined.
* Removed rimraf and shelljs dev dependencies
## Supported environments
This version has been tested in the following environments.
| Environment | Supported versions |
|-------------------|-------------------------|
| Node | 18.20.5**, 20, 22, 24 |
| Safari | 15**, 16**, 17** |
| Chrome | 138* |
| Firefox | 102**, 115**, 128, 140* |
| Edge | 138* |
\* Evergreen browser. Each version of Jasmine is tested against the latest
version available at release time.<br>
\** Supported on a best-effort basis. Support for these versions may be dropped
if it becomes impractical, and bugs affecting only these versions may not be
treated as release blockers.
------
_Release Notes generated with _[Anchorman](http://github.com/infews/anchorman)_

View File

@@ -1,3 +0,0 @@
const buildDistribution = require('./lib/buildDistribution');
buildDistribution();

View File

@@ -1,86 +0,0 @@
const fs = require('fs');
const os = require('os');
const path = require('path');
const glob = require('glob');
const ejs = require('ejs');
const archiver = require('archiver');
const buildDistribution = require('./lib/buildDistribution');
const prefix = path.join(os.tmpdir(), 'jasmine-build-standalone');
const tmpDir = fs.mkdtempSync(prefix);
buildStandaloneDist().finally(function() {
fs.rmSync(tmpDir, { recursive: true });
});
async function buildStandaloneDist() {
buildDistribution();
const pkg = JSON.parse(fs.readFileSync('package.json'));
compileSpecRunner(pkg.version);
await zipStandaloneDist(pkg.version);
}
function compileSpecRunner(jasmineVersion) {
const template = fs.readFileSync('src/SpecRunner.html.ejs',
{encoding: 'utf8'});
const runnerHtml = ejs.render(template, { jasmineVersion });
fs.writeFileSync(path.join(tmpDir, 'SpecRunner.html'), runnerHtml,
{encoding: 'utf8'});
}
async function zipStandaloneDist(jasmineVersion) {
const fileGroups = [
{
src: [
'LICENSE',
path.join(tmpDir, 'SpecRunner.html'),
]
},
{
src: [
'images/jasmine_favicon.png',
'lib/jasmine-core/jasmine.js',
'lib/jasmine-core/jasmine-html.js',
'lib/jasmine-core/jasmine.css',
'lib/jasmine-core/boot0.js',
'lib/jasmine-core/boot1.js',
],
destDir: 'lib/jasmine-' + jasmineVersion
},
{
src: glob.sync('lib/jasmine-core/example/src/*.js'),
destDir: 'src'
},
{
src: glob.sync('lib/jasmine-core/example/spec/*.js'),
destDir: 'spec'
}
];
const destPath = `./dist/jasmine-standalone-${jasmineVersion}.zip`;
const output = fs.createWriteStream(destPath);
const archive = archiver('zip');
const done = new Promise(function(resolve, reject) {
output.on('close', resolve);
archive.on('warning', reject);
archive.on('error', reject);
});
archive.pipe(output);
for (const group of fileGroups) {
for (const srcPath of group.src) {
let destPath = path.basename(srcPath);
if (group.destDir) {
destPath = `${group.destDir}/${destPath}`;
}
archive.file(srcPath, {name: destPath});
}
}
archive.finalize();
await done;
}

View File

@@ -1,129 +0,0 @@
const fs = require('fs');
const sass = require('sass');
const glob = require('glob');
const ejs = require('ejs');
const cssUrlEmbed = require('css-url-embed');
function buildDistribution() {
compileSass();
embedCssAssets();
concatFiles();
}
function embedCssAssets() {
const cssPath = 'lib/jasmine-core/jasmine.css';
cssUrlEmbed.processFile(cssPath, cssPath, function(filePath) {
if (filePath.endsWith('.png')) {
return 'image/png';
} else if (filePath.endsWith('.svg')) {
return 'image/svg+xml';
} else {
throw new Error(`Don't know MIME type for file: ${filePath}`);
}
});
}
function compileSass() {
const output = sass.compile('src/html/jasmine.scss');
fs.writeFileSync('lib/jasmine-core/jasmine.css', output.css,
{encoding: 'utf8'});
}
function concatFiles() {
const pkg = JSON.parse(fs.readFileSync('package.json'));
const configs = [
{
src: [
'src/html/requireHtml.js',
'src/html/HtmlReporter.js',
'src/html/HtmlSpecFilter.js',
'src/html/ResultsNode.js',
'src/html/QueryString.js',
'src/html/**/*.js'
],
dest: 'lib/jasmine-core/jasmine-html.js',
},
{
dest: 'lib/jasmine-core/jasmine.js',
src: [
'src/core/requireCore.js',
'src/core/matchers/requireMatchers.js',
'src/core/base.js',
'src/core/util.js',
'src/core/Spec.js',
'src/core/Order.js',
'src/core/Env.js',
'src/core/JsApiReporter.js',
'src/core/PrettyPrinter',
'src/core/Suite',
'src/core/**/*.js',
{
template: 'src/version.js',
data: {version: pkg.version}
},
],
},
{
dest: 'lib/jasmine-core/boot0.js',
src: ['src/boot/boot0.js'],
},
{
dest: 'lib/jasmine-core/boot1.js',
src: ['src/boot/boot1.js'],
}
];
const licenseBanner = {
template: 'src/licenseBanner.js.ejs',
data: {currentYear: new Date(Date.now()).getFullYear()}
};
for (const {src, dest} of configs) {
src.unshift(licenseBanner);
function expand(srcListEntry) {
if (typeof srcListEntry === 'object') {
return srcListEntry;
}
return glob.sync(srcListEntry)
.sort(function (a, b) {
// Match the sort order of previous build tools, so that the
// output is the same.
a = a.toLowerCase();
b = b.toLowerCase();
if (a < b) {
return -1;
} else if (a === b) {
return 0;
} else {
return 1;
}
});
}
const srcs = src.flatMap(expand);
const seen = new Set();
const chunks = [];
for (const s of srcs) {
let content;
if (!seen.has(s)) {
if (s.template) {
const template = fs.readFileSync(s.template, {encoding: 'utf8'});
content = ejs.render(template, s.data);
} else {
content = fs.readFileSync(s, {encoding: 'utf8'});
}
chunks.push(content);
seen.add(s);
}
}
fs.writeFileSync(dest, chunks.join('\n'), {encoding: 'utf8'});
}
}
module.exports = buildDistribution;

View File

@@ -25,15 +25,22 @@ run_browser() {
passfile=`mktemp -t jasmine-results.XXXXXX` || exit 1
failfile=`mktemp -t jasmine-results.XXXXXX` || exit 1
run_browser chrome latest
# As of 2023-09-30, Sauce Connect doesn't work with the latest Chrome version
# on the default Linux. Run on Mac OS instead. The OS specification may need to
# be updated or removed when new Chrome versions stop being available on Mac OS
# 12, although historically this has taken several major OS versions.
# See <https://saucelabs.com/products/supported-browsers-devices>.
# On Saucelabs, the test suite frequently runs ~30s slower on Mac OS than it
# does on Linux, so it's probably worth removing the OS specification once Sauce
# Connect works with Chrome latest on Linux again.
run_browser chrome latest "macOS 12"
run_browser firefox latest
run_browser firefox 128
run_browser firefox 115
run_browser firefox 102
run_browser safari 17
run_browser firefox 91
run_browser safari 16
run_browser safari 15
run_browser safari 14
run_browser MicrosoftEdge latest
echo

View File

@@ -1,28 +0,0 @@
verifyNoGlobals(() => require('../lib/jasmine-core.js').noGlobals());
const Jasmine = require('jasmine');
const jasmineCore = require('../lib/jasmine-core.js');
const runner = new Jasmine({jasmineCore: jasmineCore});
runner.loadConfigFile('./spec/support/jasmine.json');
runner.exitOnCompletion = false;
runner.execute()
.then(
result => result.overallStatus === 'passed',
err => {
console.error(err);
return false;
}
)
.then(ok => process.exit(ok ? 0 : 1));
function verifyNoGlobals(fn) {
const initialGlobals = Object.keys(global);
fn();
const extras = Object.keys(global).filter(k => !initialGlobals.includes(k));
if (extras.length !== 0) {
throw new Error('Globals were unexpectedly created: ' + extras.join(', '));
}
}

View File

@@ -1,28 +0,0 @@
const ParallelRunner = require('jasmine/parallel');
const jasmineCore = require('../lib/jasmine-core.js');
let numWorkers = require('os').cpus().length;
if (process.env['CIRCLECI']) {
// On Circle CI, the above gives the number of CPU cores on the host
// computer, which is unrelated to the resources actually available
// to the container. 2 workers gives peak performance with our current
// configuration, but 4 might increase the odds of discovering any
// parallel-specific bugs.
numWorkers = 4;
}
const runner = new ParallelRunner({jasmineCore, numWorkers});
runner.loadConfigFile('./spec/support/jasmine.json')
.then(() => {
runner.exitOnCompletion = false;
return runner.execute();
})
.then(
jasmineDoneInfo => jasmineDoneInfo.overallStatus === 'passed',
err => {
console.error(err);
return false;
}
)
.then(ok => process.exit(ok ? 0 : 1));

View File

@@ -2,19 +2,32 @@
set -o errexit
set -o pipefail
if [ -z "$SAUCE_TUNNEL_NAME" ]; then
echo "SAUCE_TUNNEL_NAME must be set" 1>&2
exit 1
if [ $# -gt 1 -o "$1" = "--help" ]; then
echo "Usage: $0 [pidfile]" 1>&2
exit
fi
if [ -z "$1" ]; then
pidfile=`mktemp`
else
pidfile="$1"
fi
outfile=`mktemp`
echo "Starting Sauce Connect"
sc legacy --proxy-localhost --tunnel-domains localhost --region us-west \
-u "$SAUCE_USERNAME" -k "$SAUCE_ACCESS_KEY" \
-X 4445 -i "$SAUCE_TUNNEL_NAME" 2>&1 | tee "$outfile" &
if [ -z "$SAUCE_TUNNEL_IDENTIFIER" ]; then
sc -u "$SAUCE_USERNAME" -k "$SAUCE_ACCESS_KEY" -X 4445 --pidfile "$pidfile" 2>&1 | tee "$outfile" &
else
sc -u "$SAUCE_USERNAME" -k "$SAUCE_ACCESS_KEY" -X 4445 --pidfile "$pidfile" -i "$SAUCE_TUNNEL_IDENTIFIER" 2>&1 | tee "$outfile" &
fi
while ! fgrep "Sauce Connect is up, you may start your tests" "$outfile" > /dev/null; do
while ! fgrep "Sauce Connect is up, you may start your tests." "$outfile" > /dev/null; do
sleep 1
if ! ps -p $(cat "$pidfile") > /dev/null; then
echo "Sauce Connect exited"
exit 1
fi
done
if ! nc -z localhost 4445; then

View File

@@ -2,38 +2,32 @@
set -o errexit
set -o pipefail
echo "Stopping Sauce Connect"
# Sauce Connect 4 docs said that we can just kill -9 it if we don't care about
# failing any ongoing sessions. In practice, that sometimes worked but usually
# leaked a tunnel so badly that you couldn't even stop it from the web UI.
#
# Sauce Connect 5 appears to be *much* more prone to hung jobs. Hung jobs have
# a shutdown deadline of *three hours*, however, they appear to usually exit
# within a minute or so. Unfortunately the only thing the Sauce Connect 5 docs
# say about shutdown is that "you can stop your tunnel from the terminal where
# Sauce Connect is running by entering Ctrl+C". Nothing is said about what to
# do if Sauce Connect doesn't exit on it own or about non-interactive usage.
#
# So we do our best to be well-behaved without assuming that Sauce Connect
# always is: send it the same signal that it would get if an interactive user
# hit ctrl-c, wait a while for it to exit, then give up so that the CI task
# doesn't keep running indefinitely.
if ! pkill -INT '^sc$'; then
echo "sc does not appear to be running" 1>&2
exit 1
if [ -z "$1" ]; then
echo "Usage: $0 sauce-connect-pid" 1>&2
exit
fi
pid="$1"
echo "PID: $pid"
echo "Stopping Sauce Connect"
# Sauce Connect docs say that we can just kill -9 it if we don't care about
# failing any ongoing sessions. In practice, that sometimes works but usually
# leaks a tunnel so badly that you can't even stop it from the web UI.
# Instead of doing that, we give Sauce Connect some time to shut down
# gracefully and then give up.
kill -INT $pid
# Wait up to 2 minutes, then give up if it's still running
n=0
while [ $n -lt 120 ] && pgrep '^sc$' > /dev/null; do
while [ $n -lt 120 ] && ps -p $pid > /dev/null; do
sleep 1
pkill -INT '^sc$' || true
kill -INT $pid 2> /dev/null || true
n=$(($n + 1))
done
if pgrep '^sc$' > /dev/null; then
if ps -p $pid > /dev/null; then
echo "Could not shut down Sauce Connect"
exit 1
fi
exit $exitcode

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,
@@ -277,18 +277,23 @@ describe('AsyncExpectation', function() {
it('reports a passing result to the spec when the comparison passes', function() {
const matchers = {
toFoo: function() {
return {
compare: function() {
return Promise.resolve({ pass: true });
}
};
}
};
const matchersUtil = {
buildFailureMessage: jasmine.createSpy('buildFailureMessage')
};
const addExpectationResult = jasmine.createSpy('addExpectationResult');
toFoo: function() {
return {
compare: function() {
return Promise.resolve({ pass: true });
}
};
}
},
matchersUtil = {
buildFailureMessage: jasmine.createSpy('buildFailureMessage')
},
addExpectationResult = jasmine.createSpy('addExpectationResult'),
errorWithStack = new Error('errorWithStack');
spyOn(jasmineUnderTest.util, 'errorWithStack').and.returnValue(
errorWithStack
);
const expectation = jasmineUnderTest.Expectation.asyncFactory({
customAsyncMatchers: matchers,
@@ -305,7 +310,7 @@ describe('AsyncExpectation', function() {
error: undefined,
expected: 'hello',
actual: 'an actual',
errorForStack: jasmine.any(Error)
errorForStack: errorWithStack
});
});
});
@@ -324,8 +329,13 @@ describe('AsyncExpectation', function() {
buildFailureMessage: function() {
return '';
}
};
const addExpectationResult = jasmine.createSpy('addExpectationResult');
},
addExpectationResult = jasmine.createSpy('addExpectationResult'),
errorWithStack = new Error('errorWithStack');
spyOn(jasmineUnderTest.util, 'errorWithStack').and.returnValue(
errorWithStack
);
const expectation = jasmineUnderTest.Expectation.asyncFactory({
customAsyncMatchers: matchers,
@@ -342,25 +352,30 @@ describe('AsyncExpectation', function() {
actual: 'an actual',
message: '',
error: undefined,
errorForStack: jasmine.any(Error)
errorForStack: errorWithStack
});
});
});
it('reports a failing result and a custom fail message to the spec when the comparison fails', function() {
const matchers = {
toFoo: function() {
return {
compare: function() {
return Promise.resolve({
pass: false,
message: 'I am a custom message'
});
}
};
}
};
const addExpectationResult = jasmine.createSpy('addExpectationResult');
toFoo: function() {
return {
compare: function() {
return Promise.resolve({
pass: false,
message: 'I am a custom message'
});
}
};
}
},
addExpectationResult = jasmine.createSpy('addExpectationResult'),
errorWithStack = new Error('errorWithStack');
spyOn(jasmineUnderTest.util, 'errorWithStack').and.returnValue(
errorWithStack
);
const expectation = jasmineUnderTest.Expectation.asyncFactory({
actual: 'an actual',
@@ -376,27 +391,32 @@ describe('AsyncExpectation', function() {
actual: 'an actual',
message: 'I am a custom message',
error: undefined,
errorForStack: jasmine.any(Error)
errorForStack: errorWithStack
});
});
});
it('reports a failing result with a custom fail message function to the spec when the comparison fails', function() {
const matchers = {
toFoo: function() {
return {
compare: function() {
return Promise.resolve({
pass: false,
message: function() {
return 'I am a custom message';
}
});
}
};
}
};
const addExpectationResult = jasmine.createSpy('addExpectationResult');
toFoo: function() {
return {
compare: function() {
return Promise.resolve({
pass: false,
message: function() {
return 'I am a custom message';
}
});
}
};
}
},
addExpectationResult = jasmine.createSpy('addExpectationResult'),
errorWithStack = new Error('errorWithStack');
spyOn(jasmineUnderTest.util, 'errorWithStack').and.returnValue(
errorWithStack
);
const expectation = jasmineUnderTest.Expectation.asyncFactory({
customAsyncMatchers: matchers,
@@ -412,23 +432,28 @@ describe('AsyncExpectation', function() {
actual: 'an actual',
message: 'I am a custom message',
error: undefined,
errorForStack: jasmine.any(Error)
errorForStack: errorWithStack
});
});
});
it('reports a passing result to the spec when the comparison fails for a negative expectation', function() {
const matchers = {
toFoo: function() {
return {
compare: function() {
return Promise.resolve({ pass: false });
}
};
}
};
const addExpectationResult = jasmine.createSpy('addExpectationResult');
const actual = 'an actual';
toFoo: function() {
return {
compare: function() {
return Promise.resolve({ pass: false });
}
};
}
},
addExpectationResult = jasmine.createSpy('addExpectationResult'),
actual = 'an actual',
errorWithStack = new Error('errorWithStack');
spyOn(jasmineUnderTest.util, 'errorWithStack').and.returnValue(
errorWithStack
);
const expectation = jasmineUnderTest.Expectation.asyncFactory({
customAsyncMatchers: matchers,
@@ -444,7 +469,7 @@ describe('AsyncExpectation', function() {
error: undefined,
expected: 'hello',
actual: actual,
errorForStack: jasmine.any(Error)
errorForStack: errorWithStack
});
});
});
@@ -463,9 +488,14 @@ describe('AsyncExpectation', function() {
buildFailureMessage: function() {
return 'default message';
}
};
const addExpectationResult = jasmine.createSpy('addExpectationResult');
const actual = 'an actual';
},
addExpectationResult = jasmine.createSpy('addExpectationResult'),
actual = 'an actual',
errorWithStack = new Error('errorWithStack');
spyOn(jasmineUnderTest.util, 'errorWithStack').and.returnValue(
errorWithStack
);
const expectation = jasmineUnderTest.Expectation.asyncFactory({
customAsyncMatchers: matchers,
@@ -482,26 +512,31 @@ describe('AsyncExpectation', function() {
actual: actual,
message: 'default message',
error: undefined,
errorForStack: jasmine.any(Error)
errorForStack: errorWithStack
});
});
});
it('reports a failing result and a custom fail message to the spec when the comparison passes for a negative expectation', function() {
const matchers = {
toFoo: function() {
return {
compare: function() {
return Promise.resolve({
pass: true,
message: 'I am a custom message'
});
}
};
}
};
const addExpectationResult = jasmine.createSpy('addExpectationResult');
const actual = 'an actual';
toFoo: function() {
return {
compare: function() {
return Promise.resolve({
pass: true,
message: 'I am a custom message'
});
}
};
}
},
addExpectationResult = jasmine.createSpy('addExpectationResult'),
actual = 'an actual',
errorWithStack = new Error('errorWithStack');
spyOn(jasmineUnderTest.util, 'errorWithStack').and.returnValue(
errorWithStack
);
const expectation = jasmineUnderTest.Expectation.asyncFactory({
customAsyncMatchers: matchers,
@@ -517,26 +552,31 @@ describe('AsyncExpectation', function() {
actual: actual,
message: 'I am a custom message',
error: undefined,
errorForStack: jasmine.any(Error)
errorForStack: errorWithStack
});
});
});
it("reports a passing result to the spec when the 'not' comparison passes, given a negativeCompare", function() {
const matchers = {
toFoo: function() {
return {
compare: function() {
return Promise.resolve({ pass: true });
},
negativeCompare: function() {
return Promise.resolve({ pass: true });
}
};
}
};
const addExpectationResult = jasmine.createSpy('addExpectationResult');
const actual = 'an actual';
toFoo: function() {
return {
compare: function() {
return Promise.resolve({ pass: true });
},
negativeCompare: function() {
return Promise.resolve({ pass: true });
}
};
}
},
addExpectationResult = jasmine.createSpy('addExpectationResult'),
actual = 'an actual',
errorWithStack = new Error('errorWithStack');
spyOn(jasmineUnderTest.util, 'errorWithStack').and.returnValue(
errorWithStack
);
const expectation = jasmineUnderTest.Expectation.asyncFactory({
customAsyncMatchers: matchers,
@@ -552,29 +592,34 @@ describe('AsyncExpectation', function() {
actual: actual,
message: '',
error: undefined,
errorForStack: jasmine.any(Error)
errorForStack: errorWithStack
});
});
});
it("reports a failing result and a custom fail message to the spec when the 'not' comparison fails, given a negativeCompare", function() {
const matchers = {
toFoo: function() {
return {
compare: function() {
return Promise.resolve({ pass: true });
},
negativeCompare: function() {
return Promise.resolve({
pass: false,
message: "I'm a custom message"
});
}
};
}
};
const addExpectationResult = jasmine.createSpy('addExpectationResult');
const actual = 'an actual';
toFoo: function() {
return {
compare: function() {
return Promise.resolve({ pass: true });
},
negativeCompare: function() {
return Promise.resolve({
pass: false,
message: "I'm a custom message"
});
}
};
}
},
addExpectationResult = jasmine.createSpy('addExpectationResult'),
actual = 'an actual',
errorWithStack = new Error('errorWithStack');
spyOn(jasmineUnderTest.util, 'errorWithStack').and.returnValue(
errorWithStack
);
const expectation = jasmineUnderTest.Expectation.asyncFactory({
customAsyncMatchers: matchers,
@@ -590,7 +635,7 @@ describe('AsyncExpectation', function() {
actual: actual,
message: "I'm a custom message",
error: undefined,
errorForStack: jasmine.any(Error)
errorForStack: errorWithStack
});
});
});
@@ -598,19 +643,24 @@ describe('AsyncExpectation', function() {
it('reports errorWithStack when a custom error message is returned', function() {
const customError = new Error('I am a custom error');
const matchers = {
toFoo: function() {
return {
compare: function() {
return Promise.resolve({
pass: false,
message: 'I am a custom message',
error: customError
});
}
};
}
};
const addExpectationResult = jasmine.createSpy('addExpectationResult');
toFoo: function() {
return {
compare: function() {
return Promise.resolve({
pass: false,
message: 'I am a custom message',
error: customError
});
}
};
}
},
addExpectationResult = jasmine.createSpy('addExpectationResult'),
errorWithStack = new Error('errorWithStack');
spyOn(jasmineUnderTest.util, 'errorWithStack').and.returnValue(
errorWithStack
);
const expectation = jasmineUnderTest.Expectation.asyncFactory({
actual: 'an actual',
@@ -626,25 +676,30 @@ describe('AsyncExpectation', function() {
actual: 'an actual',
message: 'I am a custom message',
error: undefined,
errorForStack: jasmine.any(Error)
errorForStack: errorWithStack
});
});
});
it("reports a custom message to the spec when a 'not' comparison fails", function() {
const matchers = {
toFoo: function() {
return {
compare: function() {
return Promise.resolve({
pass: true,
message: 'I am a custom message'
});
}
};
}
};
const addExpectationResult = jasmine.createSpy('addExpectationResult');
toFoo: function() {
return {
compare: function() {
return Promise.resolve({
pass: true,
message: 'I am a custom message'
});
}
};
}
},
addExpectationResult = jasmine.createSpy('addExpectationResult'),
errorWithStack = new Error('errorWithStack');
spyOn(jasmineUnderTest.util, 'errorWithStack').and.returnValue(
errorWithStack
);
const expectation = jasmineUnderTest.Expectation.asyncFactory({
actual: 'an actual',
@@ -660,27 +715,32 @@ describe('AsyncExpectation', function() {
actual: 'an actual',
message: 'I am a custom message',
error: undefined,
errorForStack: jasmine.any(Error)
errorForStack: errorWithStack
});
});
});
it("reports a custom message func to the spec when a 'not' comparison fails", function() {
const matchers = {
toFoo: function() {
return {
compare: function() {
return Promise.resolve({
pass: true,
message: function() {
return 'I am a custom message';
}
});
}
};
}
};
const addExpectationResult = jasmine.createSpy('addExpectationResult');
toFoo: function() {
return {
compare: function() {
return Promise.resolve({
pass: true,
message: function() {
return 'I am a custom message';
}
});
}
};
}
},
addExpectationResult = jasmine.createSpy('addExpectationResult'),
errorWithStack = new Error('errorWithStack');
spyOn(jasmineUnderTest.util, 'errorWithStack').and.returnValue(
errorWithStack
);
let expectation = jasmineUnderTest.Expectation.asyncFactory({
actual: 'an actual',
@@ -696,7 +756,7 @@ describe('AsyncExpectation', function() {
actual: 'an actual',
message: 'I am a custom message',
error: undefined,
errorForStack: jasmine.any(Error)
errorForStack: errorWithStack
});
});
});

View File

@@ -134,42 +134,6 @@ describe('CallTracker', function() {
expect(callTracker.mostRecent().args[1]).toEqual(arrayArg);
});
it('allows object arguments to be deep cloned', function() {
const callTracker = new jasmineUnderTest.CallTracker();
callTracker.saveArgumentsByValue(args => JSON.parse(JSON.stringify(args)));
const objectArg = { foo: { bar: { baz: ['qux'] } } },
arrayArg = ['foo', 'bar'];
callTracker.track({
object: {},
args: [objectArg, arrayArg, false, undefined, null, NaN, '', 0, 1.0]
});
objectArg.foo.bar.baz.push('quux');
expect(callTracker.mostRecent().args[0]).not.toBe(objectArg);
expect(callTracker.mostRecent().args[0]).not.toEqual(objectArg);
expect(callTracker.mostRecent().args[0]).toEqual({
foo: { bar: { baz: ['qux'] } }
});
expect(callTracker.mostRecent().args[1]).not.toBe(arrayArg);
expect(callTracker.mostRecent().args[1]).toEqual(arrayArg);
});
it('can take any function to transform arguments when saving by value', function() {
const callTracker = new jasmineUnderTest.CallTracker();
callTracker.saveArgumentsByValue(JSON.stringify);
const objectArg = { foo: { bar: { baz: ['qux'] } } },
arrayArg = ['foo', 'bar'],
args = [objectArg, arrayArg, false, undefined, null, NaN, '', 0, 1.0];
callTracker.track({ object: {}, args });
expect(callTracker.mostRecent().args).toEqual(JSON.stringify(args));
});
it('saves primitive arguments by value', function() {
const callTracker = new jasmineUnderTest.CallTracker(),
args = [undefined, null, false, '', /\s/, 0, 1.2, NaN];

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

@@ -687,159 +687,6 @@ describe('Clock (acceptance)', function() {
expect(recurring1.calls.count()).toBe(4);
});
describe('auto tick mode', () => {
let delayedFunctionScheduler;
let mockDate;
let clock;
beforeEach(() => {
delayedFunctionScheduler = new jasmineUnderTest.DelayedFunctionScheduler();
mockDate = {
install: function() {},
tick: function() {},
uninstall: function() {}
};
// window setTimeout to window to make firefox happy
const _setTimeout =
typeof window !== 'undefined' ? setTimeout.bind(window) : setTimeout;
// passing a fake global allows us to preserve the real timing functions for use in tests
const _global = { setTimeout: _setTimeout, setInterval: setInterval };
clock = new jasmineUnderTest.Clock(
_global,
function() {
return delayedFunctionScheduler;
},
mockDate
);
clock.install().autoTick();
});
afterEach(() => {
clock.uninstall();
});
it('flushes microtask queue between macrotasks', async () => {
const log = [];
await new Promise(r => clock.setTimeout(r, 10)).then(() => {
log.push(1);
Promise.resolve().then(() => log.push(2));
Promise.resolve().then(() => log.push(3));
});
await new Promise(r => clock.setTimeout(r, 10)).then(() => {
log.push(4);
Promise.resolve().then(() => log.push(5));
});
expect(log).toEqual([1, 2, 3, 4, 5]);
});
it('can run setTimeouts/setIntervals asynchronously', function() {
const recurring = jasmine.createSpy('recurring'),
fn1 = jasmine.createSpy('fn1'),
fn2 = jasmine.createSpy('fn2'),
fn3 = jasmine.createSpy('fn3');
const intervalId = clock.setInterval(recurring, 50);
// In a microtask, add some timeouts.
Promise.resolve()
.then(function() {
return new Promise(function(resolve) {
clock.setTimeout(resolve, 25);
});
})
.then(function() {
fn1();
return new Promise(function(resolve) {
clock.setTimeout(resolve, 200);
});
})
.then(function() {
fn2();
return new Promise(function(resolve) {
clock.setTimeout(resolve, 100);
});
})
.then(function() {
fn3();
});
expect(recurring).not.toHaveBeenCalled();
expect(fn1).not.toHaveBeenCalled();
expect(fn2).not.toHaveBeenCalled();
expect(fn3).not.toHaveBeenCalled();
return new Promise(resolve => clock.setTimeout(resolve, 50))
.then(function() {
expect(recurring).toHaveBeenCalledTimes(1);
expect(fn1).toHaveBeenCalled();
expect(fn2).not.toHaveBeenCalled();
expect(fn3).not.toHaveBeenCalled();
return new Promise(resolve => clock.setTimeout(resolve, 175));
})
.then(function() {
expect(recurring).toHaveBeenCalledTimes(4);
expect(fn1).toHaveBeenCalled();
expect(fn2).toHaveBeenCalled();
expect(fn3).not.toHaveBeenCalled();
clock.clearInterval(intervalId);
return new Promise(resolve => clock.setTimeout(resolve, 100));
})
.then(function() {
expect(recurring).toHaveBeenCalledTimes(4);
expect(fn1).toHaveBeenCalled();
expect(fn2).toHaveBeenCalled();
expect(fn3).toHaveBeenCalled();
});
});
it('aborts auto ticking when uninstalled, even if installed again synchonrously', async () => {
clock.uninstall();
clock.install();
let resolved = false;
const promise = new Promise(resolve => {
clock.setTimeout(resolve, 1);
}).then(() => {
resolved = true;
});
// wait some real time and verify that the clock did not flush the timer above automatically
await new Promise(resolve => setTimeout(resolve, 2));
expect(resolved).toBe(false);
// enabling auto tick again will flush the timer
clock.autoTick();
await expectAsync(promise).toBeResolved();
});
it('speeds up the execution of the timers in all browsers', async () => {
const startTimeMs = performance.now() / 1000;
await new Promise(resolve => clock.setTimeout(resolve, 5000));
await new Promise(resolve => clock.setTimeout(resolve, 5000));
await new Promise(resolve => clock.setTimeout(resolve, 5000));
await new Promise(resolve => clock.setTimeout(resolve, 5000));
const endTimeMs = performance.now() / 1000;
// Ensure we didn't take 20s to complete the awaits above and, in fact, can do it in a fraction of a second
expect(endTimeMs - startTimeMs).toBeLessThan(100);
});
it('is easy to test async functions with interleaved timers and microtasks', async () => {
async function blackBoxWithLotsOfAsyncStuff() {
await new Promise(r => clock.setTimeout(r, 10));
await Promise.resolve();
await Promise.resolve();
await new Promise(r => clock.setTimeout(r, 20));
await Promise.resolve();
await Promise.resolve();
await Promise.resolve();
return 'done';
}
const result = await blackBoxWithLotsOfAsyncStuff();
expect(result).toBe('done');
});
});
it('can clear a previously set timeout', function() {
const clearedFn = jasmine.createSpy('clearedFn'),
delayedFunctionScheduler = new jasmineUnderTest.DelayedFunctionScheduler(),

View File

@@ -86,13 +86,12 @@ describe('DelayedFunctionScheduler', function() {
it('increments scheduled fns ids unless one is passed', function() {
const scheduler = new jasmineUnderTest.DelayedFunctionScheduler();
const initial = scheduler.scheduleFunction(function() {}, 0);
expect(scheduler.scheduleFunction(function() {}, 0)).toBe(initial + 1);
expect(scheduler.scheduleFunction(function() {}, 0)).toBe(initial + 2);
expect(scheduler.scheduleFunction(function() {}, 0)).toBe(1);
expect(scheduler.scheduleFunction(function() {}, 0)).toBe(2);
expect(scheduler.scheduleFunction(function() {}, 0, [], false, 123)).toBe(
123
);
expect(scheduler.scheduleFunction(function() {}, 0)).toBe(initial + 3);
expect(scheduler.scheduleFunction(function() {}, 0)).toBe(3);
});
it('#removeFunctionWithId removes a previously scheduled function with a given id', function() {
@@ -265,42 +264,6 @@ describe('DelayedFunctionScheduler', function() {
expect(fn).toHaveBeenCalled();
});
it('runs the next scheduled funtion', function() {
const scheduler = new jasmineUnderTest.DelayedFunctionScheduler();
const fn = jasmine.createSpy('fn');
const tickSpy = jasmine.createSpy('tick');
scheduler.scheduleFunction(fn, 10, [], false, 'foo');
expect(fn).not.toHaveBeenCalled();
scheduler.runNextQueuedFunction(tickSpy);
expect(fn).toHaveBeenCalled();
expect(tickSpy).toHaveBeenCalledWith(10);
});
it('runs the only a single scheduled funtion in a time slot', function() {
const scheduler = new jasmineUnderTest.DelayedFunctionScheduler();
const fn1 = jasmine.createSpy('fn');
const fn2 = jasmine.createSpy('fn2');
const tickSpy = jasmine.createSpy('tick');
scheduler.scheduleFunction(fn1, 10, [], false, 'foo1');
scheduler.scheduleFunction(fn2, 10, [], false, 'foo2');
scheduler.runNextQueuedFunction(tickSpy);
expect(fn1).toHaveBeenCalled();
expect(fn2).not.toHaveBeenCalled();
expect(tickSpy).toHaveBeenCalledWith(10);
tickSpy.calls.reset();
scheduler.runNextQueuedFunction(tickSpy);
expect(fn2).toHaveBeenCalled();
expect(tickSpy).toHaveBeenCalledWith(0);
});
it('updates the mockDate per scheduled time', function() {
const scheduler = new jasmineUnderTest.DelayedFunctionScheduler(),
tickDate = jasmine.createSpy('tickDate');
@@ -314,28 +277,6 @@ describe('DelayedFunctionScheduler', function() {
expect(tickDate).toHaveBeenCalledWith(1);
});
it('does not conflict with native timer IDs', function() {
const NODE_JS =
typeof process !== 'undefined' &&
process.versions &&
typeof process.versions.node === 'string';
if (NODE_JS) {
pending('numeric timer ID conflicts only relevant for browsers.');
}
const nativeTimeoutId = setTimeout(function() {}, 100);
const scheduler = new jasmineUnderTest.DelayedFunctionScheduler();
const fn = jasmine.createSpy('fn');
for (let i = 0; i < nativeTimeoutId; i++) {
scheduler.scheduleFunction(fn, 0, [], false);
}
scheduler.removeFunctionWithId(nativeTimeoutId);
scheduler.tick(1);
expect(fn).toHaveBeenCalledTimes(nativeTimeoutId);
});
describe('ticking inside a scheduled function', function() {
let clock;

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() {
@@ -205,6 +192,7 @@ describe('Env', function() {
it('throws an error when given arguments', function() {
expect(function() {
// eslint-disable-next-line no-unused-vars
env.describe('done method', function(done) {});
}).toThrowError('describe does not expect any arguments');
});
@@ -263,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() {
@@ -393,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() {
@@ -422,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() {
@@ -466,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() {
@@ -529,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() {
@@ -573,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() {
@@ -746,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() {
@@ -788,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,65 +1,75 @@
describe('GlobalErrors', function() {
it('calls the added handler on error', function() {
const globals = browserGlobals();
const fakeGlobal = minimalBrowserGlobal();
const handler = jasmine.createSpy('errorHandler');
const errors = new jasmineUnderTest.GlobalErrors(globals.global);
const errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
errors.install();
errors.pushListener(handler);
const error = new Error('nope');
dispatchEvent(globals.listeners, 'error', { 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 globals = browserGlobals();
it('enables external interception of error by overriding global.onerror', function() {
const fakeGlobal = minimalBrowserGlobal();
const handler = jasmine.createSpy('errorHandler');
const errors = new jasmineUnderTest.GlobalErrors(globals.global);
const hijackHandler = jasmine.createSpy('hijackErrorHandler');
const errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
errors.install();
errors.pushListener(handler);
globals.global.onerror = () => {};
fakeGlobal.onerror = hijackHandler;
const error = new Error('nope');
dispatchEvent(globals.listeners, 'error', { 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 globals = browserGlobals();
const fakeGlobal = minimalBrowserGlobal();
const handler1 = jasmine.createSpy('errorHandler1');
const handler2 = jasmine.createSpy('errorHandler2');
const errors = new jasmineUnderTest.GlobalErrors(globals.global);
const errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
errors.install();
errors.pushListener(handler1);
errors.pushListener(handler2);
const error = new Error('nope');
dispatchEvent(globals.listeners, 'error', { 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 globals = browserGlobals();
const fakeGlobal = minimalBrowserGlobal();
const handler1 = jasmine.createSpy('errorHandler1');
const handler2 = jasmine.createSpy('errorHandler2');
const errors = new jasmineUnderTest.GlobalErrors(globals.global);
const errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
errors.install();
errors.pushListener(handler1);
@@ -67,13 +77,9 @@ describe('GlobalErrors', function() {
errors.popListener(handler2);
const error = new Error('nope');
dispatchEvent(globals.listeners, 'error', { 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 globals = browserGlobals();
const errors = new jasmineUnderTest.GlobalErrors(globals.global);
function unrelatedListener() {}
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);
expect(fakeGlobal.onerror).toBe(originalCallback);
errors.install();
globals.global.addEventListener('error', unrelatedListener);
expect(fakeGlobal.onerror).not.toBe(originalCallback);
errors.uninstall();
expect(globals.listeners.error).toEqual([unrelatedListener]);
expect(fakeGlobal.onerror).toBe(originalCallback);
});
it('rethrows the original error when there is no handler', function() {
const globals = browserGlobals();
const errors = new jasmineUnderTest.GlobalErrors(globals.global);
const fakeGlobal = minimalBrowserGlobal();
const errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
const originalError = new Error('nope');
errors.install();
try {
dispatchEvent(globals.listeners, 'error', { error: originalError });
fakeGlobal.onerror(originalError);
} catch (e) {
expect(e).toBe(originalError);
}
@@ -113,250 +126,359 @@ describe('GlobalErrors', function() {
});
it('reports uncaught exceptions in node.js', function() {
const globals = nodeGlobals();
const errors = new jasmineUnderTest.GlobalErrors(globals.global);
const handler = jasmine.createSpy('errorHandler');
function originalHandler() {}
globals.listeners.uncaughtException = [originalHandler];
const fakeGlobal = {
process: {
on: jasmine.createSpy('process.on'),
removeListener: jasmine.createSpy('process.removeListener'),
listeners: jasmine
.createSpy('process.listeners')
.and.returnValue(['foo']),
removeAllListeners: jasmine.createSpy('process.removeAllListeners')
}
},
handler = jasmine.createSpy('errorHandler'),
errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
errors.install();
expect(globals.listeners.uncaughtException).toEqual([
expect(fakeGlobal.process.on).toHaveBeenCalledWith(
'uncaughtException',
jasmine.any(Function)
]);
expect(globals.listeners.uncaughtException).not.toEqual([
originalHandler()
]);
);
expect(fakeGlobal.process.listeners).toHaveBeenCalledWith(
'uncaughtException'
);
expect(fakeGlobal.process.removeAllListeners).toHaveBeenCalledWith(
'uncaughtException'
);
errors.pushListener(handler);
dispatchEvent(globals.listeners, 'uncaughtException', new Error('bar'));
const addedListener = fakeGlobal.process.on.calls.argsFor(0)[1];
addedListener(new Error('bar'));
expect(handler).toHaveBeenCalledWith(new Error('bar'), undefined);
expect(handler).toHaveBeenCalledWith(new Error('bar'));
expect(handler.calls.argsFor(0)[0].jasmineMessage).toBe(
'Uncaught exception: Error: bar'
);
errors.uninstall();
expect(globals.listeners.uncaughtException).toEqual([originalHandler]);
expect(fakeGlobal.process.removeListener).toHaveBeenCalledWith(
'uncaughtException',
addedListener
);
expect(fakeGlobal.process.on).toHaveBeenCalledWith(
'uncaughtException',
'foo'
);
});
describe('Reporting unhandled promise rejections in node.js', function() {
it('reports rejections with `Error` reasons', function() {
const globals = nodeGlobals();
const errors = new jasmineUnderTest.GlobalErrors(globals.global);
const handler = jasmine.createSpy('errorHandler');
function originalHandler() {}
globals.listeners.unhandledRejection = [originalHandler];
const fakeGlobal = {
process: {
on: jasmine.createSpy('process.on'),
removeListener: jasmine.createSpy('process.removeListener'),
listeners: jasmine
.createSpy('process.listeners')
.and.returnValue(['foo']),
removeAllListeners: jasmine.createSpy('process.removeAllListeners')
}
},
handler = jasmine.createSpy('errorHandler'),
errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
errors.install();
expect(globals.listeners.unhandledRejection).toEqual([
expect(fakeGlobal.process.on).toHaveBeenCalledWith(
'unhandledRejection',
jasmine.any(Function)
]);
expect(globals.listeners.unhandledRejection).not.toEqual([
originalHandler()
]);
);
expect(fakeGlobal.process.listeners).toHaveBeenCalledWith(
'unhandledRejection'
);
expect(fakeGlobal.process.removeAllListeners).toHaveBeenCalledWith(
'unhandledRejection'
);
errors.pushListener(handler);
dispatchEvent(globals.listeners, 'unhandledRejection', new Error('bar'));
const addedListener = fakeGlobal.process.on.calls.argsFor(1)[1];
addedListener(new Error('bar'));
expect(handler).toHaveBeenCalledWith(new Error('bar'), undefined);
expect(handler).toHaveBeenCalledWith(new Error('bar'));
expect(handler.calls.argsFor(0)[0].jasmineMessage).toBe(
'Unhandled promise rejection: Error: bar'
);
errors.uninstall();
expect(globals.listeners.unhandledRejection).toEqual([originalHandler]);
expect(fakeGlobal.process.removeListener).toHaveBeenCalledWith(
'unhandledRejection',
addedListener
);
expect(fakeGlobal.process.on).toHaveBeenCalledWith(
'unhandledRejection',
'foo'
);
});
it('reports rejections with non-`Error` reasons', function() {
const globals = nodeGlobals();
const errors = new jasmineUnderTest.GlobalErrors(globals.global);
const handler = jasmine.createSpy('errorHandler');
const fakeGlobal = {
process: {
on: jasmine.createSpy('process.on'),
removeListener: function() {},
listeners: function() {
return [];
},
removeAllListeners: function() {}
}
},
handler = jasmine.createSpy('errorHandler'),
errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
errors.install();
errors.pushListener(handler);
dispatchEvent(globals.listeners, 'unhandledRejection', 17);
expect(fakeGlobal.process.on.calls.argsFor(1)[0]).toEqual(
'unhandledRejection'
);
const addedListener = fakeGlobal.process.on.calls.argsFor(1)[1];
addedListener(17);
expect(handler).toHaveBeenCalledWith(
new Error(
'Unhandled promise rejection: 17\n' +
'(Tip: to get a useful stack trace, use ' +
'Promise.reject(new Error(...)) instead of Promise.reject(...).)'
),
undefined
)
);
});
it('reports rejections with no reason provided', function() {
const globals = nodeGlobals();
const errors = new jasmineUnderTest.GlobalErrors(globals.global);
const handler = jasmine.createSpy('errorHandler');
const fakeGlobal = {
process: {
on: jasmine.createSpy('process.on'),
removeListener: function() {},
listeners: function() {
return [];
},
removeAllListeners: function() {}
}
},
handler = jasmine.createSpy('errorHandler'),
errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
errors.install();
errors.pushListener(handler);
dispatchEvent(globals.listeners, 'unhandledRejection', undefined);
expect(fakeGlobal.process.on.calls.argsFor(1)[0]).toEqual(
'unhandledRejection'
);
const addedListener = fakeGlobal.process.on.calls.argsFor(1)[1];
addedListener(undefined);
expect(handler).toHaveBeenCalledWith(
new Error(
'Unhandled promise rejection with no error or message\n' +
'(Tip: to get a useful stack trace, use ' +
'Promise.reject(new Error(...)) instead of Promise.reject().)'
),
undefined
)
);
});
});
describe('Reporting unhandled promise rejections in the browser', function() {
it('subscribes and unsubscribes from the unhandledrejection event', function() {
const globals = browserGlobals();
const errors = new jasmineUnderTest.GlobalErrors(globals.global);
const fakeGlobal = jasmine.createSpyObj('globalErrors', [
'addEventListener',
'removeEventListener',
'onerror'
]),
errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
errors.install();
expect(globals.listeners.unhandledrejection).toEqual([
expect(fakeGlobal.addEventListener).toHaveBeenCalledWith(
'unhandledrejection',
jasmine.any(Function)
]);
);
const addedListener = fakeGlobal.addEventListener.calls.argsFor(0)[1];
errors.uninstall();
expect(globals.listeners.unhandledrejection).toEqual([]);
});
it('reports rejections whose reason is a string', function() {
const globals = browserGlobals();
const handler = jasmine.createSpy('errorHandler');
const errors = new jasmineUnderTest.GlobalErrors(globals.global);
errors.install();
errors.pushListener(handler);
const event = { reason: 'nope' };
dispatchEvent(globals.listeners, 'unhandledrejection', 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 globals = browserGlobals();
const handler = jasmine.createSpy('errorHandler');
const errors = new jasmineUnderTest.GlobalErrors(globals.global);
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 };
dispatchEvent(globals.listeners, 'unhandledrejection', 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 globals = nodeGlobals();
const errors = new jasmineUnderTest.GlobalErrors(globals.global);
const handler = jasmine.createSpy('errorHandler');
errors.install();
errors.pushListener(handler);
dispatchEvent(globals.listeners, 'uncaughtException', 17);
expect(handler).toHaveBeenCalledWith(
new Error('Uncaught exception: 17'),
undefined
})
);
});
it('substitutes a descriptive message when the error is falsy', function() {
const globals = nodeGlobals();
const errors = new jasmineUnderTest.GlobalErrors(globals.global);
const handler = jasmine.createSpy('errorHandler');
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);
errors.install();
errors.pushListener(handler);
dispatchEvent(globals.listeners, 'uncaughtException', undefined);
fakeGlobal.onerror = hijackHandler;
expect(handler).toHaveBeenCalledWith(
new Error('Uncaught exception with no error or message'),
undefined
);
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 globals = browserGlobals();
const fakeGlobal = minimalBrowserGlobal();
const handler0 = jasmine.createSpy('handler0');
const handler1 = jasmine.createSpy('handler1');
const overrideHandler = jasmine.createSpy('overrideHandler');
const errors = new jasmineUnderTest.GlobalErrors(globals.global);
const errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
errors.install();
errors.pushListener(handler0);
errors.setOverrideListener(overrideHandler, () => {});
errors.pushListener(handler1);
dispatchEvent(globals.listeners, 'error', { 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' };
dispatchEvent(globals.listeners, 'error', 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() {
const globals = nodeGlobals();
const globalEventListeners = {};
const fakeGlobal = {
process: {
on: (name, listener) => (globalEventListeners[name] = listener),
removeListener: () => {},
listeners: name => globalEventListeners[name],
removeAllListeners: name => (globalEventListeners[name] = [])
}
};
const handler0 = jasmine.createSpy('handler0');
const handler1 = jasmine.createSpy('handler1');
const overrideHandler = jasmine.createSpy('overrideHandler');
const errors = new jasmineUnderTest.GlobalErrors(globals.global);
const errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
errors.install();
errors.pushListener(handler0);
errors.setOverrideListener(overrideHandler);
errors.pushListener(handler1);
dispatchEvent(globals.listeners, 'uncaughtException', new Error('foo'));
globalEventListeners['uncaughtException'](new Error('foo'));
expect(overrideHandler).toHaveBeenCalledWith(new Error('foo'));
expect(handler0).not.toHaveBeenCalled();
expect(handler1).not.toHaveBeenCalled();
overrideHandler.calls.reset();
errors.removeOverrideListener();
dispatchEvent(globals.listeners, 'uncaughtException', new Error('bar'));
expect(overrideHandler).not.toHaveBeenCalled();
expect(handler1).toHaveBeenCalledWith(new Error('bar'), undefined);
globalEventListeners['uncaughtException'](new Error('bar'));
expect(overrideHandler).not.toHaveBeenCalledWith(new Error('bar'));
expect(handler1).toHaveBeenCalledWith(new Error('bar'));
});
it('handles unhandled promise rejections in browsers', function() {
const globals = browserGlobals();
const globalEventListeners = {};
const fakeGlobal = {
addEventListener(name, listener) {
globalEventListeners[name] = listener;
},
removeEventListener() {}
};
const handler = jasmine.createSpy('handler');
const overrideHandler = jasmine.createSpy('overrideHandler');
const errors = new jasmineUnderTest.GlobalErrors(globals.global);
const errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
errors.install();
errors.pushListener(handler);
@@ -364,7 +486,7 @@ describe('GlobalErrors', function() {
const reason = new Error('bar');
dispatchEvent(globals.listeners, 'unhandledrejection', { reason });
globalEventListeners['unhandledrejection']({ reason: reason });
expect(overrideHandler).toHaveBeenCalledWith(
jasmine.objectContaining({
@@ -377,18 +499,32 @@ describe('GlobalErrors', function() {
});
it('handles unhandled promise rejections in Node', function() {
const globals = nodeGlobals();
const globalEventListeners = {};
const fakeGlobal = {
process: {
on(name, listener) {
globalEventListeners[name] = listener;
},
removeListener() {},
listeners(name) {
return globalEventListeners[name];
},
removeAllListeners(name) {
globalEventListeners[name] = null;
}
}
};
const handler0 = jasmine.createSpy('handler0');
const handler1 = jasmine.createSpy('handler1');
const overrideHandler = jasmine.createSpy('overrideHandler');
const errors = new jasmineUnderTest.GlobalErrors(globals.global);
const errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
errors.install();
errors.pushListener(handler0);
errors.setOverrideListener(overrideHandler, () => {});
errors.pushListener(handler1);
dispatchEvent(globals.listeners, 'unhandledRejection', new Error('nope'));
globalEventListeners['unhandledRejection'](new Error('nope'));
expect(overrideHandler).toHaveBeenCalledWith(new Error('nope'));
expect(handler0).not.toHaveBeenCalled();
@@ -396,7 +532,7 @@ describe('GlobalErrors', function() {
});
it('throws if there is already an override handler', function() {
const errors = new jasmineUnderTest.GlobalErrors(browserGlobals().global);
const errors = new jasmineUnderTest.GlobalErrors(minimalBrowserGlobal());
errors.setOverrideListener(() => {}, () => {});
expect(function() {
@@ -408,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(browserGlobals().global);
const errors = new jasmineUnderTest.GlobalErrors(minimalBrowserGlobal());
errors.setOverrideListener(() => {}, onRemove);
errors.removeOverrideListener();
@@ -417,61 +553,17 @@ describe('GlobalErrors', function() {
});
it('does not throw if there is no handler', function() {
const errors = new jasmineUnderTest.GlobalErrors(browserGlobals().global);
const errors = new jasmineUnderTest.GlobalErrors(minimalBrowserGlobal());
expect(() => errors.removeOverrideListener()).not.toThrow();
});
});
function browserGlobals() {
const listeners = { error: [], unhandledrejection: [] };
function minimalBrowserGlobal() {
return {
listeners,
global: {
addEventListener(eventName, listener) {
listeners[eventName].push(listener);
},
removeEventListener(eventName, listener) {
listeners[eventName] = listeners[eventName].filter(
l => l !== listener
);
}
}
addEventListener() {},
removeEventListener() {},
onerror: null
};
}
function nodeGlobals() {
const listeners = { uncaughtException: [], unhandledRejection: [] };
return {
listeners,
global: {
process: {
on(eventName, listener) {
listeners[eventName].push(listener);
},
removeListener(eventName, listener) {
listeners[eventName] = listeners[eventName].filter(
l => l !== listener
);
},
removeAllListeners(eventName) {
listeners[eventName] = [];
},
listeners(eventName) {
return listeners[eventName];
}
}
}
};
}
function dispatchEvent(listeners, eventName, event) {
expect(listeners[eventName].length)
.withContext(`number of ${eventName} listeners`)
.toBeGreaterThan(0);
for (const l of listeners[eventName]) {
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

@@ -164,7 +164,7 @@ describe('PrettyPrinter', function() {
"Object({ foo: 'bar', baz: 3, nullValue: null, undefinedValue: undefined })"
);
expect(pp({ foo: function() {}, bar: [1, 2, 3] })).toEqual(
"Object({ foo: Function 'foo', bar: [ 1, 2, 3 ] })"
'Object({ foo: Function, bar: [ 1, 2, 3 ] })'
);
});
@@ -450,7 +450,7 @@ describe('PrettyPrinter', function() {
};
expect(pp(objFromOtherContext)).toEqual(
"Object({ foo: 'bar', toString: Function 'toString' })"
"Object({ foo: 'bar', toString: Function })"
);
});
@@ -477,17 +477,6 @@ describe('PrettyPrinter', function() {
expect(pp(a)).toEqual('<anonymous>({ })');
});
it('stringifies functions with names', function() {
const pp = jasmineUnderTest.makePrettyPrinter();
expect(pp(foo)).toEqual("Function 'foo'");
function foo() {}
});
it('stringifies functions without names', function() {
const pp = jasmineUnderTest.makePrettyPrinter();
expect(pp(function() {})).toEqual('Function');
});
it('should handle objects with null prototype', function() {
const pp = jasmineUnderTest.makePrettyPrinter();
const obj = Object.create(null);

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();
}
@@ -239,6 +262,7 @@ describe('QueueRunner', function() {
it("sets a timeout if requested for asynchronous functions so they don't go on forever", function() {
const timeout = 3,
// eslint-disable-next-line no-unused-vars
beforeFn = { fn: function(done) {}, type: 'before', timeout: timeout },
queueableFn = { fn: jasmine.createSpy('fn'), type: 'queueable' },
onComplete = jasmine.createSpy('onComplete'),
@@ -286,6 +310,7 @@ describe('QueueRunner', function() {
});
it('by default does not set a timeout for asynchronous functions', function() {
// eslint-disable-next-line no-unused-vars
const beforeFn = { fn: function(done) {} },
queueableFn = { fn: jasmine.createSpy('fn') },
onComplete = jasmine.createSpy('onComplete'),
@@ -308,6 +333,7 @@ describe('QueueRunner', function() {
it('clears the timeout when an async function throws an exception, to prevent additional exception reporting', function() {
const queueableFn = {
// eslint-disable-next-line no-unused-vars
fn: function(done) {
throw new Error('error!');
}
@@ -380,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() {
@@ -406,6 +437,7 @@ describe('QueueRunner', function() {
it('continues running functions when an exception is thrown in async code without timing out', function() {
const queueableFn = {
// eslint-disable-next-line no-unused-vars
fn: function(done) {
throwAsync();
},
@@ -455,31 +487,6 @@ describe('QueueRunner', function() {
expect(nextQueueableFn.fn).toHaveBeenCalled();
});
it('handles a global error event with a message but no error', function() {
const queueableFn = {
fn: function(done) {
const currentHandler = globalErrors.pushListener.calls.mostRecent()
.args[0];
currentHandler(undefined, { message: 'nope' });
},
timeout: 1
};
const onException = jasmine.createSpy('onException');
const globalErrors = {
pushListener: jasmine.createSpy('pushListener'),
popListener: jasmine.createSpy('popListener')
};
const queueRunner = new jasmineUnderTest.QueueRunner({
queueableFns: [queueableFn],
onException: onException,
globalErrors: globalErrors
});
queueRunner.execute();
expect(onException).toHaveBeenCalledWith('nope');
});
it('handles exceptions thrown while waiting for the stack to clear', function() {
const queueableFn = {
fn: function(done) {
@@ -513,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() {
@@ -636,6 +609,7 @@ describe('QueueRunner', function() {
it('issues an error if the function also takes a parameter', function() {
const queueableFn = {
// eslint-disable-next-line no-unused-vars
fn: function(done) {
return new StubPromise();
}
@@ -649,17 +623,16 @@ 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.'
);
});
it('issues a more specific error if the function is `async`', function() {
// eslint-disable-next-line no-unused-vars
async function fn(done) {}
const onException = jasmine.createSpy('onException'),
queueRunner = new jasmineUnderTest.QueueRunner({
@@ -670,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({
@@ -688,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',
@@ -713,6 +697,7 @@ describe('QueueRunner', function() {
it('continues running the functions even after an exception is thrown in an async spec', function() {
const queueableFn = {
// eslint-disable-next-line no-unused-vars
fn: function(done) {
throw new Error('error');
}

View File

@@ -197,8 +197,8 @@ describe('Spec', function() {
description: 'with a spec',
parentSuiteId: 'suite1',
filename: 'someSpecFile.js',
getPath: function() {
return ['a suite', 'with a spec'];
getSpecName: function() {
return 'a suite with a spec';
},
queueableFn: { fn: null }
});
@@ -485,33 +485,17 @@ describe('Spec', function() {
});
it('can return its full name', function() {
const getPath = jasmine
.createSpy('getPath')
.and.returnValue(['expected', 'val']);
const specNameSpy = jasmine
.createSpy('specNameSpy')
.and.returnValue('expected val');
const spec = new jasmineUnderTest.Spec({
getPath,
getSpecName: specNameSpy,
queueableFn: { fn: null }
});
expect(spec.getFullName()).toBe('expected val');
expect(getPath.calls.mostRecent().args[0]).toBe(spec);
});
it('can return its full path', function() {
const getPath = jasmine
.createSpy('getPath')
.and.returnValue(['expected val']);
const spec = new jasmineUnderTest.Spec({
getPath,
queueableFn: { fn: null }
});
expect(spec.getPath()).toEqual(['expected val']);
expect(getPath.calls.mostRecent().args[0]).toBe(spec);
expect(spec.metadata.getPath()).toEqual(['expected val']);
expect(specNameSpy.calls.mostRecent().args[0].id).toEqual(spec.id);
});
describe('when a spec is marked pending during execution', function() {
@@ -602,8 +586,8 @@ describe('Spec', function() {
spec = new jasmineUnderTest.Spec({
onLateError: onLateError,
queueableFn: { fn: function() {} },
getPath: function() {
return ['a spec'];
getSpecName: function() {
return 'a spec';
}
});

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

@@ -97,11 +97,17 @@ describe('Spies', function() {
it('preserves arity of original function', function() {
const functions = [
function nullary() {},
// eslint-disable-next-line no-unused-vars
function unary(arg) {},
// eslint-disable-next-line no-unused-vars
function binary(arg1, arg2) {},
// eslint-disable-next-line no-unused-vars
function ternary(arg1, arg2, arg3) {},
// eslint-disable-next-line no-unused-vars
function quaternary(arg1, arg2, arg3, arg4) {},
// eslint-disable-next-line no-unused-vars
function quinary(arg1, arg2, arg3, arg4, arg5) {},
// eslint-disable-next-line no-unused-vars
function senary(arg1, arg2, arg3, arg4, arg5, arg6) {}
];

View File

@@ -51,27 +51,6 @@ describe('StackTrace', function() {
]);
});
it('understands Chrome/Edge style traces with messages containing blank lines', function() {
const error = {
message: 'line 1\n\nline 2',
stack:
'Error: line 1\n\nline 2\n' +
' at UserContext.<anonymous> (http://localhost:8888/__spec__/core/UtilSpec.js:115:19)\n' +
' at QueueRunner.run (http://localhost:8888/__jasmine__/jasmine.js:4320:20)'
};
const result = new jasmineUnderTest.StackTrace(error);
expect(result.message).toEqual('Error: line 1\n\nline 2');
const rawFrames = result.frames.map(function(f) {
return f.raw;
});
expect(rawFrames).toEqual([
' at UserContext.<anonymous> (http://localhost:8888/__spec__/core/UtilSpec.js:115:19)',
' at QueueRunner.run (http://localhost:8888/__jasmine__/jasmine.js:4320:20)'
]);
});
it('understands Node style traces', function() {
const error = {
message: 'nope',
@@ -116,7 +95,7 @@ describe('StackTrace', function() {
]);
});
it('understands Safari <=14/Firefox style traces', function() {
it('understands Safari <=14/Firefox/Phantom-OS X style traces', function() {
const error = {
message: 'nope',
stack:
@@ -170,7 +149,7 @@ describe('StackTrace', function() {
]);
});
it('does not mistake gibberish for Safari/Firefox style traces', function() {
it('does not mistake gibberish for Safari/Firefox/Phantom-OS X style traces', function() {
const error = {
message: 'nope',
stack: 'randomcharsnotincludingwhitespace'
@@ -180,6 +159,36 @@ describe('StackTrace', function() {
expect(result.frames).toEqual([{ raw: error.stack }]);
});
it('understands Phantom-Linux style traces', function() {
const error = {
message: 'nope',
stack:
' at UserContext.<anonymous> (http://localhost:8888/__spec__/core/UtilSpec.js:115:19)\n' +
' at QueueRunner.run (http://localhost:8888/__jasmine__/jasmine.js:4320:20)'
};
const result = new jasmineUnderTest.StackTrace(error);
expect(result.message).toBeFalsy();
expect(result.style).toEqual('v8');
expect(result.frames).toEqual([
{
raw:
' at UserContext.<anonymous> (http://localhost:8888/__spec__/core/UtilSpec.js:115:19)',
func: 'UserContext.<anonymous>',
file: 'http://localhost:8888/__spec__/core/UtilSpec.js',
line: 115
},
{
raw:
' at QueueRunner.run (http://localhost:8888/__jasmine__/jasmine.js:4320:20)',
func: 'QueueRunner.run',
file: 'http://localhost:8888/__jasmine__/jasmine.js',
line: 4320
}
]);
});
it('ignores blank lines', function() {
const error = {
message: 'nope',
@@ -232,7 +241,7 @@ describe('StackTrace', function() {
]);
});
it('considers different types of errors', function() {
it('consideres different types of errors', function() {
const error = {
message: 'nope',
stack:

View File

@@ -163,20 +163,6 @@ describe('SuiteBuilder', function() {
expect(spec2.id).toMatch(/^spec[0-9]+$/);
expect(spec1.id).not.toEqual(spec2.id);
});
it('gives each spec a full path', function() {
const env = { configuration: () => ({}) };
const suiteBuilder = new jasmineUnderTest.SuiteBuilder({ env });
let spec;
suiteBuilder.describe('a suite', function() {
suiteBuilder.describe('a nested suite', function() {
spec = suiteBuilder[fnName]('a spec', function() {});
});
});
expect(spec.getPath()).toEqual(['a suite', 'a nested suite', 'a spec']);
});
}
function sameInstanceAs(expected) {
@@ -189,202 +175,4 @@ describe('SuiteBuilder', function() {
}
};
}
describe('Duplicate name handling', function() {
describe('When forbidDuplicateNames is true', function() {
let env;
beforeEach(function() {
env = { configuration: () => ({ forbidDuplicateNames: true }) };
});
it('forbids duplicate spec names', function() {
const suiteBuilder = new jasmineUnderTest.SuiteBuilder({ env });
expect(function() {
suiteBuilder.describe('a suite', function() {
suiteBuilder.describe('a nested suite', function() {
suiteBuilder.it('a spec');
suiteBuilder.it('a spec');
});
});
}).toThrowError(
'Duplicate spec name "a spec" found in "a suite a nested suite"'
);
});
it('forbids duplicate spec names in the top suite', function() {
const suiteBuilder = new jasmineUnderTest.SuiteBuilder({ env });
expect(function() {
suiteBuilder.it('another spec');
suiteBuilder.it('another spec');
}).toThrowError(
'Duplicate spec name "another spec" found in top suite'
);
});
it('forbids duplicate suite names', function() {
const suiteBuilder = new jasmineUnderTest.SuiteBuilder({ env });
expect(function() {
suiteBuilder.describe('a suite', function() {
suiteBuilder.describe('a nested suite', function() {
suiteBuilder.describe('another suite', function() {
suiteBuilder.it('a spec');
});
suiteBuilder.describe('another suite', function() {
suiteBuilder.it('a spec');
});
});
});
}).toThrowError(
'Duplicate suite name "another suite" found in "a suite a nested suite"'
);
});
it('forbids duplicate suite names in the top suite', function() {
const suiteBuilder = new jasmineUnderTest.SuiteBuilder({ env });
expect(function() {
suiteBuilder.describe('a suite', function() {
suiteBuilder.it('a spec');
});
suiteBuilder.describe('a suite', function() {
suiteBuilder.it('a spec');
});
}).toThrowError('Duplicate suite name "a suite" found in top suite');
});
it('allows spec and suite names to be duplicated in different suites', function() {
const suiteBuilder = new jasmineUnderTest.SuiteBuilder({ env });
expect(function() {
suiteBuilder.describe('suite a', function() {
suiteBuilder.describe('dupe suite', function() {
suiteBuilder.it('dupe spec');
suiteBuilder.describe('child suite', function() {
suiteBuilder.it('dupe spec');
});
});
});
suiteBuilder.describe('suite b', function() {
suiteBuilder.describe('dupe suite', function() {
suiteBuilder.it('dupe spec');
});
});
}).not.toThrow();
});
});
describe('When forbidDuplicateNames is false', function() {
let env;
beforeEach(function() {
env = { configuration: () => ({ forbidDuplicateNames: false }) };
});
it('allows duplicate spec and suite names', function() {
const suiteBuilder = new jasmineUnderTest.SuiteBuilder({ env });
expect(function() {
suiteBuilder.describe('dupe suite', function() {
suiteBuilder.it('dupe spec');
suiteBuilder.it('dupe spec');
});
suiteBuilder.describe('dupe suite', function() {
suiteBuilder.it('dupe spec');
suiteBuilder.it('dupe spec');
});
}).not.toThrow();
});
});
});
describe('#parallelReset', function() {
it('resets the top suite result', function() {
jasmineUnderTest.Suite.prototype.handleException.and.callThrough();
const env = { configuration: () => ({}) };
const suiteBuilder = new jasmineUnderTest.SuiteBuilder({ env });
suiteBuilder.topSuite.handleException(new Error('nope'));
suiteBuilder.parallelReset();
expect(suiteBuilder.topSuite.result).toEqual({
id: suiteBuilder.topSuite.id,
description: 'Jasmine__TopLevel__Suite',
fullName: '',
failedExpectations: [],
deprecationWarnings: [],
duration: null,
properties: null,
parentSuiteId: null,
filename: undefined
});
});
it('removes children of the top suite', function() {
const env = { configuration: () => ({}) };
const suiteBuilder = new jasmineUnderTest.SuiteBuilder({ env });
suiteBuilder.describe('a suite', function() {
suiteBuilder.it('a nested spec');
});
suiteBuilder.it('a spec');
suiteBuilder.parallelReset();
expect(suiteBuilder.topSuite.children).toEqual([]);
});
it('preserves top suite befores and afters', function() {
const env = { configuration: () => ({}) };
const suiteBuilder = new jasmineUnderTest.SuiteBuilder({ env });
function beforeAll() {}
function beforeEach() {}
function afterEach() {}
function afterAll() {}
suiteBuilder.beforeAll(beforeAll);
suiteBuilder.beforeEach(beforeEach);
suiteBuilder.afterEach(afterEach);
suiteBuilder.afterAll(afterAll);
suiteBuilder.parallelReset();
expect(suiteBuilder.topSuite.beforeAllFns).toEqual([
jasmine.objectContaining({ fn: beforeAll })
]);
expect(suiteBuilder.topSuite.beforeFns).toEqual([
jasmine.objectContaining({ fn: beforeEach })
]);
expect(suiteBuilder.topSuite.afterFns).toEqual([
jasmine.objectContaining({ fn: afterEach })
]);
expect(suiteBuilder.topSuite.afterAllFns).toEqual([
jasmine.objectContaining({ fn: afterAll })
]);
});
it('resets totalSpecsDefined', function() {
const env = { configuration: () => ({}) };
const suiteBuilder = new jasmineUnderTest.SuiteBuilder({ env });
suiteBuilder.it('a spec');
suiteBuilder.parallelReset();
expect(suiteBuilder.totalSpecsDefined).toEqual(0);
});
it('resets focusedRunables', function() {
const env = { configuration: () => ({}) };
const suiteBuilder = new jasmineUnderTest.SuiteBuilder({ env });
suiteBuilder.fit('a spec', function() {});
suiteBuilder.parallelReset();
expect(suiteBuilder.focusedRunables).toEqual([]);
});
});
});

View File

@@ -378,31 +378,4 @@ describe('Suite', function() {
);
});
});
describe('#hasChildWithDescription', function() {
it('returns true if there is a child with the given description', function() {
const subject = new jasmineUnderTest.Suite({});
const description = 'a spec';
subject.addChild({ description });
expect(subject.hasChildWithDescription(description)).toBeTrue();
});
it('returns false if there is no child with the given description', function() {
const subject = new jasmineUnderTest.Suite({});
subject.addChild({ description: 'a different spec' });
expect(subject.hasChildWithDescription('a spec')).toBeFalse();
});
it('does not recurse into child suites', function() {
const subject = new jasmineUnderTest.Suite({});
const childSuite = new jasmineUnderTest.Suite({});
subject.addChild(childSuite);
const description = 'a spec';
childSuite.addChild(description);
expect(subject.hasChildWithDescription('a spec')).toBeFalse();
});
});
});

View File

@@ -128,6 +128,17 @@ describe('util', function() {
});
});
describe('isUndefined', function() {
it('reports if a variable is defined', function() {
let a;
expect(jasmineUnderTest.util.isUndefined(a)).toBe(true);
expect(jasmineUnderTest.util.isUndefined(undefined)).toBe(true);
const defined = 'diz be undefined yo';
expect(jasmineUnderTest.util.isUndefined(defined)).toBe(false);
});
});
describe('cloneArgs', function() {
it('clones primitives as-is', function() {
expect(jasmineUnderTest.util.cloneArgs([true, false])).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() {

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

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();
});
});
@@ -469,24 +469,6 @@ describe('Matchers (Integration)', function() {
});
});
describe('toBeNullish', function() {
verifyPasses(function(env) {
env.expect(undefined).toBeNullish();
});
verifyPasses(function(env) {
env.expect(null).toBeNullish();
});
verifyFails(function(env) {
env.expect(1).toBeNullish();
});
verifyFails(function(env) {
env.expect('').toBeNullish();
});
});
describe('toContain', function() {
verifyPasses(function(env) {
env.addCustomEqualityTester(function(a, b) {
@@ -628,31 +610,23 @@ 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');
});
});
describe('toHaveClasses', function() {
verifyPasses(function(env) {
const el = specHelpers.domHelpers.createElementWithClassName(
'foo bar baz'
);
env.expect(el).toHaveClasses(['bar', 'baz']);
});
verifyFails(function(env) {
const el = specHelpers.domHelpers.createElementWithClassName('foo bar');
env.expect(el).toHaveClasses(['bar', 'baz']);
});
});
describe('toHaveSpyInteractions', function() {
let spyObj;
beforeEach(function() {
@@ -675,23 +649,6 @@ describe('Matchers (Integration)', function() {
});
});
describe('toHaveNoOtherSpyInteractions', function() {
let spyObj;
beforeEach(function() {
spyObj = env.createSpyObj('NewClass', ['spyA', 'spyB']);
});
verifyPasses(function(env) {
env.expect(spyObj).toHaveNoOtherSpyInteractions();
});
verifyFails(function(env) {
spyObj.spyA();
env.expect(spyObj).toHaveNoOtherSpyInteractions();
});
});
describe('toMatch', function() {
verifyPasses(function(env) {
env.expect('foo').toMatch(/oo$/);

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([]);
});
@@ -866,6 +868,7 @@ describe('spec running', function() {
const actions = [];
env.describe('Something', function() {
// eslint-disable-next-line no-unused-vars
env.beforeEach(function(innerDone) {
actions.push('beforeEach');
}, 1);

View File

@@ -161,63 +161,6 @@ describe('DiffBuilder', function() {
expect(diffBuilder.getMessage()).toEqual(expectedMsg);
});
it('handles cases where only the expected has a custom object formatter', function() {
const formatter = function(x) {
if (typeof x === 'number') {
return '[number:' + x + ']';
}
};
const prettyPrinter = jasmineUnderTest.makePrettyPrinter([formatter]);
const diffBuilder = new jasmineUnderTest.DiffBuilder({
prettyPrinter: prettyPrinter
});
diffBuilder.setRoots('five', 4);
diffBuilder.recordMismatch();
expect(diffBuilder.getMessage()).toEqual(
"Expected 'five' to equal [number:4]."
);
});
it('handles cases where only the actual has a custom object formatter', function() {
const formatter = function(x) {
if (typeof x === 'number') {
return '[number:' + x + ']';
}
};
const prettyPrinter = jasmineUnderTest.makePrettyPrinter([formatter]);
const diffBuilder = new jasmineUnderTest.DiffBuilder({
prettyPrinter: prettyPrinter
});
diffBuilder.setRoots(5, 'four');
diffBuilder.recordMismatch();
expect(diffBuilder.getMessage()).toEqual(
"Expected [number:5] to equal 'four'."
);
});
it('handles complex cases where only one side has a custom object formatter', function() {
const formatter = function(x) {
if (typeof x === 'number') {
return '[number:' + x + ']';
}
};
const prettyPrinter = jasmineUnderTest.makePrettyPrinter([formatter]);
const diffBuilder = new jasmineUnderTest.DiffBuilder({
prettyPrinter: prettyPrinter
});
diffBuilder.setRoots(5, { foo: 'bar', fnord: { graults: ['wombat'] } });
diffBuilder.recordMismatch();
expect(diffBuilder.getMessage()).toEqual(
"Expected [number:5] to equal Object({ foo: 'bar', fnord: Object({ graults: [ 'wombat' ] }) })."
);
});
it('builds diffs involving asymmetric equality testers that implement valuesForDiff_ at the root', function() {
const prettyPrinter = jasmineUnderTest.makePrettyPrinter([]),
diffBuilder = new jasmineUnderTest.DiffBuilder({

View File

@@ -38,8 +38,6 @@ describe('toBePending', function() {
return matcher.compare(actual);
}
expect(f).toThrowError(
'Expected toBePending to be called on a promise but was on a string.'
);
expect(f).toThrowError('Expected toBePending to be called on a promise.');
});
});

View File

@@ -28,8 +28,6 @@ describe('toBeRejected', function() {
return matcher.compare(actual);
}
expect(f).toThrowError(
'Expected toBeRejected to be called on a promise but was on a string.'
);
expect(f).toThrowError('Expected toBeRejected to be called on a promise.');
});
});

View File

@@ -232,7 +232,7 @@ describe('#toBeRejectedWithError', function() {
}
expect(f).toThrowError(
'Expected toBeRejectedWithError to be called on a promise but was on a string.'
'Expected toBeRejectedWithError to be called on a promise.'
);
});
});

View File

@@ -83,7 +83,7 @@ describe('#toBeRejectedWith', function() {
}
expect(f).toThrowError(
'Expected toBeRejectedWith to be called on a promise but was on a string.'
'Expected toBeRejectedWith to be called on a promise.'
);
});
});

View File

@@ -35,8 +35,6 @@ describe('toBeResolved', function() {
return matcher.compare(actual);
}
expect(f).toThrowError(
'Expected toBeResolved to be called on a promise but was on a string.'
);
expect(f).toThrowError('Expected toBeResolved to be called on a promise.');
});
});

View File

@@ -93,7 +93,7 @@ describe('#toBeResolvedTo', function() {
}
expect(f).toThrowError(
'Expected toBeResolvedTo to be called on a promise but was on a string.'
'Expected toBeResolvedTo to be called on a promise.'
);
});
});

View File

@@ -757,9 +757,7 @@ describe('matchersUtil', function() {
const a2 = new TypedArrayCtor(2);
a1[0] = a2[0] = 0;
a1[1] = a2[1] = 1;
const diffBuilder = new jasmineUnderTest.DiffBuilder();
expect(matchersUtil.equals(a1, a2, diffBuilder)).toBe(true);
jasmine.debugLog('Diff: ' + diffBuilder.getMessage());
expect(matchersUtil.equals(a1, a2)).toBe(true);
}
);
@@ -768,9 +766,7 @@ describe('matchersUtil', function() {
const a1 = new TypedArrayCtor(2);
const a2 = new TypedArrayCtor(1);
a1[0] = a1[1] = a2[0] = 0;
const diffBuilder = new jasmineUnderTest.DiffBuilder();
expect(matchersUtil.equals(a1, a2, diffBuilder)).toBe(false);
jasmine.debugLog('Diff: ' + diffBuilder.getMessage());
expect(matchersUtil.equals(a1, a2)).toBe(false);
});
it(
@@ -781,9 +777,7 @@ describe('matchersUtil', function() {
const a2 = new TypedArrayCtor(1);
a1[0] = 0;
a2[0] = 1;
const diffBuilder = new jasmineUnderTest.DiffBuilder();
expect(matchersUtil.equals(a1, a2, diffBuilder)).toBe(false);
jasmine.debugLog('Diff: ' + diffBuilder.getMessage());
expect(matchersUtil.equals(a1, a2)).toBe(false);
}
);
@@ -830,7 +824,9 @@ describe('matchersUtil', function() {
const matchersUtil = new jasmineUnderTest.MatchersUtil();
const a1 = new TypedArrayCtor(2);
const a2 = new TypedArrayCtor(2);
// eslint-disable-next-line compat/compat
a1[0] = a2[0] = BigInt(0);
// eslint-disable-next-line compat/compat
a1[1] = a2[1] = BigInt(1);
expect(matchersUtil.equals(a1, a2)).toBe(true);
}
@@ -841,6 +837,7 @@ describe('matchersUtil', function() {
const matchersUtil = new jasmineUnderTest.MatchersUtil();
const a1 = new TypedArrayCtor(2);
const a2 = new TypedArrayCtor(1);
// eslint-disable-next-line compat/compat
a1[0] = a1[1] = a2[0] = BigInt(0);
expect(matchersUtil.equals(a1, a2)).toBe(false);
});
@@ -852,7 +849,9 @@ describe('matchersUtil', function() {
const matchersUtil = new jasmineUnderTest.MatchersUtil();
const a1 = new TypedArrayCtor(2);
const a2 = new TypedArrayCtor(2);
// eslint-disable-next-line compat/compat
a1[0] = a1[1] = a2[0] = BigInt(0);
// eslint-disable-next-line compat/compat
a2[1] = BigInt(1);
expect(matchersUtil.equals(a1, a2)).toBe(false);
}

View File

@@ -1,57 +0,0 @@
describe('toBeNullish', function() {
it('passes for null values', function() {
const matcher = jasmineUnderTest.matchers.toBeNullish();
const result = matcher.compare(null);
expect(result.pass).toBe(true);
});
it('passes for undefined values', function() {
const matcher = jasmineUnderTest.matchers.toBeNullish();
const result = matcher.compare(void 0);
expect(result.pass).toBe(true);
});
it('fails when matching defined values', function() {
const matcher = jasmineUnderTest.matchers.toBeNullish();
const result = matcher.compare('foo');
expect(result.pass).toBe(false);
});
describe('falsy values', () => {
it('fails for 0', function() {
const matcher = jasmineUnderTest.matchers.toBeNullish();
const result = matcher.compare(0);
expect(result.pass).toBe(false);
});
it('fails for -0', function() {
const matcher = jasmineUnderTest.matchers.toBeNullish();
const result = matcher.compare(-0);
expect(result.pass).toBe(false);
});
it('fails for empty string', function() {
const matcher = jasmineUnderTest.matchers.toBeNullish();
const result = matcher.compare('');
expect(result.pass).toBe(false);
});
it('fails for false', function() {
const matcher = jasmineUnderTest.matchers.toBeNullish();
const result = matcher.compare(false);
expect(result.pass).toBe(false);
});
it('fails for NaN', function() {
const matcher = jasmineUnderTest.matchers.toBeNullish();
const result = matcher.compare(NaN);
expect(result.pass).toBe(false);
});
it('fails for 0n', function() {
const matcher = jasmineUnderTest.matchers.toBeNullish();
const result = matcher.compare(BigInt(0));
expect(result.pass).toBe(false);
});
});
});

View File

@@ -1,3 +1,4 @@
/* eslint-disable compat/compat */
describe('toEqual', function() {
'use strict';
@@ -95,17 +96,6 @@ describe('toEqual', function() {
expect(compareEquals(actual, expected).message).toEqual(message);
});
it('reports mismatches as well as missing or extra properties', function() {
const actual = { x: { z: 2 } },
expected = { x: { y: 1, z: 3 } },
message =
'Expected $.x to have properties\n' +
' y: 1\n' +
'Expected $.x.z = 2 to equal 3.';
expect(compareEquals(actual, expected).message).toEqual(message);
});
it('reports missing symbol properties', function() {
const actual = { x: {} },
expected = { x: { [Symbol('y')]: 1 } },
@@ -458,9 +448,9 @@ describe('toEqual', function() {
});
it('reports mismatches between Functions', function() {
const actual = { x: function() {} };
const expected = { x: function() {} };
const message = "Expected $.x = Function 'x' to equal Function 'x'.";
const actual = { x: function() {} },
expected = { x: function() {} },
message = 'Expected $.x = Function to equal Function.';
expect(compareEquals(actual, expected).message).toEqual(message);
});

View File

@@ -112,20 +112,4 @@ describe('toHaveBeenCalledBefore', function() {
'Expected spy first spy to not have been called before spy second spy, but it was'
);
});
it('set the correct calls as verified when passing', function() {
const matcher = jasmineUnderTest.matchers.toHaveBeenCalledBefore(),
firstSpy = new jasmineUnderTest.Spy('first spy'),
secondSpy = new jasmineUnderTest.Spy('second spy');
firstSpy();
secondSpy();
matcher.compare(firstSpy, secondSpy);
expect(firstSpy.calls.count()).toBe(1);
expect(firstSpy.calls.unverifiedCount()).toBe(0);
expect(secondSpy.calls.count()).toBe(1);
expect(secondSpy.calls.unverifiedCount()).toBe(0);
});
});

View File

@@ -105,18 +105,4 @@ describe('toHaveBeenCalledOnceWith', function() {
matcher.compare(fn);
}).toThrowError(/Expected a spy, but got Function./);
});
it('set the correct calls as verified when passing', function() {
const pp = jasmineUnderTest.makePrettyPrinter(),
util = new jasmineUnderTest.MatchersUtil({ pp: pp }),
matcher = jasmineUnderTest.matchers.toHaveBeenCalledOnceWith(util),
calledSpy = new jasmineUnderTest.Spy('called-spy');
calledSpy('x');
matcher.compare(calledSpy, 'x');
expect(calledSpy.calls.count()).toBe(1);
expect(calledSpy.calls.unverifiedCount()).toBe(0);
});
});

View File

@@ -50,16 +50,4 @@ describe('toHaveBeenCalled', function() {
'Expected spy sample-spy to have been called.'
);
});
it('set the correct calls as verified when passing', function() {
const matcher = jasmineUnderTest.matchers.toHaveBeenCalled(),
spy = new jasmineUnderTest.Spy('sample-spy');
spy();
matcher.compare(spy);
expect(spy.calls.count()).toBe(1);
expect(spy.calls.unverifiedCount()).toBe(0);
});
});

View File

@@ -87,17 +87,4 @@ describe('toHaveBeenCalledTimes', function() {
' times.'
);
});
it('set the correct calls as verified when passing', function() {
const matcher = jasmineUnderTest.matchers.toHaveBeenCalledTimes(),
spy = new jasmineUnderTest.Spy('sample-spy');
spy();
spy();
matcher.compare(spy, 2);
expect(spy.calls.count()).toBe(2);
expect(spy.calls.unverifiedCount()).toBe(0);
});
});

View File

@@ -2,7 +2,6 @@ describe('toHaveBeenCalledWith', function() {
it('passes when the actual was called with matching parameters', function() {
const matchersUtil = {
contains: jasmine.createSpy('delegated-contains').and.returnValue(true),
equals: jasmine.createSpy('delegated-equals').and.returnValue(true),
pp: jasmineUnderTest.makePrettyPrinter()
},
matcher = jasmineUnderTest.matchers.toHaveBeenCalledWith(matchersUtil),
@@ -93,20 +92,4 @@ describe('toHaveBeenCalledWith', function() {
matcher.compare(fn);
}).toThrowError(/Expected a spy, but got Function./);
});
it('set the correct calls as verified when passing', function() {
const matchersUtil = {
contains: jasmine.createSpy('delegated-contains').and.returnValue(true),
equals: jasmine.createSpy('delegated-equals').and.returnValue(true),
pp: jasmineUnderTest.makePrettyPrinter()
},
matcher = jasmineUnderTest.matchers.toHaveBeenCalledWith(matchersUtil),
calledSpy = new jasmineUnderTest.Spy('called-spy');
calledSpy('a', 'b');
matcher.compare(calledSpy, 'a', 'b');
expect(calledSpy.calls.count()).toBe(1);
expect(calledSpy.calls.unverifiedCount()).toBe(0);
});
});

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

@@ -1,48 +0,0 @@
describe('toHaveClasses', function() {
it('fails for a DOM element that lacks all the expected classes', function() {
const matcher = jasmineUnderTest.matchers.toHaveClasses(),
result = matcher.compare(
specHelpers.domHelpers.createElementWithClassName(''),
['foo', 'bar']
);
expect(result.pass).toBe(false);
});
it('passes for a DOM element that has all the expected classes', function() {
const matcher = jasmineUnderTest.matchers.toHaveClasses(),
el = specHelpers.domHelpers.createElementWithClassName('foo bar baz');
expect(matcher.compare(el, ['foo', 'bar']).pass).toBe(true);
});
it('fails for a DOM element that only has some matching classes', function() {
const matcher = jasmineUnderTest.matchers.toHaveClasses(),
el = specHelpers.domHelpers.createElementWithClassName('foo bar');
expect(matcher.compare(el, ['foo', 'can']).pass).toBe(false);
});
it('throws an exception when actual is not a DOM element', function() {
const matcher = jasmineUnderTest.matchers.toHaveClasses({
pp: jasmineUnderTest.makePrettyPrinter()
});
expect(function() {
matcher.compare('x', ['foo']);
}).toThrowError("'x' is not a DOM element");
expect(function() {
matcher.compare(undefined, ['foo']);
}).toThrowError('undefined is not a DOM element');
const textNode = specHelpers.domHelpers.document.createTextNode('');
expect(function() {
matcher.compare(textNode, ['foo']);
}).toThrowError('HTMLNode is not a DOM element');
expect(function() {
matcher.compare({ classList: '' }, ['foo']);
}).toThrowError("Object({ classList: '' }) is not a DOM element");
});
});

View File

@@ -1,154 +0,0 @@
describe('toHaveNoOtherSpyInteractions', function() {
it('passes when there are no spy interactions', function() {
let matcher = jasmineUnderTest.matchers.toHaveNoOtherSpyInteractions();
let spyObj = jasmineUnderTest
.getEnv()
.createSpyObj('NewClass', ['spyA', 'spyB']);
let result = matcher.compare(spyObj);
expect(result.pass).toBeTrue();
});
it('passes when there are multiple spy interactions where checked by toHaveBeenCalled', function() {
let matcher = jasmineUnderTest.matchers.toHaveNoOtherSpyInteractions();
let toHaveBeenCalledMatcher = jasmineUnderTest.matchers.toHaveBeenCalled();
let spyObj = jasmineUnderTest
.getEnv()
.createSpyObj('NewClass', ['spyA', 'spyB']);
spyObj.spyA();
spyObj.spyB();
spyObj.spyA();
toHaveBeenCalledMatcher.compare(spyObj.spyA);
toHaveBeenCalledMatcher.compare(spyObj.spyB);
let result = matcher.compare(spyObj);
expect(result.pass).toBeTrue();
});
it('fails when there are spy interactions', function() {
const matchersUtil = new jasmineUnderTest.MatchersUtil({
pp: jasmineUnderTest.makePrettyPrinter()
});
let matcher = jasmineUnderTest.matchers.toHaveNoOtherSpyInteractions(
matchersUtil
);
let spyObj = jasmineUnderTest
.getEnv()
.createSpyObj('NewClass', ['spyA', 'spyB']);
spyObj.spyA('x');
let result = matcher.compare(spyObj);
expect(result.pass).toBeFalse();
expect(result.message).toEqual(
'Expected a spy object to have no other spy interactions, but it had the following calls:\n' +
" NewClass.spyA called with [ 'x' ]."
);
});
it('shows the right message is negated', function() {
const matchersUtil = new jasmineUnderTest.MatchersUtil({
pp: jasmineUnderTest.makePrettyPrinter()
});
let matcher = jasmineUnderTest.matchers.toHaveNoOtherSpyInteractions(
matchersUtil
);
let spyObj = jasmineUnderTest
.getEnv()
.createSpyObj('NewClass', ['spyA', 'spyB']);
spyObj.spyA();
spyObj.spyB();
let result = matcher.compare(spyObj);
expect(result.pass).toBeFalse();
expect(result.message).toEqual(
'Expected a spy object to have no other spy interactions, but it had the following calls:\n' +
' NewClass.spyA called with [ ],\n' +
' NewClass.spyB called with [ ].'
);
});
it('passes when only non-observed spy object interactions are interacted', function() {
let matcher = jasmineUnderTest.matchers.toHaveNoOtherSpyInteractions();
let spyObj = jasmineUnderTest
.getEnv()
.createSpyObj('NewClass', ['spyA', 'spyB']);
spyObj.otherMethod = function() {};
spyObj.otherMethod();
let result = matcher.compare(spyObj);
expect(result.pass).toBeTrue();
expect(result.message).toEqual(
"Expected a spy object to have other spy interactions but it didn't."
);
});
it('throws an error if a non-object is passed', function() {
let matcher = jasmineUnderTest.matchers.toHaveNoOtherSpyInteractions();
expect(function() {
matcher.compare(true);
}).toThrowError(Error, /Expected an object, but got/);
expect(function() {
matcher.compare(123);
}).toThrowError(Error, /Expected an object, but got/);
expect(function() {
matcher.compare('string');
}).toThrowError(Error, /Expected an object, but got/);
});
it('throws an error if arguments are passed', function() {
let matcher = jasmineUnderTest.matchers.toHaveNoOtherSpyInteractions();
let spyObj = jasmineUnderTest
.getEnv()
.createSpyObj('mySpyObj', ['spyA', 'spyB']);
expect(function() {
matcher.compare(spyObj, 'an argument');
}).toThrowError(Error, /Does not take arguments/);
});
it('throws an error if the spy object has no spies', function() {
let matcher = jasmineUnderTest.matchers.toHaveNoOtherSpyInteractions();
const spyObj = jasmineUnderTest
.getEnv()
.createSpyObj('mySpyObj', ['notSpy']);
// Removing spy since spy objects cannot be created without spies.
spyObj.notSpy = function() {};
expect(function() {
matcher.compare(spyObj);
}).toThrowError(
Error,
/Expected an object with spies, but object has no spies/
);
});
it('handles multiple interactions with a single spy', function() {
const matchersUtil = new jasmineUnderTest.MatchersUtil({
pp: jasmineUnderTest.makePrettyPrinter()
}),
matcher = jasmineUnderTest.matchers.toHaveNoOtherSpyInteractions(
matchersUtil
),
toHaveBeenCalledWithMatcher = jasmineUnderTest.matchers.toHaveBeenCalledWith(
matchersUtil
),
spyObj = jasmineUnderTest
.getEnv()
.createSpyObj('NewClass', ['spyA', 'spyB']);
spyObj.spyA('x');
spyObj.spyA('y');
toHaveBeenCalledWithMatcher.compare(spyObj.spyA, 'x');
let result = matcher.compare(spyObj);
expect(result.pass).toBeFalse();
});
});

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

@@ -70,7 +70,7 @@ describe('toHaveSpyInteractions', function() {
);
});
it('throws an error if a non-object is passed', function() {
it(`throws an error if a non-object is passed`, function() {
let matcher = jasmineUnderTest.matchers.toHaveSpyInteractions();
expect(function() {

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());

Some files were not shown because too many files have changed in this diff Show More