pax_global_header00006660000000000000000000000064143273176600014523gustar00rootroot0000000000000052 comment=481f1e7c5c8745091e5c794e5a94cb4e927229c7 jasmine-4.5.0/000077500000000000000000000000001432731766000131575ustar00rootroot00000000000000jasmine-4.5.0/.circleci/000077500000000000000000000000001432731766000150125ustar00rootroot00000000000000jasmine-4.5.0/.circleci/config.yml000066400000000000000000000072011432731766000170020ustar00rootroot00000000000000# Run tests against supported Node versions, and (except for pull requests) # against supported browsers. version: 2.1 executors: node18: docker: - image: cimg/node:18.0.0 # Latest 18.x working_directory: ~/workspace node16: docker: - image: cimg/node:16.14.2 # Latest 16.x working_directory: ~/workspace node14: docker: - image: cimg/node:14.17.4 # Latest 14.x working_directory: ~/workspace node12_latest: docker: - image: cimg/node:12.22.10 # Latest 12.x working_directory: ~/workspace node12_17: docker: - image: cimg/node:12.17.0 # Oldest version supported by Jasmine working_directory: ~/workspace jobs: build: parameters: executor: type: executor executor: << parameters.executor >> steps: - checkout - run: name: Report Node and NPM versions command: echo "Using Node $(node --version) and NPM $(npm --version)" - run: name: Install dependencies command: npm install - run: name: Build command: npm run build - persist_to_workspace: root: . paths: - . test_node: &test_node parameters: executor: type: executor executor: << parameters.executor >> steps: - attach_workspace: at: . - run: name: Run tests command: npm test test_browsers: &test_browsers executor: node14 steps: - attach_workspace: at: . - run: name: Install Sauce Connect command: | cd /tmp curl https://saucelabs.com/downloads/sc-4.7.1-linux.tar.gz | tar zxf - chmod +x sc-4.7.1-linux/bin/sc mkdir ~/workspace/bin cp sc-4.7.1-linux/bin/sc ~/workspace/bin ~/workspace/bin/sc --version - run: name: Run tests command: | # Do everything in one step because Sauce Connect won't exit # cleanly if we kill it from a different step than it started in. export PATH=$PATH:$HOME/workspace/bin export SAUCE_TUNNEL_IDENTIFIER=$CIRCLE_BUILD_NUM scripts/start-sauce-connect sauce-pidfile set +o errexit scripts/run-all-browsers exitcode=$? set -o errexit scripts/stop-sauce-connect $(cat sauce-pidfile) exit $exitcode workflows: version: 2 push: jobs: - build: executor: node18 name: build_node_18 - build: executor: node16 name: build_node_16 - build: executor: node14 name: build_node_14 - build: executor: node12_latest name: build_node_12_latest - build: executor: node12_17 name: build_node_12_17 - test_node: executor: node18 name: test_node_18 requires: - build_node_18 - test_node: executor: node16 name: test_node_16 requires: - build_node_16 - test_node: executor: node14 name: test_node_14 requires: - build_node_14 - test_node: executor: node12_latest name: test_node_12_latest requires: - build_node_12_latest - test_node: executor: node12_17 name: test_node_12_17 requires: - build_node_12_17 - test_browsers: requires: - build_node_14 filters: branches: ignore: /pull\/.*/ # Don't run on pull requests. jasmine-4.5.0/.editorconfig000066400000000000000000000001771432731766000156410ustar00rootroot00000000000000[*] charset = utf-8 end_of_line = lf insert_final_newline = true [*.{js, json, sh, yml}] indent_style = space indent_size = 2 jasmine-4.5.0/.gitattributes000066400000000000000000000000371432731766000160520ustar00rootroot00000000000000* text=auto eol=lf *.png -text jasmine-4.5.0/.github/000077500000000000000000000000001432731766000145175ustar00rootroot00000000000000jasmine-4.5.0/.github/CONTRIBUTING.md000066400000000000000000000121571432731766000167560ustar00rootroot00000000000000# Contributing to Jasmine We welcome your contributions! Thanks for helping make Jasmine a better project for everyone. If you want to contribute but don't know what to work on, [issues tagged help needed](https://github.com/issues?q=is%3Aopen+is%3Aissue+org%3Ajasmine+label%3A%22help+needed%22+) should have enough detail to get started. ## Before Submitting a Pull Request 1. Ensure all specs are green in browsers *and* node. * Use `npm test` to test in Node. * Use `npm run serve` to test in browsers. 2. Fix any eslint or prettier errors reported at the end of `npm test`. Prettier errors can be automatically fixed by running `npm run cleanup`. 3. Build `jasmine.js` with `npm run build` and run all specs again. This ensures that your changes self-test well. 5. Revert your changes to `jasmine.js` and `jasmine-html.js`. When we accept your pull request, we will generate these files as a separate commit and merge the entire branch into master. We only accept green pull requests. If you see that the CI build failed, please fix it. Feel free to ask for help if you're stuck. ## Background ### Directory Structure * `/src` contains all of the source files * `/src/core` - generic source files * `/src/html` - browser-specific files * `/src/boot` - sources for boot files (see below) * `/spec` contains all of the tests * mirrors the source directory * there are some additional files * `/lib` contains the compiled copy of Jasmine. This is used to self-test and distributed as the `jasmine-core` Node, and Ruby packages. ### Self-testing Jasmine tests itself. The files in `lib` are loaded first, defining the reference `jasmine`. Then the files in `src` are loaded, defining the reference `jasmineUnderTest`. So there are two copies of the code loaded under test. The tests should always use `jasmineUnderTest` to refer to the objects and functions that are being tested. But the tests can use functions on `jasmine` as needed. _Be careful how you structure any new test code_. Copy the patterns you see in the existing code - this ensures that the code you're testing is not leaking into the `jasmine` reference and vice-versa. ### `boot0.js` and `boot1.js` These files file does all of the setup necessary for Jasmine to work in a browser. They load all of the code, create an `Env`, attach the global functions, and build the reporter. It also sets up the execution of the `Env` - for browsers this is in `window.onload`. While the default in `lib` is appropriate for browsers, projects may wish to customize this file. ### Compatibility Jasmine runs in both Node and a variety of browsers. See the README for the list of currently supported environments. ## Development All source code belongs in `src/`. The `core/` directory contains the bulk of Jasmine's functionality. This code should remain browser- and environment-agnostic. If your feature or fix cannot be, as mentioned above, please degrade gracefully. Any code that depends on a browser (specifically, it expects `window` to be the global or `document` is present) should live in `src/html/`. ### Install Dev Dependencies Jasmine Core relies on Node.js. To install the Node dependencies, you will need Node.js and npm. $ npm install ...will install all of the node modules locally. Now run $ npm test ...you should see tests run and eslint checking formatting. ### How to write new Jasmine code Or, How to make a successful pull request * _Do not change the public interface_. Lots of projects depend on Jasmine and if you aren't careful you'll break them. * _Be environment agnostic_. Some people run their specs in browsers, others in Node. Jasmine should support them all as much as possible. * _Be browser agnostic_ - if you must rely on browser-specific functionality, please write it in a way that degrades gracefully. * _Write specs_ - Jasmine's a testing framework. Don't add functionality without test-driving it. * _Write code in the style of the rest of the repo_ - Jasmine should look like a cohesive whole. * _Ensure the *entire* test suite is green_ in all the big browsers, Node, and ESLint/Prettier. Your contribution shouldn't break Jasmine for other users. Follow these tips and your pull request, patch, or suggestion is much more likely to be integrated. ### Running Specs Be sure to run the tests in at least one supported Node version and at least a couple of supported browsers. To run the tests in Node, simply use `npm test` as described above. To run the tests in a browser, run `npm run serve` and then visit `http://localhost:8888`. If you have the necessary Selenium drivers installed (e.g. geckodriver or chromedriver), you can also use Jasmine's CI tooling: $ JASMINE_BROWSER= npm run ci ### Submitting a Pull Requeset Once you've done the steps listed under "Before Submitting a Pull Request" above, you can submit a pull request via the [standard GitHub process](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/creating-a-pull-request). TL;DR: Fork the repository, push your work up to your fork, and create a PR from there. jasmine-4.5.0/.github/ISSUE_TEMPLATE.md000066400000000000000000000043471432731766000172340ustar00rootroot00000000000000## Are you creating an issue in the correct repository? - When in doubt, create an issue here. - If you have an issue with the Jasmine docs, file an issue in the docs repo here: https://github.com/jasmine/jasmine.github.io - If you have an issue with TypeScript typings, start a discussion at [DefinitelyTpyed](https://github.com/DefinitelyTyped/DefinitelyTyped/discussions/new?category=issues-with-a-types-package) - This repository is for the core Jasmine framework - If you are using a test runner that wraps Jasmine, consider filing an issue with that library if appropriate: - [Jasmine npm](https://github.com/jasmine/jasmine-npm/issues) - [Jasmine browser runner](https://github.com/jasmine/jasmine-browser/issues) - [Jasmine gem](https://github.com/jasmine/jasmine-gem/issues) - [Jasmine py](https://github.com/jasmine/jasmine-py/issues) - [Gulp Jasmine Browser](https://github.com/jasmine/gulp-jasmine-browser/issues) - [Karma](https://github.com/karma-runner/karma/issues) - [Grunt Contrib Jasmine](https://github.com/gruntjs/grunt-contrib-jasmine/issues) ## Expected Behavior ## Current Behavior ## Possible Solution ## Suite that reproduces the behavior (for bugs) ```javascript describe("sample", function() { }); ``` ## Context ## Your Environment * Version used: * Environment name and version (e.g. Chrome 39, node.js 5.4): * Operating System and version (desktop or mobile): * Link to your project: jasmine-4.5.0/.github/PULL_REQUEST_TEMPLATE.md000066400000000000000000000026211432731766000203210ustar00rootroot00000000000000 ## Description ## Motivation and Context ## How Has This Been Tested? ## Types of changes - [ ] Bug fix (non-breaking change which fixes an issue) - [ ] New feature (non-breaking change which adds functionality) - [ ] Breaking change (fix or feature that would cause existing functionality to change) ## Checklist: - [ ] My code follows the code style of this project. - [ ] My change requires a change to the documentation. - [ ] I have updated the documentation accordingly. - [ ] I have read the [**CONTRIBUTING**](https://github.com/jasmine/jasmine/blob/main/.github/CONTRIBUTING.md) guide. - [ ] I have added tests to cover my changes. - [ ] All new and existing tests passed. jasmine-4.5.0/.gitignore000066400000000000000000000004001432731766000151410ustar00rootroot00000000000000.idea/ .svn/ .DS_Store site/ .bundle/ .pairs .rvmrc .ruby-gemset .ruby-version *.gem .bundle tags Gemfile.lock package-lock.json yarn.lock pkg/* .sass-cache/* src/html/.sass-cache/* node_modules/ sauce_connect.log *.swp build/ dist nbproject/ *.iml .envrc jasmine-4.5.0/CODE_OF_CONDUCT.md000066400000000000000000000062411432731766000157610ustar00rootroot00000000000000# Contributor Covenant Code of Conduct ## Our Pledge In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. ## Our Standards Examples of behavior that contributes to creating a positive environment include: * Using welcoming and inclusive language * Being respectful of differing viewpoints and experiences * Gracefully accepting constructive criticism * Focusing on what is best for the community * Showing empathy towards other community members Examples of unacceptable behavior by participants include: * The use of sexualized language or imagery and unwelcome sexual attention or advances * Trolling, insulting/derogatory comments, and personal or political attacks * Public or private harassment * Publishing others' private information, such as a physical or electronic address, without explicit permission * Other conduct which could reasonably be considered inappropriate in a professional setting ## Our Responsibilities Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. ## Scope This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. ## Enforcement Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at jasmine-maintainers@googlegroups.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. ## Attribution This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] [homepage]: http://contributor-covenant.org [version]: http://contributor-covenant.org/version/1/4/ jasmine-4.5.0/Gruntfile.js000066400000000000000000000035071432731766000154610ustar00rootroot00000000000000module.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(', ')); } } jasmine-4.5.0/MIT.LICENSE000066400000000000000000000020451432731766000146150ustar00rootroot00000000000000Copyright (c) 2008-2019 Pivotal Labs Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. jasmine-4.5.0/README.md000066400000000000000000000055071432731766000144450ustar00rootroot00000000000000[](http://jasmine.github.io) [![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 3.x? Check out the [upgrade guide](https://jasmine.github.io/tutorials/upgrading_to_Jasmine_4.0). ## Contributing Please read the [contributors' guide](https://github.com/jasmine/jasmine/blob/main/.github/CONTRIBUTING.md). ## Installation There are several different ways to install Jasmine, depending on your environment and how you'd like to use it. See the [Getting Started page](https://jasmine.github.io/pages/getting_started.html) for details. ## Usage See the [documentation site](https://jasmine.github.io/pages/docs_home.html), particularly the [Your First Suite tutorial](https://jasmine.github.io/tutorials/your_first_suite) for information on writing specs, and [the FAQ](https://jasmine.github.io/pages/faq.html). ## Supported environments Jasmine tests itself across popular browsers (Safari, Chrome, Firefox, and Microsoft Edge) as well as Node. | Environment | Supported versions | |-------------------|--------------------| | Node | 12.17+, 14, 16, 18 | | Safari | 14-16 | | Chrome | Evergreen | | Firefox | Evergreen, 91 | | 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. 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 * [Gwendolyn Van Hove](mailto:gwen@slackersoft.net) * [Steve Gravrock](mailto:sdg@panix.com) ### Maintainers Emeritus * [Davis W. Frank](mailto:dwfrank@pivotal.io) * [Rajan Agaskar](mailto:rajan@pivotal.io) * [Greg Cobb](mailto:gcobb@pivotal.io) * [Chris Amavisca](mailto:camavisca@pivotal.io) * [Christian Williams](mailto:antixian666@gmail.com) * Sheel Choksi Copyright (c) 2008-2022 Jasmine Maintainers. This software is licensed under the [MIT License](https://github.com/jasmine/jasmine/blob/main/MIT.LICENSE). jasmine-4.5.0/RELEASE.md000066400000000000000000000051421432731766000145630ustar00rootroot00000000000000# How to work on a Jasmine Release ## Development ___Jasmine Core Maintainers Only___ Follow the instructions in `CONTRIBUTING.md` during development. ### Git Rules Please attempt to keep commits to `main` small, but cohesive. If a feature is contained in a bunch of small commits (e.g., it has several wip commits or small work), please squash them when pushing to `main`. ### Version We attempt to stick to [Semantic Versioning](http://semver.org/). Most of the time, development should be against a new minor version - fixing bugs and adding new features that are backwards compatible. The current version lives in the file `/package.json`. This version will be copied to `jasmine.js` when the distribution is built. When releasing a new version, update `package.json` with the new version and `npm run build` to update the gem version number. Note that Jasmine should only use the "patch" version number if the new release contains only bug fixes. When `jasmine-core` revs its major or minor version, the `jasmine` NPM package should also rev to that version. ## Release When ready to release - specs are all green and the stories are done: 1. Update the release notes in `release_notes` - use the Anchorman gem to generate the markdown file and edit accordingly. Include a list of supported environments. 1. Update the version in `package.json` 1. Run `npm run build`. ### Commit and push core changes 1. Commit release notes and version changes (jasmine.js, package.json) 2. Push 3. Tag the release and push the tag. 4. Wait for Circle CI to go green ### Build standalone distribution 1. Build the standalone distribution with `grunt buildStandaloneDist` 1. This will generate `dist/jasmine-standalone-.zip`, which you will upload later (see "Finally" below). ### Release the core NPM module 1. `npm adduser` to save your credentials locally 1. `npm publish .` to publish what's in `package.json` ### Release the docs 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 . ### Publish the GitHub release 1. Visit the releases page and find the tag just published. 2. Paste in a link to the correct release notes for this release. 3. If it is a pre-release, mark it as such. 4. Attach the standalone zipfile. jasmine-4.5.0/grunt/000077500000000000000000000000001432731766000143165ustar00rootroot00000000000000jasmine-4.5.0/grunt/config/000077500000000000000000000000001432731766000155635ustar00rootroot00000000000000jasmine-4.5.0/grunt/config/compress.js000066400000000000000000000024321432731766000177550ustar00rootroot00000000000000var 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/") } ] } }; jasmine-4.5.0/grunt/config/concat.js000066400000000000000000000025421432731766000173730ustar00rootroot00000000000000var 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 } } } }; jasmine-4.5.0/grunt/config/cssUrlEmbed.js000066400000000000000000000002131432731766000203250ustar00rootroot00000000000000module.exports = { encodeWithBaseDir: { files: { "lib/jasmine-core/jasmine.css": ["lib/jasmine-core/jasmine.css"] } } }; jasmine-4.5.0/grunt/config/sass.js000066400000000000000000000003321432731766000170700ustar00rootroot00000000000000const sass = require('sass'); module.exports = { options: { implementation: sass, sourceComments: false }, dist: { files: { "lib/jasmine-core/jasmine.css": "src/html/jasmine.scss" } } }; jasmine-4.5.0/grunt/tasks/000077500000000000000000000000001432731766000154435ustar00rootroot00000000000000jasmine-4.5.0/grunt/tasks/build_standalone.js000066400000000000000000000015461432731766000213160ustar00rootroot00000000000000var 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" ] ); jasmine-4.5.0/grunt/templates/000077500000000000000000000000001432731766000163145ustar00rootroot00000000000000jasmine-4.5.0/grunt/templates/SpecRunner.html.jst000066400000000000000000000016561432731766000220750ustar00rootroot00000000000000 Jasmine Spec Runner v<%= jasmineVersion %> jasmine-4.5.0/grunt/templates/licenseBanner.js.jst000066400000000000000000000020711432731766000222210ustar00rootroot00000000000000/* Copyright (c) 2008-<%= currentYear %> Pivotal Labs Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ jasmine-4.5.0/images/000077500000000000000000000000001432731766000144245ustar00rootroot00000000000000jasmine-4.5.0/images/jasmine-horizontal.png000066400000000000000000000032621432731766000207520ustar00rootroot00000000000000PNG  IHDRZvPLTEU@3UI@9MF@;ID@DB@DB@>;A@CA@>C@>@CBA@CBA@@BA@BA@@BAA@BA@A@B@@CBA@A@@BA@A@BBA@BB@BAA@BABAA@BAA@BAABABABAA@BA@AAAAAABAAAAAABAAAAAA@BAAB@AAA@AAA@AAAAAA@AABAAABABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA :DtRNS  "#$%''(*+,-.01456789:;<<>?@BCDHIJKLMNPSTUWXXYZ[^_`abcfghijklmopqrstvxyz{|}~ApIDATx^WSu`l8nU T2 -CVVQjYE(tetD@Z a{G_:ι/y}?`U'۝;oS<с8\zA ([ YmiW<+B*;Jߨk7*K&9/FS~tp >&5OILuHLX8?:ڊ0M'^2&S^b9Rβ#%W){jlaGkt_{-k?~߶!6=0<WInBV#,7|4]9Ғ6e_cR=WPMl4mհ9䗫^"EjV1wDة#R%e=HZwѯmS9z}a-`~T= meX3kkAKEÛ5J{s^eL) ]~?HЯV%pFo]ldb`T|wPځK=yNqzxto %6ޣ;M) i+҂ohH*)n>9'i.vWD٨MNptFeǾcC/iùq^W^x1Є vkcjUҊj ҌؑN/ɓ ð:̒j7=,6ɐYr%ݴš͗}[&w?{UIENDB`jasmine-4.5.0/images/jasmine-horizontal.svg000066400000000000000000000212021432731766000207570ustar00rootroot00000000000000 image/svg+xmljasmine-4.5.0/images/jasmine_favicon.png000066400000000000000000000027161432731766000202730ustar00rootroot00000000000000PNG  IHDR DOPLTEU@fUI`M]UN[UPZQUQYURURXURXUSSUSWSQUSSUSRUSRTRTRUTRUTUTSUSTSUTSUTUTSUTSUTSUSUTSUTTSUTSSTTTTUTTUTSTTSTTSTTSTSTSTTSTTSTSTTSTTSTSTTSUTTUTTUTTUTUTTUTTUTUTUTTUTTUTUUTSTTSTTSTSTTTTTTTTTTTTTTTTTTTTTTTTT0tRNS  !"%'()+,-.1345678:;=>?@ABCEFGHJLMNOPTUWXYZ[\]^_`bcdefgjklmnqstwz{|}~XFjIDATx^m[QTSFEZdD=$;B {!;DDv(% R 9\J9:s~{mS;'ZP F-+r%33U( Uu@[v)wy4jCeR Tzds? d.ɶ *% yX$ˠv(A)Sy9B A[pJ~ewe$t^H=B&rLp0H'P2X,G-# UNF'tts ,0KP"/$IrO?dIZOSe$/%4=%[JX^N4QZdb݃9{ ̓/| TѲC*ʅnRt0?XD`o,wIsܟ`Usc -YT:HHթx#)@^^ vΛΑk|IqW@}[@ g p"H lzVeeeL~ |LQ"[Ven܄q(PJoųeK# nW[NtXZ,u[{IENDB`jasmine-4.5.0/lib/000077500000000000000000000000001432731766000137255ustar00rootroot00000000000000jasmine-4.5.0/lib/jasmine-core.js000066400000000000000000000037271432731766000166500ustar00rootroot00000000000000/** * Note: Only available on Node. * @module jasmine-core */ const jasmineRequire = require('./jasmine-core/jasmine.js'); module.exports = jasmineRequire; /** * Boots a copy of Jasmine and returns an object as described in {@link jasmine}. * @type {function} * @return {jasmine} */ module.exports.boot = require('./jasmine-core/node_boot.js'); /** * Boots a copy of Jasmine and returns an object containing the properties * that would normally be added to the global object. If noGlobals is called * multiple times, the same object is returned every time. * * Do not call boot() if you also call noGlobals(). * * @example * const {describe, beforeEach, it, expect, jasmine} = require('jasmine-core').noGlobals(); */ module.exports.noGlobals = (function() { let jasmineInterface; return function bootWithoutGlobals() { if (!jasmineInterface) { const jasmine = jasmineRequire.core(jasmineRequire); const env = jasmine.getEnv({ suppressLoadErrors: true }); jasmineInterface = jasmineRequire.interface(jasmine, env); } return jasmineInterface; }; }()); const path = require('path'), fs = require('fs'); const rootPath = path.join(__dirname, 'jasmine-core'), bootFiles = ['boot0.js', 'boot1.js'], legacyBootFiles = ['boot.js'], nodeBootFiles = ['node_boot.js'], cssFiles = [], jsFiles = [], jsFilesToSkip = ['jasmine.js'].concat(bootFiles, legacyBootFiles, nodeBootFiles); fs.readdirSync(rootPath).forEach(function(file) { if(fs.statSync(path.join(rootPath, file)).isFile()) { switch(path.extname(file)) { case '.css': cssFiles.push(file); break; case '.js': if (jsFilesToSkip.indexOf(file) < 0) { jsFiles.push(file); } break; } } }); module.exports.files = { path: rootPath, bootDir: rootPath, bootFiles: bootFiles, nodeBootFiles: nodeBootFiles, cssFiles: cssFiles, jsFiles: ['jasmine.js'].concat(jsFiles), imagesDir: path.join(__dirname, '../images') }; jasmine-4.5.0/lib/jasmine-core/000077500000000000000000000000001432731766000163015ustar00rootroot00000000000000jasmine-4.5.0/lib/jasmine-core/boot0.js000066400000000000000000000052251432731766000176660ustar00rootroot00000000000000/* Copyright (c) 2008-2022 Pivotal Labs Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /** This file starts the process of "booting" Jasmine. It initializes Jasmine, makes its globals available, and creates the env. This file should be loaded after `jasmine.js` and `jasmine_html.js`, but before `boot1.js` or any project source files or spec files are loaded. */ (function() { const jasmineRequire = window.jasmineRequire || require('./jasmine.js'); /** * ## Require & Instantiate * * Require Jasmine's core files. Specifically, this requires and attaches all of Jasmine's code to the `jasmine` reference. */ const jasmine = jasmineRequire.core(jasmineRequire), global = jasmine.getGlobal(); global.jasmine = jasmine; /** * Since this is being run in a browser and the results should populate to an HTML page, require the HTML-specific Jasmine code, injecting the same reference. */ jasmineRequire.html(jasmine); /** * Create the Jasmine environment. This is used to run all specs in a project. */ const env = jasmine.getEnv(); /** * ## The Global Interface * * Build up the functions that will be exposed as the Jasmine public interface. A project can customize, rename or alias any of these functions as desired, provided the implementation remains unchanged. */ const jasmineInterface = jasmineRequire.interface(jasmine, env); /** * Add all of the Jasmine global/public interface to the global scope, so a project can use the public interface directly. For example, calling `describe` in specs instead of `jasmine.getEnv().describe`. */ for (const property in jasmineInterface) { global[property] = jasmineInterface[property]; } })(); jasmine-4.5.0/lib/jasmine-core/boot1.js000066400000000000000000000106621432731766000176700ustar00rootroot00000000000000/* Copyright (c) 2008-2022 Pivotal Labs Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /** This file finishes 'booting' Jasmine, performing all of the necessary initialization before executing the loaded environment and all of a project's specs. This file should be loaded after `boot0.js` but before any project source files or spec files are loaded. Thus this file can also be used to customize Jasmine for a project. If a project is using Jasmine via the standalone distribution, this file can be customized directly. If you only wish to configure the Jasmine env, you can load another file that calls `jasmine.getEnv().configure({...})` after `boot0.js` is loaded and before this file is loaded. */ (function() { const env = jasmine.getEnv(); /** * ## Runner Parameters * * More browser specific code - wrap the query string in an object and to allow for getting/setting parameters from the runner user interface. */ const queryString = new jasmine.QueryString({ getWindowLocation: function() { return window.location; } }); const filterSpecs = !!queryString.getParam('spec'); const config = { stopOnSpecFailure: queryString.getParam('stopOnSpecFailure'), stopSpecOnExpectationFailure: queryString.getParam( 'stopSpecOnExpectationFailure' ), hideDisabled: queryString.getParam('hideDisabled') }; const random = queryString.getParam('random'); if (random !== undefined && random !== '') { config.random = random; } const seed = queryString.getParam('seed'); if (seed) { config.seed = seed; } /** * ## Reporters * The `HtmlReporter` builds all of the HTML UI for the runner page. This reporter paints the dots, stars, and x's for specs, as well as all spec names and all failures (if any). */ const htmlReporter = new jasmine.HtmlReporter({ env: env, navigateWithNewParam: function(key, value) { return queryString.navigateWithNewParam(key, value); }, addToExistingQueryString: function(key, value) { return queryString.fullStringWithNewParam(key, value); }, getContainer: function() { return document.body; }, createElement: function() { return document.createElement.apply(document, arguments); }, createTextNode: function() { return document.createTextNode.apply(document, arguments); }, timer: new jasmine.Timer(), filterSpecs: filterSpecs }); /** * The `jsApiReporter` also receives spec results, and is used by any environment that needs to extract the results from JavaScript. */ env.addReporter(jsApiReporter); env.addReporter(htmlReporter); /** * Filter which specs will be run by matching the start of the full name against the `spec` query param. */ const specFilter = new jasmine.HtmlSpecFilter({ filterString: function() { return queryString.getParam('spec'); } }); config.specFilter = function(spec) { return specFilter.matches(spec.getFullName()); }; env.configure(config); /** * ## Execution * * Replace the browser window's `onload`, ensure it's called, and then run all of the loaded specs. This includes initializing the `HtmlReporter` instance and then executing the loaded Jasmine environment. All of this will happen after all of the specs are loaded. */ const currentWindowOnload = window.onload; window.onload = function() { if (currentWindowOnload) { currentWindowOnload(); } htmlReporter.initialize(); env.execute(); }; })(); jasmine-4.5.0/lib/jasmine-core/example/000077500000000000000000000000001432731766000177345ustar00rootroot00000000000000jasmine-4.5.0/lib/jasmine-core/example/node_example/000077500000000000000000000000001432731766000223745ustar00rootroot00000000000000jasmine-4.5.0/lib/jasmine-core/example/node_example/lib/000077500000000000000000000000001432731766000231425ustar00rootroot00000000000000jasmine-4.5.0/lib/jasmine-core/example/node_example/lib/jasmine_examples/000077500000000000000000000000001432731766000264665ustar00rootroot00000000000000jasmine-4.5.0/lib/jasmine-core/example/node_example/lib/jasmine_examples/Player.js000066400000000000000000000005771432731766000302710ustar00rootroot00000000000000class Player { play(song) { this.currentlyPlayingSong = song; this.isPlaying = true; } pause() { this.isPlaying = false; } resume() { if (this.isPlaying) { throw new Error('song is already playing'); } this.isPlaying = true; } makeFavorite() { this.currentlyPlayingSong.persistFavoriteStatus(true); } } module.exports = Player; jasmine-4.5.0/lib/jasmine-core/example/node_example/lib/jasmine_examples/Song.js000066400000000000000000000002251432731766000277310ustar00rootroot00000000000000class Song { persistFavoriteStatus(value) { // something complicated throw new Error('not yet implemented'); } } module.exports = Song; jasmine-4.5.0/lib/jasmine-core/example/node_example/spec/000077500000000000000000000000001432731766000233265ustar00rootroot00000000000000jasmine-4.5.0/lib/jasmine-core/example/node_example/spec/helpers/000077500000000000000000000000001432731766000247705ustar00rootroot00000000000000jasmine-4.5.0/lib/jasmine-core/example/node_example/spec/helpers/jasmine_examples/000077500000000000000000000000001432731766000303145ustar00rootroot00000000000000jasmine-4.5.0/lib/jasmine-core/example/node_example/spec/helpers/jasmine_examples/SpecHelper.js000066400000000000000000000005021432731766000327010ustar00rootroot00000000000000beforeEach(function () { jasmine.addMatchers({ toBePlaying: function () { return { compare: function (actual, expected) { const player = actual; return { pass: player.currentlyPlayingSong === expected && player.isPlaying }; } }; } }); }); jasmine-4.5.0/lib/jasmine-core/example/node_example/spec/jasmine_examples/000077500000000000000000000000001432731766000266525ustar00rootroot00000000000000jasmine-4.5.0/lib/jasmine-core/example/node_example/spec/jasmine_examples/PlayerSpec.js000066400000000000000000000031601432731766000312570ustar00rootroot00000000000000const Player = require('../../lib/jasmine_examples/Player'); const Song = require('../../lib/jasmine_examples/Song'); describe('Player', function() { let player; let song; beforeEach(function() { player = new Player(); song = new Song(); }); it('should be able to play a Song', function() { player.play(song); expect(player.currentlyPlayingSong).toEqual(song); // demonstrates use of custom matcher expect(player).toBePlaying(song); }); describe('when song has been paused', function() { beforeEach(function() { player.play(song); player.pause(); }); it('should indicate that the song is currently paused', function() { expect(player.isPlaying).toBeFalsy(); // demonstrates use of 'not' with a custom matcher expect(player).not.toBePlaying(song); }); it('should be possible to resume', function() { player.resume(); expect(player.isPlaying).toBeTruthy(); expect(player.currentlyPlayingSong).toEqual(song); }); }); // demonstrates use of spies to intercept and test method calls it('tells the current song if the user has made it a favorite', function() { spyOn(song, 'persistFavoriteStatus'); player.play(song); player.makeFavorite(); expect(song.persistFavoriteStatus).toHaveBeenCalledWith(true); }); //demonstrates use of expected exceptions describe('#resume', function() { it('should throw an exception if song is already playing', function() { player.play(song); expect(function() { player.resume(); }).toThrowError('song is already playing'); }); }); }); jasmine-4.5.0/lib/jasmine-core/example/spec/000077500000000000000000000000001432731766000206665ustar00rootroot00000000000000jasmine-4.5.0/lib/jasmine-core/example/spec/PlayerSpec.js000066400000000000000000000027711432731766000233020ustar00rootroot00000000000000describe('Player', function() { let player; let song; beforeEach(function() { player = new Player(); song = new Song(); }); it('should be able to play a Song', function() { player.play(song); expect(player.currentlyPlayingSong).toEqual(song); // demonstrates use of custom matcher expect(player).toBePlaying(song); }); describe('when song has been paused', function() { beforeEach(function() { player.play(song); player.pause(); }); it('should indicate that the song is currently paused', function() { expect(player.isPlaying).toBeFalsy(); // demonstrates use of 'not' with a custom matcher expect(player).not.toBePlaying(song); }); it('should be possible to resume', function() { player.resume(); expect(player.isPlaying).toBeTruthy(); expect(player.currentlyPlayingSong).toEqual(song); }); }); // demonstrates use of spies to intercept and test method calls it('tells the current song if the user has made it a favorite', function() { spyOn(song, 'persistFavoriteStatus'); player.play(song); player.makeFavorite(); expect(song.persistFavoriteStatus).toHaveBeenCalledWith(true); }); //demonstrates use of expected exceptions describe('#resume', function() { it('should throw an exception if song is already playing', function() { player.play(song); expect(function() { player.resume(); }).toThrowError('song is already playing'); }); }); }); jasmine-4.5.0/lib/jasmine-core/example/spec/SpecHelper.js000066400000000000000000000005021432731766000232530ustar00rootroot00000000000000beforeEach(function () { jasmine.addMatchers({ toBePlaying: function () { return { compare: function (actual, expected) { const player = actual; return { pass: player.currentlyPlayingSong === expected && player.isPlaying }; } }; } }); }); jasmine-4.5.0/lib/jasmine-core/example/src/000077500000000000000000000000001432731766000205235ustar00rootroot00000000000000jasmine-4.5.0/lib/jasmine-core/example/src/Player.js000066400000000000000000000005451432731766000223210ustar00rootroot00000000000000class Player { play(song) { this.currentlyPlayingSong = song; this.isPlaying = true; } pause() { this.isPlaying = false; } resume() { if (this.isPlaying) { throw new Error('song is already playing'); } this.isPlaying = true; } makeFavorite() { this.currentlyPlayingSong.persistFavoriteStatus(true); } } jasmine-4.5.0/lib/jasmine-core/example/src/Song.js000066400000000000000000000001751432731766000217720ustar00rootroot00000000000000class Song { persistFavoriteStatus(value) { // something complicated throw new Error('not yet implemented'); } } jasmine-4.5.0/lib/jasmine-core/jasmine-html.js000066400000000000000000000664731432731766000212470ustar00rootroot00000000000000/* Copyright (c) 2008-2022 Pivotal Labs Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ // eslint-disable-next-line no-var var jasmineRequire = window.jasmineRequire || require('./jasmine.js'); jasmineRequire.html = function(j$) { j$.ResultsNode = jasmineRequire.ResultsNode(); j$.HtmlReporter = jasmineRequire.HtmlReporter(j$); j$.QueryString = jasmineRequire.QueryString(); j$.HtmlSpecFilter = jasmineRequire.HtmlSpecFilter(); }; jasmineRequire.HtmlReporter = function(j$) { function ResultsStateBuilder() { this.topResults = new j$.ResultsNode({}, '', null); this.currentParent = this.topResults; this.specsExecuted = 0; this.failureCount = 0; this.pendingSpecCount = 0; } ResultsStateBuilder.prototype.suiteStarted = function(result) { this.currentParent.addChild(result, 'suite'); this.currentParent = this.currentParent.last(); }; ResultsStateBuilder.prototype.suiteDone = function(result) { this.currentParent.updateResult(result); if (this.currentParent !== this.topResults) { this.currentParent = this.currentParent.parent; } if (result.status === 'failed') { this.failureCount++; } }; ResultsStateBuilder.prototype.specStarted = function(result) {}; ResultsStateBuilder.prototype.specDone = function(result) { this.currentParent.addChild(result, 'spec'); if (result.status !== 'excluded') { this.specsExecuted++; } if (result.status === 'failed') { this.failureCount++; } if (result.status == 'pending') { this.pendingSpecCount++; } }; ResultsStateBuilder.prototype.jasmineDone = function(result) { if (result.failedExpectations) { this.failureCount += result.failedExpectations.length; } }; function HtmlReporter(options) { function config() { return (options.env && options.env.configuration()) || {}; } const getContainer = options.getContainer; const createElement = options.createElement; const createTextNode = options.createTextNode; const navigateWithNewParam = options.navigateWithNewParam || function() {}; const addToExistingQueryString = options.addToExistingQueryString || defaultQueryString; const filterSpecs = options.filterSpecs; let htmlReporterMain; let symbols; const deprecationWarnings = []; const failures = []; this.initialize = function() { clearPrior(); htmlReporterMain = createDom( 'div', { className: 'jasmine_html-reporter' }, createDom( 'div', { className: 'jasmine-banner' }, createDom('a', { className: 'jasmine-title', href: 'http://jasmine.github.io/', target: '_blank' }), createDom('span', { className: 'jasmine-version' }, j$.version) ), createDom('ul', { className: 'jasmine-symbol-summary' }), createDom('div', { className: 'jasmine-alert' }), createDom( 'div', { className: 'jasmine-results' }, createDom('div', { className: 'jasmine-failures' }) ) ); getContainer().appendChild(htmlReporterMain); }; let totalSpecsDefined; this.jasmineStarted = function(options) { totalSpecsDefined = options.totalSpecsDefined || 0; }; const summary = createDom('div', { className: 'jasmine-summary' }); const stateBuilder = new ResultsStateBuilder(); this.suiteStarted = function(result) { stateBuilder.suiteStarted(result); }; this.suiteDone = function(result) { stateBuilder.suiteDone(result); if (result.status === 'failed') { failures.push(failureDom(result)); } addDeprecationWarnings(result, 'suite'); }; this.specStarted = function(result) { stateBuilder.specStarted(result); }; this.specDone = function(result) { stateBuilder.specDone(result); if (noExpectations(result)) { const noSpecMsg = "Spec '" + result.fullName + "' has no expectations."; if (result.status === 'failed') { console.error(noSpecMsg); } else { console.warn(noSpecMsg); } } if (!symbols) { symbols = find('.jasmine-symbol-summary'); } symbols.appendChild( createDom('li', { className: this.displaySpecInCorrectFormat(result), id: 'spec_' + result.id, title: result.fullName }) ); if (result.status === 'failed') { failures.push(failureDom(result)); } addDeprecationWarnings(result, 'spec'); }; this.displaySpecInCorrectFormat = function(result) { return noExpectations(result) && result.status === 'passed' ? 'jasmine-empty' : this.resultStatus(result.status); }; this.resultStatus = function(status) { if (status === 'excluded') { return config().hideDisabled ? 'jasmine-excluded-no-display' : 'jasmine-excluded'; } return 'jasmine-' + status; }; this.jasmineDone = function(doneResult) { stateBuilder.jasmineDone(doneResult); const banner = find('.jasmine-banner'); const alert = find('.jasmine-alert'); const order = doneResult && doneResult.order; alert.appendChild( createDom( 'span', { className: 'jasmine-duration' }, 'finished in ' + doneResult.totalTime / 1000 + 's' ) ); banner.appendChild(optionsMenu(config())); if (stateBuilder.specsExecuted < totalSpecsDefined) { const skippedMessage = 'Ran ' + stateBuilder.specsExecuted + ' of ' + totalSpecsDefined + ' specs - run all'; // include window.location.pathname to fix issue with karma-jasmine-html-reporter in angular: see https://github.com/jasmine/jasmine/issues/1906 const skippedLink = (window.location.pathname || '') + addToExistingQueryString('spec', ''); alert.appendChild( createDom( 'span', { className: 'jasmine-bar jasmine-skipped' }, createDom( 'a', { href: skippedLink, title: 'Run all specs' }, skippedMessage ) ) ); } let statusBarMessage = ''; let statusBarClassName = 'jasmine-overall-result jasmine-bar '; const globalFailures = (doneResult && doneResult.failedExpectations) || []; const failed = stateBuilder.failureCount + globalFailures.length > 0; if (totalSpecsDefined > 0 || failed) { statusBarMessage += pluralize('spec', stateBuilder.specsExecuted) + ', ' + pluralize('failure', stateBuilder.failureCount); if (stateBuilder.pendingSpecCount) { statusBarMessage += ', ' + pluralize('pending spec', stateBuilder.pendingSpecCount); } } if (doneResult.overallStatus === 'passed') { statusBarClassName += ' jasmine-passed '; } else if (doneResult.overallStatus === 'incomplete') { statusBarClassName += ' jasmine-incomplete '; statusBarMessage = 'Incomplete: ' + doneResult.incompleteReason + ', ' + statusBarMessage; } else { statusBarClassName += ' jasmine-failed '; } let seedBar; if (order && order.random) { seedBar = createDom( 'span', { className: 'jasmine-seed-bar' }, ', randomized with seed ', createDom( 'a', { title: 'randomized with seed ' + order.seed, href: seedHref(order.seed) }, order.seed ) ); } alert.appendChild( createDom( 'span', { className: statusBarClassName }, statusBarMessage, seedBar ) ); const errorBarClassName = 'jasmine-bar jasmine-errored'; const afterAllMessagePrefix = 'AfterAll '; for (let i = 0; i < globalFailures.length; i++) { alert.appendChild( createDom( 'span', { className: errorBarClassName }, globalFailureMessage(globalFailures[i]) ) ); } function globalFailureMessage(failure) { if (failure.globalErrorType === 'load') { const prefix = 'Error during loading: ' + failure.message; if (failure.filename) { return ( prefix + ' in ' + failure.filename + ' line ' + failure.lineno ); } else { return prefix; } } else if (failure.globalErrorType === 'afterAll') { return afterAllMessagePrefix + failure.message; } else { return failure.message; } } addDeprecationWarnings(doneResult); for (let i = 0; i < deprecationWarnings.length; i++) { const children = []; let context; switch (deprecationWarnings[i].runnableType) { case 'spec': context = '(in spec: ' + deprecationWarnings[i].runnableName + ')'; break; case 'suite': context = '(in suite: ' + deprecationWarnings[i].runnableName + ')'; break; default: context = ''; } deprecationWarnings[i].message.split('\n').forEach(function(line) { children.push(line); children.push(createDom('br')); }); children[0] = 'DEPRECATION: ' + children[0]; children.push(context); if (deprecationWarnings[i].stack) { children.push(createExpander(deprecationWarnings[i].stack)); } alert.appendChild( createDom( 'span', { className: 'jasmine-bar jasmine-warning' }, children ) ); } const results = find('.jasmine-results'); results.appendChild(summary); summaryList(stateBuilder.topResults, summary); if (failures.length) { alert.appendChild( createDom( 'span', { className: 'jasmine-menu jasmine-bar jasmine-spec-list' }, createDom('span', {}, 'Spec List | '), createDom( 'a', { className: 'jasmine-failures-menu', href: '#' }, 'Failures' ) ) ); alert.appendChild( createDom( 'span', { className: 'jasmine-menu jasmine-bar jasmine-failure-list' }, createDom( 'a', { className: 'jasmine-spec-list-menu', href: '#' }, 'Spec List' ), createDom('span', {}, ' | Failures ') ) ); find('.jasmine-failures-menu').onclick = function() { setMenuModeTo('jasmine-failure-list'); return false; }; find('.jasmine-spec-list-menu').onclick = function() { setMenuModeTo('jasmine-spec-list'); return false; }; setMenuModeTo('jasmine-failure-list'); const failureNode = find('.jasmine-failures'); for (let i = 0; i < failures.length; i++) { failureNode.appendChild(failures[i]); } } }; return this; function failureDom(result) { const failure = createDom( 'div', { className: 'jasmine-spec-detail jasmine-failed' }, failureDescription(result, stateBuilder.currentParent), createDom('div', { className: 'jasmine-messages' }) ); const messages = failure.childNodes[1]; for (let i = 0; i < result.failedExpectations.length; i++) { const expectation = result.failedExpectations[i]; messages.appendChild( createDom( 'div', { className: 'jasmine-result-message' }, expectation.message ) ); messages.appendChild( createDom( 'div', { className: 'jasmine-stack-trace' }, expectation.stack ) ); } if (result.failedExpectations.length === 0) { messages.appendChild( createDom( 'div', { className: 'jasmine-result-message' }, 'Spec has no expectations' ) ); } if (result.debugLogs) { messages.appendChild(debugLogTable(result.debugLogs)); } return failure; } function debugLogTable(debugLogs) { const tbody = createDom('tbody'); debugLogs.forEach(function(entry) { tbody.appendChild( createDom( 'tr', {}, createDom('td', {}, entry.timestamp.toString()), createDom('td', {}, entry.message) ) ); }); return createDom( 'div', { className: 'jasmine-debug-log' }, createDom( 'div', { className: 'jasmine-debug-log-header' }, 'Debug logs' ), createDom( 'table', {}, createDom( 'thead', {}, createDom( 'tr', {}, createDom('th', {}, 'Time (ms)'), createDom('th', {}, 'Message') ) ), tbody ) ); } function summaryList(resultsTree, domParent) { let specListNode; for (let i = 0; i < resultsTree.children.length; i++) { const resultNode = resultsTree.children[i]; if (filterSpecs && !hasActiveSpec(resultNode)) { continue; } if (resultNode.type === 'suite') { const suiteListNode = createDom( 'ul', { className: 'jasmine-suite', id: 'suite-' + resultNode.result.id }, createDom( 'li', { className: 'jasmine-suite-detail jasmine-' + resultNode.result.status }, createDom( 'a', { href: specHref(resultNode.result) }, resultNode.result.description ) ) ); summaryList(resultNode, suiteListNode); domParent.appendChild(suiteListNode); } if (resultNode.type === 'spec') { if (domParent.getAttribute('class') !== 'jasmine-specs') { specListNode = createDom('ul', { className: 'jasmine-specs' }); domParent.appendChild(specListNode); } let specDescription = resultNode.result.description; if (noExpectations(resultNode.result)) { specDescription = 'SPEC HAS NO EXPECTATIONS ' + specDescription; } if ( resultNode.result.status === 'pending' && resultNode.result.pendingReason !== '' ) { specDescription = specDescription + ' PENDING WITH MESSAGE: ' + resultNode.result.pendingReason; } specListNode.appendChild( createDom( 'li', { className: 'jasmine-' + resultNode.result.status, id: 'spec-' + resultNode.result.id }, createDom( 'a', { href: specHref(resultNode.result) }, specDescription ) ) ); } } } function optionsMenu(config) { const optionsMenuDom = createDom( 'div', { className: 'jasmine-run-options' }, createDom('span', { className: 'jasmine-trigger' }, 'Options'), createDom( 'div', { className: 'jasmine-payload' }, createDom( 'div', { className: 'jasmine-stop-on-failure' }, createDom('input', { className: 'jasmine-fail-fast', id: 'jasmine-fail-fast', type: 'checkbox' }), createDom( 'label', { className: 'jasmine-label', for: 'jasmine-fail-fast' }, 'stop execution on spec failure' ) ), createDom( 'div', { className: 'jasmine-throw-failures' }, createDom('input', { className: 'jasmine-throw', id: 'jasmine-throw-failures', type: 'checkbox' }), createDom( 'label', { className: 'jasmine-label', for: 'jasmine-throw-failures' }, 'stop spec on expectation failure' ) ), createDom( 'div', { className: 'jasmine-random-order' }, createDom('input', { className: 'jasmine-random', id: 'jasmine-random-order', type: 'checkbox' }), createDom( 'label', { className: 'jasmine-label', for: 'jasmine-random-order' }, 'run tests in random order' ) ), createDom( 'div', { className: 'jasmine-hide-disabled' }, createDom('input', { className: 'jasmine-disabled', id: 'jasmine-hide-disabled', type: 'checkbox' }), createDom( 'label', { className: 'jasmine-label', for: 'jasmine-hide-disabled' }, 'hide disabled tests' ) ) ) ); const failFastCheckbox = optionsMenuDom.querySelector( '#jasmine-fail-fast' ); failFastCheckbox.checked = config.stopOnSpecFailure; failFastCheckbox.onclick = function() { navigateWithNewParam('stopOnSpecFailure', !config.stopOnSpecFailure); }; const throwCheckbox = optionsMenuDom.querySelector( '#jasmine-throw-failures' ); throwCheckbox.checked = config.stopSpecOnExpectationFailure; throwCheckbox.onclick = function() { navigateWithNewParam( 'stopSpecOnExpectationFailure', !config.stopSpecOnExpectationFailure ); }; const randomCheckbox = optionsMenuDom.querySelector( '#jasmine-random-order' ); randomCheckbox.checked = config.random; randomCheckbox.onclick = function() { navigateWithNewParam('random', !config.random); }; const hideDisabled = optionsMenuDom.querySelector( '#jasmine-hide-disabled' ); hideDisabled.checked = config.hideDisabled; hideDisabled.onclick = function() { navigateWithNewParam('hideDisabled', !config.hideDisabled); }; const optionsTrigger = optionsMenuDom.querySelector('.jasmine-trigger'), optionsPayload = optionsMenuDom.querySelector('.jasmine-payload'), isOpen = /\bjasmine-open\b/; optionsTrigger.onclick = function() { if (isOpen.test(optionsPayload.className)) { optionsPayload.className = optionsPayload.className.replace( isOpen, '' ); } else { optionsPayload.className += ' jasmine-open'; } }; return optionsMenuDom; } function failureDescription(result, suite) { const wrapper = createDom( 'div', { className: 'jasmine-description' }, createDom( 'a', { title: result.description, href: specHref(result) }, result.description ) ); let suiteLink; while (suite && suite.parent) { wrapper.insertBefore(createTextNode(' > '), wrapper.firstChild); suiteLink = createDom( 'a', { href: suiteHref(suite) }, suite.result.description ); wrapper.insertBefore(suiteLink, wrapper.firstChild); suite = suite.parent; } return wrapper; } function suiteHref(suite) { const els = []; while (suite && suite.parent) { els.unshift(suite.result.description); suite = suite.parent; } // include window.location.pathname to fix issue with karma-jasmine-html-reporter in angular: see https://github.com/jasmine/jasmine/issues/1906 return ( (window.location.pathname || '') + addToExistingQueryString('spec', els.join(' ')) ); } function addDeprecationWarnings(result, runnableType) { if (result && result.deprecationWarnings) { for (let i = 0; i < result.deprecationWarnings.length; i++) { const warning = result.deprecationWarnings[i].message; deprecationWarnings.push({ message: warning, stack: result.deprecationWarnings[i].stack, runnableName: result.fullName, runnableType: runnableType }); } } } function createExpander(stackTrace) { const expandLink = createDom('a', { href: '#' }, 'Show stack trace'); const root = createDom( 'div', { className: 'jasmine-expander' }, expandLink, createDom( 'div', { className: 'jasmine-expander-contents jasmine-stack-trace' }, stackTrace ) ); expandLink.addEventListener('click', function(e) { e.preventDefault(); if (root.classList.contains('jasmine-expanded')) { root.classList.remove('jasmine-expanded'); expandLink.textContent = 'Show stack trace'; } else { root.classList.add('jasmine-expanded'); expandLink.textContent = 'Hide stack trace'; } }); return root; } function find(selector) { return getContainer().querySelector('.jasmine_html-reporter ' + selector); } function clearPrior() { const oldReporter = find(''); if (oldReporter) { getContainer().removeChild(oldReporter); } } function createDom(type, attrs, childrenArrayOrVarArgs) { const el = createElement(type); let children; if (j$.isArray_(childrenArrayOrVarArgs)) { children = childrenArrayOrVarArgs; } else { children = []; for (let i = 2; i < arguments.length; i++) { children.push(arguments[i]); } } for (let i = 0; i < children.length; i++) { const child = children[i]; if (typeof child === 'string') { el.appendChild(createTextNode(child)); } else { if (child) { el.appendChild(child); } } } for (const attr in attrs) { if (attr == 'className') { el[attr] = attrs[attr]; } else { el.setAttribute(attr, attrs[attr]); } } return el; } function pluralize(singular, count) { const word = count == 1 ? singular : singular + 's'; return '' + count + ' ' + word; } function specHref(result) { // include window.location.pathname to fix issue with karma-jasmine-html-reporter in angular: see https://github.com/jasmine/jasmine/issues/1906 return ( (window.location.pathname || '') + addToExistingQueryString('spec', result.fullName) ); } function seedHref(seed) { // include window.location.pathname to fix issue with karma-jasmine-html-reporter in angular: see https://github.com/jasmine/jasmine/issues/1906 return ( (window.location.pathname || '') + addToExistingQueryString('seed', seed) ); } function defaultQueryString(key, value) { return '?' + key + '=' + value; } function setMenuModeTo(mode) { htmlReporterMain.setAttribute('class', 'jasmine_html-reporter ' + mode); } function noExpectations(result) { const allExpectations = result.failedExpectations.length + result.passedExpectations.length; return ( allExpectations === 0 && (result.status === 'passed' || result.status === 'failed') ); } function hasActiveSpec(resultNode) { if (resultNode.type == 'spec' && resultNode.result.status != 'excluded') { return true; } if (resultNode.type == 'suite') { for (let i = 0, j = resultNode.children.length; i < j; i++) { if (hasActiveSpec(resultNode.children[i])) { return true; } } } } } return HtmlReporter; }; jasmineRequire.HtmlSpecFilter = function() { function HtmlSpecFilter(options) { const filterString = options && options.filterString() && options.filterString().replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&'); const filterPattern = new RegExp(filterString); this.matches = function(specName) { return filterPattern.test(specName); }; } return HtmlSpecFilter; }; jasmineRequire.ResultsNode = function() { function ResultsNode(result, type, parent) { this.result = result; this.type = type; this.parent = parent; this.children = []; this.addChild = function(result, type) { this.children.push(new ResultsNode(result, type, this)); }; this.last = function() { return this.children[this.children.length - 1]; }; this.updateResult = function(result) { this.result = result; }; } return ResultsNode; }; jasmineRequire.QueryString = function() { function QueryString(options) { this.navigateWithNewParam = function(key, value) { options.getWindowLocation().search = this.fullStringWithNewParam( key, value ); }; this.fullStringWithNewParam = function(key, value) { const paramMap = queryStringToParamMap(); paramMap[key] = value; return toQueryString(paramMap); }; this.getParam = function(key) { return queryStringToParamMap()[key]; }; return this; function toQueryString(paramMap) { const qStrPairs = []; for (const prop in paramMap) { qStrPairs.push( encodeURIComponent(prop) + '=' + encodeURIComponent(paramMap[prop]) ); } return '?' + qStrPairs.join('&'); } function queryStringToParamMap() { const paramStr = options.getWindowLocation().search.substring(1); let params = []; const paramMap = {}; if (paramStr.length > 0) { params = paramStr.split('&'); for (let i = 0; i < params.length; i++) { const p = params[i].split('='); let value = decodeURIComponent(p[1]); if (value === 'true' || value === 'false') { value = JSON.parse(value); } paramMap[decodeURIComponent(p[0])] = value; } } return paramMap; } } return QueryString; }; jasmine-4.5.0/lib/jasmine-core/jasmine.css000066400000000000000000000530461432731766000204510ustar00rootroot00000000000000@charset "UTF-8"; body { overflow-y: scroll; } .jasmine_html-reporter { width: 100%; background-color: #eee; padding: 5px; margin: -8px; font-size: 11px; font-family: Monaco, "Lucida Console", monospace; line-height: 14px; color: #333; } .jasmine_html-reporter a { text-decoration: none; } .jasmine_html-reporter a:hover { text-decoration: underline; } .jasmine_html-reporter p, .jasmine_html-reporter h1, .jasmine_html-reporter h2, .jasmine_html-reporter h3, .jasmine_html-reporter h4, .jasmine_html-reporter h5, .jasmine_html-reporter h6 { margin: 0; line-height: 14px; } .jasmine_html-reporter .jasmine-banner, .jasmine_html-reporter .jasmine-symbol-summary, .jasmine_html-reporter .jasmine-summary, .jasmine_html-reporter .jasmine-result-message, .jasmine_html-reporter .jasmine-spec .jasmine-description, .jasmine_html-reporter .jasmine-spec-detail .jasmine-description, .jasmine_html-reporter .jasmine-alert .jasmine-bar, .jasmine_html-reporter .jasmine-stack-trace { padding-left: 9px; padding-right: 9px; } .jasmine_html-reporter .jasmine-banner { position: relative; } .jasmine_html-reporter .jasmine-banner .jasmine-title { background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFoAAAAZCAMAAACGusnyAAACdlBMVEX/////AP+AgICqVaqAQICZM5mAVYCSSZKAQICOOY6ATYCLRouAQICJO4mSSYCIRIiPQICHPIeOR4CGQ4aMQICGPYaLRoCFQ4WKQICPPYWJRYCOQoSJQICNPoSIRICMQoSHQICHRICKQoOHQICKPoOJO4OJQYOMQICMQ4CIQYKLQICIPoKLQ4CKQICNPoKJQISMQ4KJQoSLQYKJQISLQ4KIQoSKQYKIQICIQISMQoSKQYKLQIOLQoOJQYGLQIOKQIOMQoGKQYOLQYGKQIOLQoGJQYOJQIOKQYGJQIOKQoGKQIGLQIKLQ4KKQoGLQYKJQIGKQYKJQIGKQIKJQoGKQYKLQIGKQYKLQIOJQoKKQoOJQYKKQIOJQoKKQoOKQIOLQoKKQYOLQYKJQIOKQoKKQYKKQoKJQYOKQYKLQIOKQoKLQYOKQYKLQIOJQoGKQYKJQYGJQoGKQYKLQoGLQYGKQoGJQYKKQYGJQIKKQoGJQYKLQIKKQYGLQYKKQYGKQYGKQYKJQYOKQoKJQYOKQYKLQYOLQYOKQYKLQYOKQoKKQYKKQYOKQYOJQYKKQYKLQYKKQIKKQoKKQYKKQYKKQoKJQIKKQYKLQYKKQYKKQIKKQYKKQYKKQYKKQIKKQYKJQYGLQYGKQYKKQYKKQYGKQIKKQYGKQYOJQoKKQYOLQYKKQYOKQoKKQYKKQoKKQYKKQYKJQYKLQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKJQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKLQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKmIDpEAAAA0XRSTlMAAQIDBAUGBwgJCgsMDQ4PEBESExQVFhcYGRobHB0eHyAiIyQlJycoKissLS4wMTQ1Njc4OTo7PDw+P0BCQ0RISUpLTE1OUFNUVVdYWFlaW15fYGFiY2ZnaGlqa2xtb3BxcnN0dnh5ent8fX5/gIGChIWIioyNjo+QkZOUlZaYmZqbnJ2eoKGio6WmqKmsra6vsLGztre4ubq7vL2+wMHDxMjJysvNzs/Q0dLU1tfY2dvc3t/g4eLj5ebn6Onq6+zt7u/w8vP09fb3+Pn6+/z9/vkVQXAAAAMaSURBVHhe5dXxV1N1GMfxz2ABbDgIAm5VDJOyVDIJLUMaVpBWUZUaGbmqoGpZRSiGiRWp6KoZ5AB0ZY50RImZQIlahKkMYXv/R90dBvET/rJfOr3Ouc8v99zPec59zvf56j+vYKlViSf7250X4Mr3O29Tgq08BdGB4DhcekEJ5YkQKFsgWZdtj9JpV+I8xPjLFqkrsEIqO8PHSpis36jWazcqjEsfJjkvRssVU37SdIOu4XCf5vEJPsnwJpnRNU9JmxhMk8l1gehIrq7hTFjzOD+Vf88629qKMJVNltInFeRexRQyJlNeqd1iGDlSzrIUIyXbyFfm3RYprcQRe7lqtWyGYbfc6dT0R2vmdOOkX3u55C1rP37ftiH+tDby4r/RBT0w8TyEkr+epB9XgPDmSYYWbrhCuFYaIyw3fDQAXTnSkh+ANofiHmWf9l+FY1I90FdQTetstO00o23novzVsJ7uB3/C5TkbjRwZ5JerwV4iRWq9HFbFMaK/d0TYqayRiQPuIxxS3Bu8JWU90/60tKi7vkhaznez0a/TbVOKj5CaOZh6fWG6/Lyv9B/ZLR1gw/S/fpbeVD3MCW1li6SvWDOn65tr99/uvWtBS0XDm4s1t+sOHpG0kpBKx/l77wOSnxLpcx6TXmXLTPQOKYOf9Q1dfr8/SJ2mFdCvl1Yl93DiHUZvXeLJbGSzYu5gVJ2slbSakOR8dxCq5adQ2oFLqsE9Ex3L4qQO0eOPeU5x56bypXp4onSEb5OkICX6lDat55TeoztNKQcJaakrz9KCb95oD69IKq+yKW4XPjknaS52V0TZqE2cTtXjcHSCRmUO88e+85hj3EP74i9p8pylw7lxgMDyyl6OV7ZejnjNMfatu87LxRbH0IS35gt2a4ZjmGpVBdKK3Wr6INk8jWWSGqbA55CKgjBRC6E9w78ydTg3ABS3AFV1QN0Y4Aa2pgEjWnQURj9L0ayK6R2ysEqxHUKzYnLvvyU+i9KM2JHJzE4vyZOyDcOwOsySajeLPc8sNvPJkFlyJd20wpqAzZeAfZ3oWybxd+P/3j+SG3uSBdf2VQAAAABJRU5ErkJggg==") no-repeat; background: url("data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjwhLS0gQ3JlYXRlZCB3aXRoIElua3NjYXBlIChodHRwOi8vd3d3Lmlua3NjYXBlLm9yZy8pIC0tPgoKPHN2ZwogICB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iCiAgIHhtbG5zOmNjPSJodHRwOi8vY3JlYXRpdmVjb21tb25zLm9yZy9ucyMiCiAgIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyIKICAgeG1sbnM6c3ZnPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIKICAgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIgogICB4bWxuczppbmtzY2FwZT0iaHR0cDovL3d3dy5pbmtzY2FwZS5vcmcvbmFtZXNwYWNlcy9pbmtzY2FwZSIKICAgdmVyc2lvbj0iMS4xIgogICB3aWR0aD0iNjgxLjk2MjUyIgogICBoZWlnaHQ9IjE4Ny41IgogICBpZD0ic3ZnMiIKICAgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+PG1ldGFkYXRhCiAgICAgaWQ9Im1ldGFkYXRhOCI+PHJkZjpSREY+PGNjOldvcmsKICAgICAgICAgcmRmOmFib3V0PSIiPjxkYzpmb3JtYXQ+aW1hZ2Uvc3ZnK3htbDwvZGM6Zm9ybWF0PjxkYzp0eXBlCiAgICAgICAgICAgcmRmOnJlc291cmNlPSJodHRwOi8vcHVybC5vcmcvZGMvZGNtaXR5cGUvU3RpbGxJbWFnZSIgLz48L2NjOldvcms+PC9yZGY6UkRGPjwvbWV0YWRhdGE+PGRlZnMKICAgICBpZD0iZGVmczYiPjxjbGlwUGF0aAogICAgICAgaWQ9ImNsaXBQYXRoMTgiPjxwYXRoCiAgICAgICAgIGQ9Ik0gMCwxNTAwIDAsMCBsIDU0NTUuNzQsMCAwLDE1MDAgTCAwLDE1MDAgeiIKICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgICAgaWQ9InBhdGgyMCIgLz48L2NsaXBQYXRoPjwvZGVmcz48ZwogICAgIHRyYW5zZm9ybT0ibWF0cml4KDEuMjUsMCwwLC0xLjI1LDAsMTg3LjUpIgogICAgIGlkPSJnMTAiPjxnCiAgICAgICB0cmFuc2Zvcm09InNjYWxlKDAuMSwwLjEpIgogICAgICAgaWQ9ImcxMiI+PGcKICAgICAgICAgaWQ9ImcxNCI+PGcKICAgICAgICAgICBjbGlwLXBhdGg9InVybCgjY2xpcFBhdGgxOCkiCiAgICAgICAgICAgaWQ9ImcxNiI+PHBhdGgKICAgICAgICAgICAgIGQ9Im0gMTU0NCw1OTkuNDM0IGMgMC45MiwtNDAuMzUyIDI1LjY4LC04MS42MDIgNzEuNTMsLTgxLjYwMiAyNy41MSwwIDQ3LjY4LDEyLjgzMiA2MS40NCwzNS43NTQgMTIuODMsMjIuOTMgMTIuODMsNTYuODUyIDEyLjgzLDgyLjUyNyBsIDAsMzI5LjE4NCAtNzEuNTIsMCAwLDEwNC41NDMgMjY2LjgzLDAgMCwtMTA0LjU0MyAtNzAuNiwwIDAsLTM0NC43NyBjIDAsLTU4LjY5MSAtMy42OCwtMTA0LjUzMSAtNDQuOTMsLTE1Mi4yMTggLTM2LjY4LC00Mi4xOCAtOTYuMjgsLTY2LjAyIC0xNTMuMTQsLTY2LjAyIC0xMTcuMzcsMCAtMjA3LjI0LDc3Ljk0MSAtMjAyLjY0LDE5Ny4xNDUgbCAxMzAuMiwwIgogICAgICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgICAgICAgIGlkPSJwYXRoMjIiCiAgICAgICAgICAgICBzdHlsZT0iZmlsbDojOGE0MTgyO2ZpbGwtb3BhY2l0eToxO2ZpbGwtcnVsZTpub256ZXJvO3N0cm9rZTpub25lIiAvPjxwYXRoCiAgICAgICAgICAgICBkPSJtIDIzMDEuNCw2NjIuNjk1IGMgMCw4MC43MDMgLTY2Ljk0LDE0NS44MTMgLTE0Ny42MywxNDUuODEzIC04My40NCwwIC0xNDcuNjMsLTY4Ljc4MSAtMTQ3LjYzLC0xNTEuMzAxIDAsLTc5Ljc4NSA2Ni45NCwtMTQ1LjgwMSAxNDUuOCwtMTQ1LjgwMSA4NC4zNSwwIDE0OS40Niw2Ny44NTIgMTQ5LjQ2LDE1MS4yODkgeiBtIC0xLjgzLC0xODEuNTQ3IGMgLTM1Ljc3LC01NC4wOTcgLTkzLjUzLC03OC44NTkgLTE1Ny43MiwtNzguODU5IC0xNDAuMywwIC0yNTEuMjQsMTE2LjQ0OSAtMjUxLjI0LDI1NC45MTggMCwxNDIuMTI5IDExMy43LDI2MC40MSAyNTYuNzQsMjYwLjQxIDYzLjI3LDAgMTE4LjI5LC0yOS4zMzYgMTUyLjIyLC04Mi41MjMgbCAwLDY5LjY4NyAxNzUuMTQsMCAwLC0xMDQuNTI3IC02MS40NCwwIDAsLTI4MC41OTggNjEuNDQsMCAwLC0xMDQuNTI3IC0xNzUuMTQsMCAwLDY2LjAxOSIKICAgICAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICAgICAgICBpZD0icGF0aDI0IgogICAgICAgICAgICAgc3R5bGU9ImZpbGw6IzhhNDE4MjtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6bm9uemVybztzdHJva2U6bm9uZSIgLz48cGF0aAogICAgICAgICAgICAgZD0ibSAyNjIyLjMzLDU1Ny4yNTggYyAzLjY3LC00NC4wMTYgMzMuMDEsLTczLjM0OCA3OC44NiwtNzMuMzQ4IDMzLjkzLDAgNjYuOTMsMjMuODI0IDY2LjkzLDYwLjUwNCAwLDQ4LjYwNiAtNDUuODQsNTYuODU2IC04My40NCw2Ni45NDEgLTg1LjI4LDIyLjAwNCAtMTc4LjgxLDQ4LjYwNiAtMTc4LjgxLDE1NS44NzkgMCw5My41MzYgNzguODYsMTQ3LjYzMyAxNjUuOTgsMTQ3LjYzMyA0NCwwIDgzLjQzLC05LjE3NiAxMTAuOTQsLTQ0LjAwOCBsIDAsMzMuOTIyIDgyLjUzLDAgMCwtMTMyLjk2NSAtMTA4LjIxLDAgYyAtMS44MywzNC44NTYgLTI4LjQyLDU3Ljc3NCAtNjMuMjYsNTcuNzc0IC0zMC4yNiwwIC02Mi4zNSwtMTcuNDIyIC02Mi4zNSwtNTEuMzQ4IDAsLTQ1Ljg0NyA0NC45MywtNTUuOTMgODAuNjksLTY0LjE4IDg4LjAyLC0yMC4xNzUgMTgyLjQ3LC00Ny42OTUgMTgyLjQ3LC0xNTcuNzM0IDAsLTk5LjAyNyAtODMuNDQsLTE1NC4wMzkgLTE3NS4xMywtMTU0LjAzOSAtNDkuNTMsMCAtOTQuNDYsMTUuNTgyIC0xMjYuNTUsNTMuMTggbCAwLC00MC4zNCAtODUuMjcsMCAwLDE0Mi4xMjkgMTE0LjYyLDAiCiAgICAgICAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIgogICAgICAgICAgICAgaWQ9InBhdGgyNiIKICAgICAgICAgICAgIHN0eWxlPSJmaWxsOiM4YTQxODI7ZmlsbC1vcGFjaXR5OjE7ZmlsbC1ydWxlOm5vbnplcm87c3Ryb2tlOm5vbmUiIC8+PHBhdGgKICAgICAgICAgICAgIGQ9Im0gMjk4OC4xOCw4MDAuMjU0IC02My4yNiwwIDAsMTA0LjUyNyAxNjUuMDUsMCAwLC03My4zNTUgYyAzMS4xOCw1MS4zNDcgNzguODYsODUuMjc3IDE0MS4yMSw4NS4yNzcgNjcuODUsMCAxMjQuNzEsLTQxLjI1OCAxNTIuMjEsLTEwMi42OTkgMjYuNiw2Mi4zNTEgOTIuNjIsMTAyLjY5OSAxNjAuNDcsMTAyLjY5OSA1My4xOSwwIDEwNS40NiwtMjIgMTQxLjIxLC02Mi4zNTEgMzguNTIsLTQ0LjkzOCAzOC41MiwtOTMuNTMyIDM4LjUyLC0xNDkuNDU3IGwgMCwtMTg1LjIzOSA2My4yNywwIDAsLTEwNC41MjcgLTIzOC40MiwwIDAsMTA0LjUyNyA2My4yOCwwIDAsMTU3LjcxNSBjIDAsMzIuMTAyIDAsNjAuNTI3IC0xNC42Nyw4OC45NTcgLTE4LjM0LDI2LjU4MiAtNDguNjEsNDAuMzQ0IC03OS43Nyw0MC4zNDQgLTMwLjI2LDAgLTYzLjI4LC0xMi44NDQgLTgyLjUzLC0zNi42NzIgLTIyLjkzLC0yOS4zNTUgLTIyLjkzLC01Ni44NjMgLTIyLjkzLC05Mi42MjkgbCAwLC0xNTcuNzE1IDYzLjI3LDAgMCwtMTA0LjUyNyAtMjM4LjQxLDAgMCwxMDQuNTI3IDYzLjI4LDAgMCwxNTAuMzgzIGMgMCwyOS4zNDggMCw2Ni4wMjMgLTE0LjY3LDkxLjY5OSAtMTUuNTksMjkuMzM2IC00Ny42OSw0NC45MzQgLTgwLjcsNDQuOTM0IC0zMS4xOCwwIC01Ny43NywtMTEuMDA4IC03Ny45NCwtMzUuNzc0IC0yNC43NywtMzAuMjUzIC0yNi42LC02Mi4zNDMgLTI2LjYsLTk5Ljk0MSBsIDAsLTE1MS4zMDEgNjMuMjcsMCAwLC0xMDQuNTI3IC0yMzguNCwwIDAsMTA0LjUyNyA2My4yNiwwIDAsMjgwLjU5OCIKICAgICAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICAgICAgICBpZD0icGF0aDI4IgogICAgICAgICAgICAgc3R5bGU9ImZpbGw6IzhhNDE4MjtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6bm9uemVybztzdHJva2U6bm9uZSIgLz48cGF0aAogICAgICAgICAgICAgZD0ibSAzOTk4LjY2LDk1MS41NDcgLTExMS44NywwIDAsMTE4LjI5MyAxMTEuODcsMCAwLC0xMTguMjkzIHogbSAwLC00MzEuODkxIDYzLjI3LDAgMCwtMTA0LjUyNyAtMjM5LjMzLDAgMCwxMDQuNTI3IDY0LjE5LDAgMCwyODAuNTk4IC02My4yNywwIDAsMTA0LjUyNyAxNzUuMTQsMCAwLC0zODUuMTI1IgogICAgICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgICAgICAgIGlkPSJwYXRoMzAiCiAgICAgICAgICAgICBzdHlsZT0iZmlsbDojOGE0MTgyO2ZpbGwtb3BhY2l0eToxO2ZpbGwtcnVsZTpub256ZXJvO3N0cm9rZTpub25lIiAvPjxwYXRoCiAgICAgICAgICAgICBkPSJtIDQxNTkuMTIsODAwLjI1NCAtNjMuMjcsMCAwLDEwNC41MjcgMTc1LjE0LDAgMCwtNjkuNjg3IGMgMjkuMzUsNTQuMTAxIDg0LjM2LDgwLjY5OSAxNDQuODcsODAuNjk5IDUzLjE5LDAgMTA1LjQ1LC0yMi4wMTYgMTQxLjIyLC02MC41MjcgNDAuMzQsLTQ0LjkzNCA0MS4yNiwtODguMDMyIDQxLjI2LC0xNDMuOTU3IGwgMCwtMTkxLjY1MyA2My4yNywwIDAsLTEwNC41MjcgLTIzOC40LDAgMCwxMDQuNTI3IDYzLjI2LDAgMCwxNTguNjM3IGMgMCwzMC4yNjIgMCw2MS40MzQgLTE5LjI2LDg4LjAzNSAtMjAuMTcsMjYuNTgyIC01My4xOCwzOS40MTQgLTg2LjE5LDM5LjQxNCAtMzMuOTMsMCAtNjguNzcsLTEzLjc1IC04OC45NCwtNDEuMjUgLTIxLjA5LC0yNy41IC0yMS4wOSwtNjkuNjg3IC0yMS4wOSwtMTAyLjcwNyBsIDAsLTE0Mi4xMjkgNjMuMjYsMCAwLC0xMDQuNTI3IC0yMzguNCwwIDAsMTA0LjUyNyA2My4yNywwIDAsMjgwLjU5OCIKICAgICAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICAgICAgICBpZD0icGF0aDMyIgogICAgICAgICAgICAgc3R5bGU9ImZpbGw6IzhhNDE4MjtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6bm9uemVybztzdHJva2U6bm9uZSIgLz48cGF0aAogICAgICAgICAgICAgZD0ibSA1MDgyLjQ4LDcwMy45NjUgYyAtMTkuMjQsNzAuNjA1IC04MS42LDExNS41NDcgLTE1NC4wNCwxMTUuNTQ3IC02Ni4wNCwwIC0xMjkuMywtNTEuMzQ4IC0xNDMuMDUsLTExNS41NDcgbCAyOTcuMDksMCB6IG0gODUuMjcsLTE0NC44ODMgYyAtMzguNTEsLTkzLjUyMyAtMTI5LjI3LC0xNTYuNzkzIC0yMzEuMDUsLTE1Ni43OTMgLTE0My4wNywwIC0yNTcuNjgsMTExLjg3MSAtMjU3LjY4LDI1NS44MzYgMCwxNDQuODgzIDEwOS4xMiwyNjEuMzI4IDI1NC45MSwyNjEuMzI4IDY3Ljg3LDAgMTM1LjcyLC0zMC4yNTggMTgzLjM5LC03OC44NjMgNDguNjIsLTUxLjM0NCA2OC43OSwtMTEzLjY5NSA2OC43OSwtMTgzLjM4MyBsIC0zLjY3LC0zOS40MzQgLTM5Ni4xMywwIGMgMTQuNjcsLTY3Ljg2MyA3Ny4wMywtMTE3LjM2MyAxNDYuNzIsLTExNy4zNjMgNDguNTksMCA5MC43NiwxOC4zMjggMTE4LjI4LDU4LjY3MiBsIDExNi40NCwwIgogICAgICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgICAgICAgIGlkPSJwYXRoMzQiCiAgICAgICAgICAgICBzdHlsZT0iZmlsbDojOGE0MTgyO2ZpbGwtb3BhY2l0eToxO2ZpbGwtcnVsZTpub256ZXJvO3N0cm9rZTpub25lIiAvPjxwYXRoCiAgICAgICAgICAgICBkPSJtIDY5MC44OTUsODUwLjcwMyA5MC43NSwwIDIyLjU0MywzMS4wMzUgMCwyNDMuMTIyIC0xMzUuODI5LDAgMCwtMjQzLjE0MSAyMi41MzYsLTMxLjAxNiIKICAgICAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICAgICAgICBpZD0icGF0aDM2IgogICAgICAgICAgICAgc3R5bGU9ImZpbGw6IzhhNDE4MjtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6bm9uemVybztzdHJva2U6bm9uZSIgLz48cGF0aAogICAgICAgICAgICAgZD0ibSA2MzIuMzk1LDc0Mi4yNTggMjguMDM5LDg2LjMwNCAtMjIuNTUxLDMxLjA0IC0yMzEuMjIzLDc1LjEyOCAtNDEuOTc2LC0xMjkuMTgzIDIzMS4yNTcsLTc1LjEzNyAzNi40NTQsMTEuODQ4IgogICAgICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgICAgICAgIGlkPSJwYXRoMzgiCiAgICAgICAgICAgICBzdHlsZT0iZmlsbDojOGE0MTgyO2ZpbGwtb3BhY2l0eToxO2ZpbGwtcnVsZTpub256ZXJvO3N0cm9rZTpub25lIiAvPjxwYXRoCiAgICAgICAgICAgICBkPSJtIDcxNy40NDksNjUzLjEwNSAtNzMuNDEsNTMuMzYgLTM2LjQ4OCwtMTEuODc1IC0xNDIuOTAzLC0xOTYuNjkyIDEwOS44ODMsLTc5LjgyOCAxNDIuOTE4LDE5Ni43MDMgMCwzOC4zMzIiCiAgICAgICAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIgogICAgICAgICAgICAgaWQ9InBhdGg0MCIKICAgICAgICAgICAgIHN0eWxlPSJmaWxsOiM4YTQxODI7ZmlsbC1vcGFjaXR5OjE7ZmlsbC1ydWxlOm5vbnplcm87c3Ryb2tlOm5vbmUiIC8+PHBhdGgKICAgICAgICAgICAgIGQ9Im0gODI4LjUyLDcwNi40NjUgLTczLjQyNiwtNTMuMzQgMC4wMTEsLTM4LjM1OSBMIDg5OC4wMDQsNDE4LjA3IDEwMDcuOSw0OTcuODk4IDg2NC45NzMsNjk0LjYwOSA4MjguNTIsNzA2LjQ2NSIKICAgICAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICAgICAgICBpZD0icGF0aDQyIgogICAgICAgICAgICAgc3R5bGU9ImZpbGw6IzhhNDE4MjtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6bm9uemVybztzdHJva2U6bm9uZSIgLz48cGF0aAogICAgICAgICAgICAgZD0ibSA4MTIuMDg2LDgyOC41ODYgMjguMDU1LC04Ni4zMiAzNi40ODQsLTExLjgzNiAyMzEuMjI1LDc1LjExNyAtNDEuOTcsMTI5LjE4MyAtMjMxLjIzOSwtNzUuMTQgLTIyLjU1NSwtMzEuMDA0IgogICAgICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgICAgICAgIGlkPSJwYXRoNDQiCiAgICAgICAgICAgICBzdHlsZT0iZmlsbDojOGE0MTgyO2ZpbGwtb3BhY2l0eToxO2ZpbGwtcnVsZTpub256ZXJvO3N0cm9rZTpub25lIiAvPjxwYXRoCiAgICAgICAgICAgICBkPSJtIDczNi4zMDEsMTMzNS44OCBjIC0zMjMuMDQ3LDAgLTU4NS44NzUsLTI2Mi43OCAtNTg1Ljg3NSwtNTg1Ljc4MiAwLC0zMjMuMTE4IDI2Mi44MjgsLTU4NS45NzcgNTg1Ljg3NSwtNTg1Ljk3NyAzMjMuMDE5LDAgNTg1LjgwOSwyNjIuODU5IDU4NS44MDksNTg1Ljk3NyAwLDMyMy4wMDIgLTI2Mi43OSw1ODUuNzgyIC01ODUuODA5LDU4NS43ODIgbCAwLDAgeiBtIDAsLTExOC42MSBjIDI1Ny45NzIsMCA0NjcuMTg5LC0yMDkuMTMgNDY3LjE4OSwtNDY3LjE3MiAwLC0yNTguMTI5IC0yMDkuMjE3LC00NjcuMzQ4IC00NjcuMTg5LC00NjcuMzQ4IC0yNTguMDc0LDAgLTQ2Ny4yNTQsMjA5LjIxOSAtNDY3LjI1NCw0NjcuMzQ4IDAsMjU4LjA0MiAyMDkuMTgsNDY3LjE3MiA0NjcuMjU0LDQ2Ny4xNzIiCiAgICAgICAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIgogICAgICAgICAgICAgaWQ9InBhdGg0NiIKICAgICAgICAgICAgIHN0eWxlPSJmaWxsOiM4YTQxODI7ZmlsbC1vcGFjaXR5OjE7ZmlsbC1ydWxlOm5vbnplcm87c3Ryb2tlOm5vbmUiIC8+PHBhdGgKICAgICAgICAgICAgIGQ9Im0gMTA5MS4xMyw2MTkuODgzIC0xNzUuNzcxLDU3LjEyMSAxMS42MjksMzUuODA4IDE3NS43NjIsLTU3LjEyMSAtMTEuNjIsLTM1LjgwOCIKICAgICAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICAgICAgICBpZD0icGF0aDQ4IgogICAgICAgICAgICAgc3R5bGU9ImZpbGw6IzhhNDE4MjtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6bm9uemVybztzdHJva2U6bm9uZSIgLz48cGF0aAogICAgICAgICAgICAgZD0iTSA4NjYuOTU3LDkwMi4wNzQgODM2LjUsOTI0LjE5OSA5NDUuMTIxLDEwNzMuNzMgOTc1LjU4NiwxMDUxLjYxIDg2Ni45NTcsOTAyLjA3NCIKICAgICAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICAgICAgICBpZD0icGF0aDUwIgogICAgICAgICAgICAgc3R5bGU9ImZpbGw6IzhhNDE4MjtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6bm9uemVybztzdHJva2U6bm9uZSIgLz48cGF0aAogICAgICAgICAgICAgZD0iTSA2MDcuNDY1LDkwMy40NDUgNDk4Ljg1NSwxMDUyLjk3IDUyOS4zMiwxMDc1LjEgNjM3LjkzLDkyNS41NjYgNjA3LjQ2NSw5MDMuNDQ1IgogICAgICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgICAgICAgIGlkPSJwYXRoNTIiCiAgICAgICAgICAgICBzdHlsZT0iZmlsbDojOGE0MTgyO2ZpbGwtb3BhY2l0eToxO2ZpbGwtcnVsZTpub256ZXJvO3N0cm9rZTpub25lIiAvPjxwYXRoCiAgICAgICAgICAgICBkPSJtIDM4MC42ODgsNjIyLjEyOSAtMTEuNjI2LDM1LjgwMSAxNzUuNzU4LDU3LjA5IDExLjYyMSwtMzUuODAxIC0xNzUuNzUzLC01Ny4wOSIKICAgICAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICAgICAgICBpZD0icGF0aDU0IgogICAgICAgICAgICAgc3R5bGU9ImZpbGw6IzhhNDE4MjtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6bm9uemVybztzdHJva2U6bm9uZSIgLz48cGF0aAogICAgICAgICAgICAgZD0ibSA3MTYuMjg5LDM3Ni41OSAzNy42NDA2LDAgMCwxODQuODE2IC0zNy42NDA2LDAgMCwtMTg0LjgxNiB6IgogICAgICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgICAgICAgIGlkPSJwYXRoNTYiCiAgICAgICAgICAgICBzdHlsZT0iZmlsbDojOGE0MTgyO2ZpbGwtb3BhY2l0eToxO2ZpbGwtcnVsZTpub256ZXJvO3N0cm9rZTpub25lIiAvPjwvZz48L2c+PC9nPjwvZz48L3N2Zz4=") no-repeat, none; background-size: 100%; display: block; float: left; width: 90px; height: 25px; } .jasmine_html-reporter .jasmine-banner .jasmine-version { margin-left: 14px; position: relative; top: 6px; } .jasmine_html-reporter #jasmine_content { position: fixed; right: 100%; } .jasmine_html-reporter .jasmine-version { color: #aaa; } .jasmine_html-reporter .jasmine-banner { margin-top: 14px; } .jasmine_html-reporter .jasmine-duration { color: #fff; float: right; line-height: 28px; padding-right: 9px; } .jasmine_html-reporter .jasmine-symbol-summary { overflow: hidden; margin: 14px 0; } .jasmine_html-reporter .jasmine-symbol-summary li { display: inline-block; height: 10px; width: 14px; font-size: 16px; } .jasmine_html-reporter .jasmine-symbol-summary li.jasmine-passed { font-size: 14px; } .jasmine_html-reporter .jasmine-symbol-summary li.jasmine-passed:before { color: #007069; content: "•"; } .jasmine_html-reporter .jasmine-symbol-summary li.jasmine-failed { line-height: 9px; } .jasmine_html-reporter .jasmine-symbol-summary li.jasmine-failed:before { color: #ca3a11; content: "×"; font-weight: bold; margin-left: -1px; } .jasmine_html-reporter .jasmine-symbol-summary li.jasmine-excluded { font-size: 14px; } .jasmine_html-reporter .jasmine-symbol-summary li.jasmine-excluded:before { color: #bababa; content: "•"; } .jasmine_html-reporter .jasmine-symbol-summary li.jasmine-excluded-no-display { font-size: 14px; display: none; } .jasmine_html-reporter .jasmine-symbol-summary li.jasmine-pending { line-height: 17px; } .jasmine_html-reporter .jasmine-symbol-summary li.jasmine-pending:before { color: #ba9d37; content: "*"; } .jasmine_html-reporter .jasmine-symbol-summary li.jasmine-empty { font-size: 14px; } .jasmine_html-reporter .jasmine-symbol-summary li.jasmine-empty:before { color: #ba9d37; content: "•"; } .jasmine_html-reporter .jasmine-run-options { float: right; margin-right: 5px; border: 1px solid #8a4182; color: #8a4182; position: relative; line-height: 20px; } .jasmine_html-reporter .jasmine-run-options .jasmine-trigger { cursor: pointer; padding: 8px 16px; } .jasmine_html-reporter .jasmine-run-options .jasmine-payload { position: absolute; display: none; right: -1px; border: 1px solid #8a4182; background-color: #eee; white-space: nowrap; padding: 4px 8px; } .jasmine_html-reporter .jasmine-run-options .jasmine-payload.jasmine-open { display: block; } .jasmine_html-reporter .jasmine-bar { line-height: 28px; font-size: 14px; display: block; color: #eee; } .jasmine_html-reporter .jasmine-bar.jasmine-failed, .jasmine_html-reporter .jasmine-bar.jasmine-errored { background-color: #ca3a11; border-bottom: 1px solid #eee; } .jasmine_html-reporter .jasmine-bar.jasmine-passed { background-color: #007069; } .jasmine_html-reporter .jasmine-bar.jasmine-incomplete { background-color: #bababa; } .jasmine_html-reporter .jasmine-bar.jasmine-skipped { background-color: #bababa; } .jasmine_html-reporter .jasmine-bar.jasmine-warning { margin-top: 14px; margin-bottom: 14px; background-color: #ba9d37; color: #333; } .jasmine_html-reporter .jasmine-bar.jasmine-menu { background-color: #fff; color: #aaa; } .jasmine_html-reporter .jasmine-bar.jasmine-menu a { color: #333; } .jasmine_html-reporter .jasmine-bar a { color: white; } .jasmine_html-reporter.jasmine-spec-list .jasmine-bar.jasmine-menu.jasmine-failure-list, .jasmine_html-reporter.jasmine-spec-list .jasmine-results .jasmine-failures { display: none; } .jasmine_html-reporter.jasmine-failure-list .jasmine-bar.jasmine-menu.jasmine-spec-list, .jasmine_html-reporter.jasmine-failure-list .jasmine-summary { display: none; } .jasmine_html-reporter .jasmine-results { margin-top: 14px; } .jasmine_html-reporter .jasmine-summary { margin-top: 14px; } .jasmine_html-reporter .jasmine-summary ul { list-style-type: none; margin-left: 14px; padding-top: 0; padding-left: 0; } .jasmine_html-reporter .jasmine-summary ul.jasmine-suite { margin-top: 7px; margin-bottom: 7px; } .jasmine_html-reporter .jasmine-summary li.jasmine-passed a { color: #007069; } .jasmine_html-reporter .jasmine-summary li.jasmine-failed a { color: #ca3a11; } .jasmine_html-reporter .jasmine-summary li.jasmine-empty a { color: #ba9d37; } .jasmine_html-reporter .jasmine-summary li.jasmine-pending a { color: #ba9d37; } .jasmine_html-reporter .jasmine-summary li.jasmine-excluded a { color: #bababa; } .jasmine_html-reporter .jasmine-specs li.jasmine-passed a:before { content: "• "; } .jasmine_html-reporter .jasmine-specs li.jasmine-failed a:before { content: "× "; } .jasmine_html-reporter .jasmine-specs li.jasmine-empty a:before { content: "* "; } .jasmine_html-reporter .jasmine-specs li.jasmine-pending a:before { content: "• "; } .jasmine_html-reporter .jasmine-specs li.jasmine-excluded a:before { content: "• "; } .jasmine_html-reporter .jasmine-description + .jasmine-suite { margin-top: 0; } .jasmine_html-reporter .jasmine-suite { margin-top: 14px; } .jasmine_html-reporter .jasmine-suite a { color: #333; } .jasmine_html-reporter .jasmine-failures .jasmine-spec-detail { margin-bottom: 28px; } .jasmine_html-reporter .jasmine-failures .jasmine-spec-detail .jasmine-description { background-color: #ca3a11; color: white; } .jasmine_html-reporter .jasmine-failures .jasmine-spec-detail .jasmine-description a { color: white; } .jasmine_html-reporter .jasmine-result-message { padding-top: 14px; color: #333; white-space: pre-wrap; } .jasmine_html-reporter .jasmine-result-message span.jasmine-result { display: block; } .jasmine_html-reporter .jasmine-stack-trace { margin: 5px 0 0 0; max-height: 224px; overflow: auto; line-height: 18px; color: #666; border: 1px solid #ddd; background: white; white-space: pre; } .jasmine_html-reporter .jasmine-expander a { display: block; margin-left: 14px; color: blue; text-decoration: underline; } .jasmine_html-reporter .jasmine-expander-contents { display: none; } .jasmine_html-reporter .jasmine-expanded { padding-bottom: 10px; } .jasmine_html-reporter .jasmine-expanded .jasmine-expander-contents { display: block; margin-left: 14px; padding: 5px; } .jasmine_html-reporter .jasmine-debug-log { margin: 5px 0 0 0; padding: 5px; color: #666; border: 1px solid #ddd; background: white; } .jasmine_html-reporter .jasmine-debug-log table { border-spacing: 0; } .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-4.5.0/lib/jasmine-core/jasmine.js000066400000000000000000011012401432731766000202640ustar00rootroot00000000000000/* Copyright (c) 2008-2022 Pivotal Labs Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ // eslint-disable-next-line no-unused-vars,no-var var getJasmineRequireObj = (function(jasmineGlobal) { let jasmineRequire; if ( typeof module !== 'undefined' && module.exports && typeof exports !== 'undefined' ) { if (typeof global !== 'undefined') { jasmineGlobal = global; } else { jasmineGlobal = {}; } jasmineRequire = exports; } else { if ( typeof window !== 'undefined' && typeof window.toString === 'function' && window.toString() === '[object GjsGlobal]' ) { jasmineGlobal = window; } jasmineRequire = jasmineGlobal.jasmineRequire = {}; } function getJasmineRequire() { return jasmineRequire; } getJasmineRequire().core = function(jRequire) { const j$ = {}; jRequire.base(j$, jasmineGlobal); j$.util = jRequire.util(j$); j$.errors = jRequire.errors(); j$.formatErrorMsg = jRequire.formatErrorMsg(); j$.Any = jRequire.Any(j$); j$.Anything = jRequire.Anything(j$); j$.CallTracker = jRequire.CallTracker(j$); j$.MockDate = jRequire.MockDate(j$); j$.getClearStack = jRequire.clearStack(j$); j$.Clock = jRequire.Clock(); j$.DelayedFunctionScheduler = jRequire.DelayedFunctionScheduler(j$); j$.Deprecator = jRequire.Deprecator(j$); j$.Env = jRequire.Env(j$); j$.StackTrace = jRequire.StackTrace(j$); j$.ExceptionFormatter = jRequire.ExceptionFormatter(j$); j$.ExpectationFilterChain = jRequire.ExpectationFilterChain(); j$.Expector = jRequire.Expector(j$); j$.Expectation = jRequire.Expectation(j$); j$.buildExpectationResult = jRequire.buildExpectationResult(j$); j$.JsApiReporter = jRequire.JsApiReporter(j$); j$.makePrettyPrinter = jRequire.makePrettyPrinter(j$); j$.basicPrettyPrinter_ = j$.makePrettyPrinter(); j$.MatchersUtil = jRequire.MatchersUtil(j$); j$.ObjectContaining = jRequire.ObjectContaining(j$); j$.ArrayContaining = jRequire.ArrayContaining(j$); j$.ArrayWithExactContents = jRequire.ArrayWithExactContents(j$); j$.MapContaining = jRequire.MapContaining(j$); j$.SetContaining = jRequire.SetContaining(j$); j$.QueueRunner = jRequire.QueueRunner(j$); j$.NeverSkipPolicy = jRequire.NeverSkipPolicy(j$); j$.SkipAfterBeforeAllErrorPolicy = jRequire.SkipAfterBeforeAllErrorPolicy( j$ ); j$.CompleteOnFirstErrorSkipPolicy = jRequire.CompleteOnFirstErrorSkipPolicy( j$ ); j$.ReportDispatcher = jRequire.ReportDispatcher(j$); j$.RunableResources = jRequire.RunableResources(j$); j$.Runner = jRequire.Runner(j$); j$.Spec = jRequire.Spec(j$); j$.Spy = jRequire.Spy(j$); j$.SpyFactory = jRequire.SpyFactory(j$); j$.SpyRegistry = jRequire.SpyRegistry(j$); j$.SpyStrategy = jRequire.SpyStrategy(j$); j$.StringMatching = jRequire.StringMatching(j$); j$.StringContaining = jRequire.StringContaining(j$); j$.UserContext = jRequire.UserContext(j$); j$.Suite = jRequire.Suite(j$); j$.SuiteBuilder = jRequire.SuiteBuilder(j$); j$.Timer = jRequire.Timer(); j$.TreeProcessor = jRequire.TreeProcessor(); j$.version = jRequire.version(); j$.Order = jRequire.Order(); j$.DiffBuilder = jRequire.DiffBuilder(j$); j$.NullDiffBuilder = jRequire.NullDiffBuilder(j$); j$.ObjectPath = jRequire.ObjectPath(j$); j$.MismatchTree = jRequire.MismatchTree(j$); j$.GlobalErrors = jRequire.GlobalErrors(j$); j$.Truthy = jRequire.Truthy(j$); j$.Falsy = jRequire.Falsy(j$); j$.Empty = jRequire.Empty(j$); j$.NotEmpty = jRequire.NotEmpty(j$); j$.Is = jRequire.Is(j$); j$.matchers = jRequire.requireMatchers(jRequire, j$); j$.asyncMatchers = jRequire.requireAsyncMatchers(jRequire, j$); return j$; }; return getJasmineRequire; })(this); getJasmineRequireObj().requireMatchers = function(jRequire, j$) { const availableMatchers = [ 'nothing', 'toBe', 'toBeCloseTo', 'toBeDefined', 'toBeInstanceOf', 'toBeFalse', 'toBeFalsy', 'toBeGreaterThan', 'toBeGreaterThanOrEqual', 'toBeLessThan', 'toBeLessThanOrEqual', 'toBeNaN', 'toBeNegativeInfinity', 'toBeNull', 'toBePositiveInfinity', 'toBeTrue', 'toBeTruthy', 'toBeUndefined', 'toContain', 'toEqual', 'toHaveSize', 'toHaveBeenCalled', 'toHaveBeenCalledBefore', 'toHaveBeenCalledOnceWith', 'toHaveBeenCalledTimes', 'toHaveBeenCalledWith', 'toHaveClass', 'toHaveSpyInteractions', 'toMatch', 'toThrow', 'toThrowError', 'toThrowMatching' ], matchers = {}; for (const name of availableMatchers) { matchers[name] = jRequire[name](j$); } return matchers; }; getJasmineRequireObj().base = function(j$, jasmineGlobal) { /** * Maximum object depth the pretty printer will print to. * Set this to a lower value to speed up pretty printing if you have large objects. * @name jasmine.MAX_PRETTY_PRINT_DEPTH * @default 8 * @since 1.3.0 */ j$.MAX_PRETTY_PRINT_DEPTH = 8; /** * Maximum number of array elements to display when pretty printing objects. * This will also limit the number of keys and values displayed for an object. * Elements past this number will be ellipised. * @name jasmine.MAX_PRETTY_PRINT_ARRAY_LENGTH * @default 50 * @since 2.7.0 */ j$.MAX_PRETTY_PRINT_ARRAY_LENGTH = 50; /** * Maximum number of characters to display when pretty printing objects. * Characters past this number will be ellipised. * @name jasmine.MAX_PRETTY_PRINT_CHARS * @default 100 * @since 2.9.0 */ j$.MAX_PRETTY_PRINT_CHARS = 1000; /** * Default number of milliseconds Jasmine will wait for an asynchronous spec, * before, or after function to complete. This can be overridden on a case by * case basis by passing a time limit as the third argument to {@link it}, * {@link beforeEach}, {@link afterEach}, {@link beforeAll}, or * {@link afterAll}. The value must be no greater than the largest number of * milliseconds supported by setTimeout, which is usually 2147483647. * * While debugging tests, you may want to set this to a large number (or pass * a large number to one of the functions mentioned above) so that Jasmine * does not move on to after functions or the next spec while you're debugging. * @name jasmine.DEFAULT_TIMEOUT_INTERVAL * @default 5000 * @since 1.3.0 */ let DEFAULT_TIMEOUT_INTERVAL = 5000; Object.defineProperty(j$, 'DEFAULT_TIMEOUT_INTERVAL', { get: function() { return DEFAULT_TIMEOUT_INTERVAL; }, set: function(newValue) { j$.util.validateTimeout(newValue, 'jasmine.DEFAULT_TIMEOUT_INTERVAL'); DEFAULT_TIMEOUT_INTERVAL = newValue; } }); j$.getGlobal = function() { return jasmineGlobal; }; /** * Get the currently booted Jasmine Environment. * * @name jasmine.getEnv * @since 1.3.0 * @function * @return {Env} */ j$.getEnv = function(options) { const env = (j$.currentEnv_ = j$.currentEnv_ || new j$.Env(options)); //jasmine. singletons in here (setTimeout blah blah). return env; }; j$.isArray_ = function(value) { return j$.isA_('Array', value); }; j$.isObject_ = function(value) { return ( !j$.util.isUndefined(value) && value !== null && j$.isA_('Object', value) ); }; j$.isString_ = function(value) { return j$.isA_('String', value); }; j$.isNumber_ = function(value) { return j$.isA_('Number', value); }; j$.isFunction_ = function(value) { return j$.isA_('Function', value); }; j$.isAsyncFunction_ = function(value) { return j$.isA_('AsyncFunction', value); }; j$.isGeneratorFunction_ = function(value) { return j$.isA_('GeneratorFunction', value); }; j$.isTypedArray_ = function(value) { return ( j$.isA_('Float32Array', value) || j$.isA_('Float64Array', value) || j$.isA_('Int16Array', value) || j$.isA_('Int32Array', value) || j$.isA_('Int8Array', value) || j$.isA_('Uint16Array', value) || j$.isA_('Uint32Array', value) || j$.isA_('Uint8Array', value) || j$.isA_('Uint8ClampedArray', value) ); }; j$.isA_ = function(typeName, value) { return j$.getType_(value) === '[object ' + typeName + ']'; }; j$.isError_ = function(value) { if (!value) { return false; } if (value instanceof Error) { return true; } return typeof value.stack === 'string' && typeof value.message === 'string'; }; j$.isAsymmetricEqualityTester_ = function(obj) { return obj ? j$.isA_('Function', obj.asymmetricMatch) : false; }; j$.getType_ = function(value) { return Object.prototype.toString.apply(value); }; j$.isDomNode = function(obj) { // Node is a function, because constructors return typeof jasmineGlobal.Node !== 'undefined' ? obj instanceof jasmineGlobal.Node : obj !== null && typeof obj === 'object' && typeof obj.nodeType === 'number' && typeof obj.nodeName === 'string'; // return obj.nodeType > 0; }; j$.isMap = function(obj) { return ( obj !== null && typeof obj !== 'undefined' && obj.constructor === jasmineGlobal.Map ); }; j$.isSet = function(obj) { return ( obj !== null && typeof obj !== 'undefined' && obj.constructor === jasmineGlobal.Set ); }; j$.isWeakMap = function(obj) { return ( obj !== null && typeof obj !== 'undefined' && obj.constructor === jasmineGlobal.WeakMap ); }; j$.isURL = function(obj) { return ( obj !== null && typeof obj !== 'undefined' && obj.constructor === jasmineGlobal.URL ); }; j$.isIterable_ = function(value) { return value && !!value[Symbol.iterator]; }; j$.isDataView = function(obj) { return ( obj !== null && typeof obj !== 'undefined' && obj.constructor === jasmineGlobal.DataView ); }; j$.isPromise = function(obj) { return !!obj && obj.constructor === jasmineGlobal.Promise; }; j$.isPromiseLike = function(obj) { return !!obj && j$.isFunction_(obj.then); }; j$.fnNameFor = function(func) { if (func.name) { return func.name; } const matches = func.toString().match(/^\s*function\s*(\w+)\s*\(/) || func.toString().match(/^\s*\[object\s*(\w+)Constructor\]/); return matches ? matches[1] : ''; }; j$.isPending_ = function(promise) { const sentinel = {}; return Promise.race([promise, Promise.resolve(sentinel)]).then( function(result) { return result === sentinel; }, function() { return false; } ); }; /** * Get an {@link AsymmetricEqualityTester}, usable in any {@link matchers|matcher} that uses Jasmine's equality (e.g. {@link matchers#toEqual|toEqual}, {@link matchers#toContain|toContain}, or {@link matchers#toHaveBeenCalledWith|toHaveBeenCalledWith}), * that will succeed if the actual value being compared is an instance of the specified class/constructor. * @name jasmine.any * @since 1.3.0 * @function * @param {Constructor} clazz - The constructor to check against. */ j$.any = function(clazz) { return new j$.Any(clazz); }; /** * Get an {@link AsymmetricEqualityTester}, usable in any {@link matchers|matcher} that uses Jasmine's equality (e.g. {@link matchers#toEqual|toEqual}, {@link matchers#toContain|toContain}, or {@link matchers#toHaveBeenCalledWith|toHaveBeenCalledWith}), * that will succeed if the actual value being compared is not `null` and not `undefined`. * @name jasmine.anything * @since 2.2.0 * @function */ j$.anything = function() { return new j$.Anything(); }; /** * Get an {@link AsymmetricEqualityTester}, usable in any {@link matchers|matcher} that uses Jasmine's equality (e.g. {@link matchers#toEqual|toEqual}, {@link matchers#toContain|toContain}, or {@link matchers#toHaveBeenCalledWith|toHaveBeenCalledWith}), * that will succeed if the actual value being compared is `true` or anything truthy. * @name jasmine.truthy * @since 3.1.0 * @function */ j$.truthy = function() { return new j$.Truthy(); }; /** * Get an {@link AsymmetricEqualityTester}, usable in any {@link matchers|matcher} that uses Jasmine's equality (e.g. {@link matchers#toEqual|toEqual}, {@link matchers#toContain|toContain}, or {@link matchers#toHaveBeenCalledWith|toHaveBeenCalledWith}), * that will succeed if the actual value being compared is `null`, `undefined`, `0`, `false` or anything falsey. * @name jasmine.falsy * @since 3.1.0 * @function */ j$.falsy = function() { return new j$.Falsy(); }; /** * Get an {@link AsymmetricEqualityTester}, usable in any {@link matchers|matcher} that uses Jasmine's equality (e.g. {@link matchers#toEqual|toEqual}, {@link matchers#toContain|toContain}, or {@link matchers#toHaveBeenCalledWith|toHaveBeenCalledWith}), * that will succeed if the actual value being compared is empty. * @name jasmine.empty * @since 3.1.0 * @function */ j$.empty = function() { return new j$.Empty(); }; /** * Get an {@link AsymmetricEqualityTester}, usable in any {@link matchers|matcher} * that passes if the actual value is the same as the sample as determined * by the `===` operator. * @name jasmine.is * @function * @param {Object} sample - The value to compare the actual to. */ j$.is = function(sample) { return new j$.Is(sample); }; /** * Get an {@link AsymmetricEqualityTester}, usable in any {@link matchers|matcher} that uses Jasmine's equality (e.g. {@link matchers#toEqual|toEqual}, {@link matchers#toContain|toContain}, or {@link matchers#toHaveBeenCalledWith|toHaveBeenCalledWith}), * that will succeed if the actual value being compared is not empty. * @name jasmine.notEmpty * @since 3.1.0 * @function */ j$.notEmpty = function() { return new j$.NotEmpty(); }; /** * Get an {@link AsymmetricEqualityTester}, usable in any {@link matchers|matcher} that uses Jasmine's equality (e.g. {@link matchers#toEqual|toEqual}, {@link matchers#toContain|toContain}, or {@link matchers#toHaveBeenCalledWith|toHaveBeenCalledWith}), * that will succeed if the actual value being compared contains at least the keys and values. * @name jasmine.objectContaining * @since 1.3.0 * @function * @param {Object} sample - The subset of properties that _must_ be in the actual. */ j$.objectContaining = function(sample) { return new j$.ObjectContaining(sample); }; /** * Get an {@link AsymmetricEqualityTester}, usable in any {@link matchers|matcher} that uses Jasmine's equality (e.g. {@link matchers#toEqual|toEqual}, {@link matchers#toContain|toContain}, or {@link matchers#toHaveBeenCalledWith|toHaveBeenCalledWith}), * that will succeed if the actual value is a `String` that matches the `RegExp` or `String`. * @name jasmine.stringMatching * @since 2.2.0 * @function * @param {RegExp|String} expected */ j$.stringMatching = function(expected) { return new j$.StringMatching(expected); }; /** * Get an {@link AsymmetricEqualityTester}, usable in any {@link matchers|matcher} that uses Jasmine's equality (e.g. {@link matchers#toEqual|toEqual}, {@link matchers#toContain|toContain}, or {@link matchers#toHaveBeenCalledWith|toHaveBeenCalledWith}), * that will succeed if the actual value is a `String` that contains the specified `String`. * @name jasmine.stringContaining * @since 3.10.0 * @function * @param {String} expected */ j$.stringContaining = function(expected) { return new j$.StringContaining(expected); }; /** * Get an {@link AsymmetricEqualityTester}, usable in any {@link matchers|matcher} that uses Jasmine's equality (e.g. {@link matchers#toEqual|toEqual}, {@link matchers#toContain|toContain}, or {@link matchers#toHaveBeenCalledWith|toHaveBeenCalledWith}), * that will succeed if the actual value is an `Array` that contains at least the elements in the sample. * @name jasmine.arrayContaining * @since 2.2.0 * @function * @param {Array} sample */ j$.arrayContaining = function(sample) { return new j$.ArrayContaining(sample); }; /** * Get an {@link AsymmetricEqualityTester}, usable in any {@link matchers|matcher} that uses Jasmine's equality (e.g. {@link matchers#toEqual|toEqual}, {@link matchers#toContain|toContain}, or {@link matchers#toHaveBeenCalledWith|toHaveBeenCalledWith}), * that will succeed if the actual value is an `Array` that contains all of the elements in the sample in any order. * @name jasmine.arrayWithExactContents * @since 2.8.0 * @function * @param {Array} sample */ j$.arrayWithExactContents = function(sample) { return new j$.ArrayWithExactContents(sample); }; /** * Get an {@link AsymmetricEqualityTester}, usable in any {@link matchers|matcher} that uses Jasmine's equality (e.g. {@link matchers#toEqual|toEqual}, {@link matchers#toContain|toContain}, or {@link matchers#toHaveBeenCalledWith|toHaveBeenCalledWith}), * that will succeed if every key/value pair in the sample passes the deep equality comparison * with at least one key/value pair in the actual value being compared * @name jasmine.mapContaining * @since 3.5.0 * @function * @param {Map} sample - The subset of items that _must_ be in the actual. */ j$.mapContaining = function(sample) { return new j$.MapContaining(sample); }; /** * Get an {@link AsymmetricEqualityTester}, usable in any {@link matchers|matcher} that uses Jasmine's equality (e.g. {@link matchers#toEqual|toEqual}, {@link matchers#toContain|toContain}, or {@link matchers#toHaveBeenCalledWith|toHaveBeenCalledWith}), * that will succeed if every item in the sample passes the deep equality comparison * with at least one item in the actual value being compared * @name jasmine.setContaining * @since 3.5.0 * @function * @param {Set} sample - The subset of items that _must_ be in the actual. */ j$.setContaining = function(sample) { return new j$.SetContaining(sample); }; /** * Determines whether the provided function is a Jasmine spy. * @name jasmine.isSpy * @since 2.0.0 * @function * @param {Function} putativeSpy - The function to check. * @return {Boolean} */ j$.isSpy = function(putativeSpy) { if (!putativeSpy) { return false; } return ( putativeSpy.and instanceof j$.SpyStrategy && putativeSpy.calls instanceof j$.CallTracker ); }; /** * Logs a message for use in debugging. If the spec fails, trace messages * will be included in the {@link SpecResult|result} passed to the * reporter's specDone method. * * This method should be called only when a spec (including any associated * beforeEach or afterEach functions) is running. * @function * @name jasmine.debugLog * @since 4.0.0 * @param {String} msg - The message to log */ j$.debugLog = function(msg) { j$.getEnv().debugLog(msg); }; /** * Replaces Jasmine's global error handling with a spy. This prevents Jasmine * from treating uncaught exceptions and unhandled promise rejections * as spec failures and allows them to be inspected using the spy's * {@link Spy#calls|calls property} and related matchers such as * {@link matchers#toHaveBeenCalledWith|toHaveBeenCalledWith}. * * After installing the spy, spyOnGlobalErrorsAsync immediately calls its * argument, which must be an async or promise-returning function. The spy * will be passed as the first argument to that callback. Normal error * handling will be restored when the promise returned from the callback is * settled. * * Note: The JavaScript runtime may deliver uncaught error events and unhandled * rejection events asynchronously, especially in browsers. If the event * occurs after the promise returned from the callback is settled, it won't * be routed to the spy even if the underlying error occurred previously. * It's up to you to ensure that the returned promise isn't resolved until * all of the error/rejection events that you want to handle have occurred. * * You must await the return value of spyOnGlobalErrorsAsync. * @name jasmine.spyOnGlobalErrorsAsync * @function * @async * @param {AsyncFunction} fn - A function to run, during which the global error spy will be effective * @example * it('demonstrates global error spies', async function() { * await jasmine.spyOnGlobalErrorsAsync(async function(globalErrorSpy) { * setTimeout(function() { * throw new Error('the expected error'); * }); * await new Promise(function(resolve) { * setTimeout(resolve); * }); * const expected = new Error('the expected error'); * expect(globalErrorSpy).toHaveBeenCalledWith(expected); * }); * }); */ j$.spyOnGlobalErrorsAsync = async function(fn) { await jasmine.getEnv().spyOnGlobalErrorsAsync(fn); }; }; getJasmineRequireObj().util = function(j$) { const util = {}; util.isUndefined = function(obj) { return obj === void 0; }; util.clone = function(obj) { if (Object.prototype.toString.apply(obj) === '[object Array]') { return obj.slice(); } const cloned = {}; for (const prop in obj) { if (obj.hasOwnProperty(prop)) { cloned[prop] = obj[prop]; } } return cloned; }; util.cloneArgs = function(args) { return Array.from(args).map(function(arg) { const str = Object.prototype.toString.apply(arg), primitives = /^\[object (Boolean|String|RegExp|Number)/; // All falsey values are either primitives, `null`, or `undefined. if (!arg || str.match(primitives)) { return arg; } else if (str === '[object Date]') { return new Date(arg.valueOf()); } else { return j$.util.clone(arg); } }); }; util.getPropertyDescriptor = function(obj, methodName) { let descriptor, proto = obj; do { descriptor = Object.getOwnPropertyDescriptor(proto, methodName); proto = Object.getPrototypeOf(proto); } while (!descriptor && proto); return descriptor; }; util.has = function(obj, key) { return Object.prototype.hasOwnProperty.call(obj, key); }; util.errorWithStack = function errorWithStack() { // Don't throw and catch. That makes it harder for users to debug their // code with exception breakpoints, and it's unnecessary since all // supported environments populate new Error().stack return new Error(); }; function callerFile() { const trace = new j$.StackTrace(util.errorWithStack()); return trace.frames[2].file; } util.jasmineFile = (function() { let result; return function() { if (!result) { result = callerFile(); } return result; }; })(); util.validateTimeout = function(timeout, msgPrefix) { // Timeouts are implemented with setTimeout, which only supports a limited // range of values. The limit is unspecified, as is the behavior when it's // exceeded. But on all currently supported JS runtimes, setTimeout calls // the callback immediately when the timeout is greater than 2147483647 // (the maximum value of a signed 32 bit integer). const max = 2147483647; if (timeout > max) { throw new Error( (msgPrefix || 'Timeout value') + ' cannot be greater than ' + max ); } }; return util; }; getJasmineRequireObj().Spec = function(j$) { function Spec(attrs) { this.expectationFactory = attrs.expectationFactory; this.asyncExpectationFactory = attrs.asyncExpectationFactory; this.resultCallback = attrs.resultCallback || function() {}; this.id = attrs.id; this.description = attrs.description || ''; this.queueableFn = attrs.queueableFn; this.beforeAndAfterFns = attrs.beforeAndAfterFns || function() { return { befores: [], afters: [] }; }; this.userContext = attrs.userContext || function() { return {}; }; this.onStart = attrs.onStart || function() {}; this.autoCleanClosures = attrs.autoCleanClosures === undefined ? true : !!attrs.autoCleanClosures; this.getSpecName = attrs.getSpecName || function() { return ''; }; this.onLateError = attrs.onLateError || function() {}; this.catchingExceptions = attrs.catchingExceptions || function() { return true; }; this.throwOnExpectationFailure = !!attrs.throwOnExpectationFailure; this.timer = attrs.timer || new j$.Timer(); if (!this.queueableFn.fn) { this.exclude(); } /** * @typedef SpecResult * @property {String} id - The unique id of this spec. * @property {String} description - The description passed to the {@link it} that created this spec. * @property {String} fullName - The full description including all ancestors of this spec. * @property {Expectation[]} failedExpectations - The list of expectations that failed during execution of this spec. * @property {Expectation[]} passedExpectations - The list of expectations that passed during execution of this spec. * @property {Expectation[]} deprecationWarnings - The list of deprecation warnings that occurred during execution this spec. * @property {String} pendingReason - If the spec is {@link pending}, this will be the reason. * @property {String} status - Once the spec has completed, this string represents the pass/fail status of this spec. * @property {number} duration - The time in ms used by the spec execution, including any before/afterEach. * @property {Object} properties - User-supplied properties, if any, that were set using {@link Env#setSpecProperty} * @property {DebugLogEntry[]|null} debugLogs - Messages, if any, that were logged using {@link jasmine.debugLog} during a failing spec. * @since 2.0.0 */ this.result = { id: this.id, description: this.description, fullName: this.getFullName(), failedExpectations: [], passedExpectations: [], deprecationWarnings: [], pendingReason: '', duration: null, properties: null, debugLogs: null }; this.reportedDone = false; } Spec.prototype.addExpectationResult = function(passed, data, isError) { const expectationResult = j$.buildExpectationResult(data); if (passed) { this.result.passedExpectations.push(expectationResult); } else { if (this.reportedDone) { this.onLateError(expectationResult); } else { this.result.failedExpectations.push(expectationResult); // TODO: refactor so that we don't need to override cached status if (this.result.status) { this.result.status = 'failed'; } } if (this.throwOnExpectationFailure && !isError) { throw new j$.errors.ExpectationFailed(); } } }; Spec.prototype.setSpecProperty = function(key, value) { this.result.properties = this.result.properties || {}; this.result.properties[key] = value; }; Spec.prototype.expect = function(actual) { return this.expectationFactory(actual, this); }; Spec.prototype.expectAsync = function(actual) { return this.asyncExpectationFactory(actual, this); }; Spec.prototype.execute = function( queueRunnerFactory, onComplete, excluded, failSpecWithNoExp ) { const onStart = { fn: done => { this.timer.start(); this.onStart(this, done); } }; const complete = { fn: done => { if (this.autoCleanClosures) { this.queueableFn.fn = null; } this.result.status = this.status(excluded, failSpecWithNoExp); this.result.duration = this.timer.elapsed(); if (this.result.status !== 'failed') { this.result.debugLogs = null; } this.resultCallback(this.result, done); }, type: 'specCleanup' }; const fns = this.beforeAndAfterFns(); const runnerConfig = { isLeaf: true, queueableFns: [...fns.befores, this.queueableFn, ...fns.afters], onException: e => this.handleException(e), onMultipleDone: () => { // Issue a deprecation. Include the context ourselves and pass // ignoreRunnable: true, since getting here always means that we've already // moved on and the current runnable isn't the one that caused the problem. this.onLateError( new Error( 'An asynchronous spec, beforeEach, or afterEach function called its ' + "'done' callback more than once.\n(in spec: " + this.getFullName() + ')' ) ); }, onComplete: () => { if (this.result.status === 'failed') { onComplete(new j$.StopExecutionError('spec failed')); } else { onComplete(); } }, userContext: this.userContext(), runnableName: this.getFullName.bind(this) }; if (this.markedPending || excluded === true) { runnerConfig.queueableFns = []; } runnerConfig.queueableFns.unshift(onStart); runnerConfig.queueableFns.push(complete); queueRunnerFactory(runnerConfig); }; Spec.prototype.reset = function() { this.result = { id: this.id, description: this.description, fullName: this.getFullName(), failedExpectations: [], passedExpectations: [], deprecationWarnings: [], pendingReason: this.excludeMessage, duration: null, properties: null, debugLogs: null }; this.markedPending = this.markedExcluding; this.reportedDone = false; }; Spec.prototype.handleException = function handleException(e) { if (Spec.isPendingSpecException(e)) { this.pend(extractCustomPendingMessage(e)); return; } if (e instanceof j$.errors.ExpectationFailed) { return; } this.addExpectationResult( false, { matcherName: '', passed: false, expected: '', actual: '', error: e }, true ); }; /* * Marks state as pending * @param {string} [message] An optional reason message */ Spec.prototype.pend = function(message) { this.markedPending = true; if (message) { this.result.pendingReason = message; } }; /* * Like {@link Spec#pend}, but pending state will survive {@link Spec#reset} * Useful for fit, xit, where pending state remains. * @param {string} [message] An optional reason message */ Spec.prototype.exclude = function(message) { this.markedExcluding = true; if (this.message) { this.excludeMessage = message; } this.pend(message); }; Spec.prototype.getResult = function() { this.result.status = this.status(); return this.result; }; Spec.prototype.status = function(excluded, failSpecWithNoExpectations) { if (excluded === true) { return 'excluded'; } if (this.markedPending) { return 'pending'; } if ( this.result.failedExpectations.length > 0 || (failSpecWithNoExpectations && this.result.failedExpectations.length + this.result.passedExpectations.length === 0) ) { return 'failed'; } return 'passed'; }; Spec.prototype.getFullName = function() { return this.getSpecName(this); }; Spec.prototype.addDeprecationWarning = function(deprecation) { if (typeof deprecation === 'string') { deprecation = { message: deprecation }; } this.result.deprecationWarnings.push( j$.buildExpectationResult(deprecation) ); }; Spec.prototype.debugLog = function(msg) { if (!this.result.debugLogs) { this.result.debugLogs = []; } /** * @typedef DebugLogEntry * @property {String} message - The message that was passed to {@link jasmine.debugLog}. * @property {number} timestamp - The time when the entry was added, in * milliseconds from the spec's start time */ this.result.debugLogs.push({ message: msg, timestamp: this.timer.elapsed() }); }; const extractCustomPendingMessage = function(e) { const fullMessage = e.toString(), boilerplateStart = fullMessage.indexOf(Spec.pendingSpecExceptionMessage), boilerplateEnd = boilerplateStart + Spec.pendingSpecExceptionMessage.length; return fullMessage.slice(boilerplateEnd); }; Spec.pendingSpecExceptionMessage = '=> marked Pending'; Spec.isPendingSpecException = function(e) { return !!( e && e.toString && e.toString().indexOf(Spec.pendingSpecExceptionMessage) !== -1 ); }; /** * @interface Spec * @see Configuration#specFilter * @since 2.0.0 */ Object.defineProperty(Spec.prototype, 'metadata', { get: function() { if (!this.metadata_) { this.metadata_ = { /** * The unique ID of this spec. * @name Spec#id * @readonly * @type {string} * @since 2.0.0 */ id: this.id, /** * The description passed to the {@link it} that created this spec. * @name Spec#description * @readonly * @type {string} * @since 2.0.0 */ description: this.description, /** * The full description including all ancestors of this spec. * @name Spec#getFullName * @function * @returns {string} * @since 2.0.0 */ getFullName: this.getFullName.bind(this) }; } return this.metadata_; } }); return Spec; }; getJasmineRequireObj().Order = function() { function Order(options) { this.random = 'random' in options ? options.random : true; const seed = (this.seed = options.seed || generateSeed()); this.sort = this.random ? randomOrder : naturalOrder; function naturalOrder(items) { return items; } function randomOrder(items) { const copy = items.slice(); copy.sort(function(a, b) { return jenkinsHash(seed + a.id) - jenkinsHash(seed + b.id); }); return copy; } function generateSeed() { return String(Math.random()).slice(-5); } // Bob Jenkins One-at-a-Time Hash algorithm is a non-cryptographic hash function // used to get a different output when the key changes slightly. // We use your return to sort the children randomly in a consistent way when // used in conjunction with a seed function jenkinsHash(key) { let hash, i; for (hash = i = 0; i < key.length; ++i) { hash += key.charCodeAt(i); hash += hash << 10; hash ^= hash >> 6; } hash += hash << 3; hash ^= hash >> 11; hash += hash << 15; return hash; } } return Order; }; getJasmineRequireObj().Env = function(j$) { /** * @class Env * @since 2.0.0 * @classdesc The Jasmine environment.
* _Note:_ Do not construct this directly. You can obtain the Env instance by * calling {@link jasmine.getEnv}. * @hideconstructor */ function Env(options) { options = options || {}; const self = this; const global = options.global || j$.getGlobal(); const realSetTimeout = global.setTimeout; const realClearTimeout = global.clearTimeout; const clearStack = j$.getClearStack(global); this.clock = new j$.Clock( global, function() { return new j$.DelayedFunctionScheduler(); }, new j$.MockDate(global) ); const globalErrors = new j$.GlobalErrors(); const installGlobalErrors = (function() { let installed = false; return function() { if (!installed) { globalErrors.install(); installed = true; } }; })(); const runableResources = new j$.RunableResources({ getCurrentRunableId: function() { const r = runner.currentRunable(); return r ? r.id : null; }, globalErrors }); let reporter; let topSuite; let runner; /** * This represents the available options to configure Jasmine. * Options that are not provided will use their default values. * @see Env#configure * @interface Configuration * @since 3.3.0 */ const config = { /** * Whether to randomize spec execution order * @name Configuration#random * @since 3.3.0 * @type Boolean * @default true */ random: true, /** * Seed to use as the basis of randomization. * Null causes the seed to be determined randomly at the start of execution. * @name Configuration#seed * @since 3.3.0 * @type (number|string) * @default null */ seed: null, /** * Whether to stop execution of the suite after the first spec failure * @name Configuration#stopOnSpecFailure * @since 3.9.0 * @type Boolean * @default false */ stopOnSpecFailure: false, /** * Whether to fail the spec if it ran no expectations. By default * a spec that ran no expectations is reported as passed. Setting this * to true will report such spec as a failure. * @name Configuration#failSpecWithNoExpectations * @since 3.5.0 * @type Boolean * @default false */ failSpecWithNoExpectations: false, /** * Whether to cause specs to only have one expectation failure. * @name Configuration#stopSpecOnExpectationFailure * @since 3.3.0 * @type Boolean * @default false */ stopSpecOnExpectationFailure: false, /** * A function that takes a spec and returns true if it should be executed * or false if it should be skipped. * @callback SpecFilter * @param {Spec} spec - The spec that the filter is being applied to. * @return boolean */ /** * Function to use to filter specs * @name Configuration#specFilter * @since 3.3.0 * @type SpecFilter * @default A function that always returns true. */ specFilter: function() { return true; }, /** * Whether or not reporters should hide disabled specs from their output. * Currently only supported by Jasmine's HTMLReporter * @name Configuration#hideDisabled * @since 3.3.0 * @type Boolean * @default false */ hideDisabled: false, /** * Clean closures when a suite is done running (done by clearing the stored function reference). * This prevents memory leaks, but you won't be able to run jasmine multiple times. * @name Configuration#autoCleanClosures * @since 3.10.0 * @type boolean * @default true */ autoCleanClosures: true, /** * Whether or not to issue warnings for certain deprecated functionality * every time it's used. If not set or set to false, deprecation warnings * for methods that tend to be called frequently will be issued only once * or otherwise throttled to to prevent the suite output from being flooded * with warnings. * @name Configuration#verboseDeprecations * @since 3.6.0 * @type Boolean * @default false */ verboseDeprecations: false }; if (!options.suppressLoadErrors) { installGlobalErrors(); globalErrors.pushListener(function loadtimeErrorHandler( message, filename, lineno, colNo, err ) { topSuite.result.failedExpectations.push({ passed: false, globalErrorType: 'load', message: message, stack: err && err.stack, filename: filename, lineno: lineno }); }); } /** * Configure your jasmine environment * @name Env#configure * @since 3.3.0 * @argument {Configuration} configuration * @function */ this.configure = function(configuration) { const booleanProps = [ 'random', 'failSpecWithNoExpectations', 'hideDisabled', 'stopOnSpecFailure', 'stopSpecOnExpectationFailure', 'autoCleanClosures' ]; booleanProps.forEach(function(prop) { if (typeof configuration[prop] !== 'undefined') { config[prop] = !!configuration[prop]; } }); if (configuration.specFilter) { config.specFilter = configuration.specFilter; } if (typeof configuration.seed !== 'undefined') { config.seed = configuration.seed; } if (configuration.hasOwnProperty('verboseDeprecations')) { config.verboseDeprecations = configuration.verboseDeprecations; deprecator.verboseDeprecations(config.verboseDeprecations); } }; /** * Get the current configuration for your jasmine environment * @name Env#configuration * @since 3.3.0 * @function * @returns {Configuration} */ this.configuration = function() { const result = {}; for (const property in config) { result[property] = config[property]; } return result; }; this.setDefaultSpyStrategy = function(defaultStrategyFn) { runableResources.setDefaultSpyStrategy(defaultStrategyFn); }; this.addSpyStrategy = function(name, fn) { runableResources.customSpyStrategies()[name] = fn; }; this.addCustomEqualityTester = function(tester) { runableResources.customEqualityTesters().push(tester); }; this.addMatchers = function(matchersToAdd) { runableResources.addCustomMatchers(matchersToAdd); }; this.addAsyncMatchers = function(matchersToAdd) { runableResources.addCustomAsyncMatchers(matchersToAdd); }; this.addCustomObjectFormatter = function(formatter) { runableResources.customObjectFormatters().push(formatter); }; j$.Expectation.addCoreMatchers(j$.matchers); j$.Expectation.addAsyncCoreMatchers(j$.asyncMatchers); const expectationFactory = function(actual, spec) { return j$.Expectation.factory({ matchersUtil: runableResources.makeMatchersUtil(), customMatchers: runableResources.customMatchers(), actual: actual, addExpectationResult: addExpectationResult }); function addExpectationResult(passed, result) { return spec.addExpectationResult(passed, result); } }; // TODO: Unify recordLateError with recordLateExpectation? The extra // diagnostic info added by the latter is probably useful in most cases. function recordLateError(error) { const isExpectationResult = error.matcherName !== undefined && error.passed !== undefined; const result = isExpectationResult ? error : j$.buildExpectationResult({ error, passed: false, matcherName: '', expected: '', actual: '' }); routeLateFailure(result); } function recordLateExpectation(runable, runableType, result) { const delayedExpectationResult = {}; Object.keys(result).forEach(function(k) { delayedExpectationResult[k] = result[k]; }); delayedExpectationResult.passed = false; delayedExpectationResult.globalErrorType = 'lateExpectation'; delayedExpectationResult.message = runableType + ' "' + runable.getFullName() + '" ran a "' + result.matcherName + '" expectation after it finished.\n'; if (result.message) { delayedExpectationResult.message += 'Message: "' + result.message + '"\n'; } delayedExpectationResult.message += '1. Did you forget to return or await the result of expectAsync?\n' + '2. Was done() invoked before an async operation completed?\n' + '3. Did an expectation follow a call to done()?'; topSuite.result.failedExpectations.push(delayedExpectationResult); } function routeLateFailure(expectationResult) { // Report the result on the nearest ancestor suite that hasn't already // been reported done. for (let r = runner.currentRunable(); r; r = r.parentSuite) { if (!r.reportedDone) { if (r === topSuite) { expectationResult.globalErrorType = 'lateError'; } r.result.failedExpectations.push(expectationResult); return; } } // If we get here, all results have been reported and there's nothing we // can do except log the result and hope the user sees it. console.error('Jasmine received a result after the suite finished:'); console.error(expectationResult); } const asyncExpectationFactory = function(actual, spec, runableType) { return j$.Expectation.asyncFactory({ matchersUtil: runableResources.makeMatchersUtil(), customAsyncMatchers: runableResources.customAsyncMatchers(), actual: actual, addExpectationResult: addExpectationResult }); function addExpectationResult(passed, result) { if (runner.currentRunable() !== spec) { recordLateExpectation(spec, runableType, result); } return spec.addExpectationResult(passed, result); } }; /** * Causes a deprecation warning to be logged to the console and reported to * reporters. * * The optional second parameter is an object that can have either of the * following properties: * * omitStackTrace: Whether to omit the stack trace. Optional. Defaults to * false. This option is ignored if the deprecation is an Error. Set this * when the stack trace will not contain anything that helps the user find * the source of the deprecation. * * ignoreRunnable: Whether to log the deprecation on the root suite, ignoring * the spec or suite that's running when it happens. Optional. Defaults to * false. * * @name Env#deprecated * @since 2.99 * @function * @param {String|Error} deprecation The deprecation message * @param {Object} [options] Optional extra options, as described above */ this.deprecated = function(deprecation, options) { const runable = runner.currentRunable() || topSuite; deprecator.addDeprecationWarning(runable, deprecation, options); }; function queueRunnerFactory(options) { options.clearStack = options.clearStack || clearStack; options.timeout = { setTimeout: realSetTimeout, clearTimeout: realClearTimeout }; options.fail = self.fail; options.globalErrors = globalErrors; options.onException = options.onException || function(e) { (runner.currentRunable() || topSuite).handleException(e); }; options.deprecated = self.deprecated; new j$.QueueRunner(options).execute(); } const suiteBuilder = new j$.SuiteBuilder({ env: this, expectationFactory, asyncExpectationFactory, onLateError: recordLateError, specResultCallback, specStarted, queueRunnerFactory }); topSuite = suiteBuilder.topSuite; const deprecator = new j$.Deprecator(topSuite); /** * Provides the root suite, through which all suites and specs can be * accessed. * @function * @name Env#topSuite * @return {Suite} the root suite * @since 2.0.0 */ this.topSuite = function() { return topSuite.metadata; }; /** * This represents the available reporter callback for an object passed to {@link Env#addReporter}. * @interface Reporter * @see custom_reporter */ reporter = new j$.ReportDispatcher( [ /** * `jasmineStarted` is called after all of the specs have been loaded, but just before execution starts. * @function * @name Reporter#jasmineStarted * @param {JasmineStartedInfo} suiteInfo Information about the full Jasmine suite that is being run * @param {Function} [done] Used to specify to Jasmine that this callback is asynchronous and Jasmine should wait until it has been called before moving on. * @returns {} Optionally return a Promise instead of using `done` to cause Jasmine to wait for completion. * @see async */ 'jasmineStarted', /** * When the entire suite has finished execution `jasmineDone` is called * @function * @name Reporter#jasmineDone * @param {JasmineDoneInfo} suiteInfo Information about the full Jasmine suite that just finished running. * @param {Function} [done] Used to specify to Jasmine that this callback is asynchronous and Jasmine should wait until it has been called before moving on. * @returns {} Optionally return a Promise instead of using `done` to cause Jasmine to wait for completion. * @see async */ 'jasmineDone', /** * `suiteStarted` is invoked when a `describe` starts to run * @function * @name Reporter#suiteStarted * @param {SuiteResult} result Information about the individual {@link describe} being run * @param {Function} [done] Used to specify to Jasmine that this callback is asynchronous and Jasmine should wait until it has been called before moving on. * @returns {} Optionally return a Promise instead of using `done` to cause Jasmine to wait for completion. * @see async */ 'suiteStarted', /** * `suiteDone` is invoked when all of the child specs and suites for a given suite have been run * * While jasmine doesn't require any specific functions, not defining a `suiteDone` will make it impossible for a reporter to know when a suite has failures in an `afterAll`. * @function * @name Reporter#suiteDone * @param {SuiteResult} result * @param {Function} [done] Used to specify to Jasmine that this callback is asynchronous and Jasmine should wait until it has been called before moving on. * @returns {} Optionally return a Promise instead of using `done` to cause Jasmine to wait for completion. * @see async */ 'suiteDone', /** * `specStarted` is invoked when an `it` starts to run (including associated `beforeEach` functions) * @function * @name Reporter#specStarted * @param {SpecResult} result Information about the individual {@link it} being run * @param {Function} [done] Used to specify to Jasmine that this callback is asynchronous and Jasmine should wait until it has been called before moving on. * @returns {} Optionally return a Promise instead of using `done` to cause Jasmine to wait for completion. * @see async */ 'specStarted', /** * `specDone` is invoked when an `it` and its associated `beforeEach` and `afterEach` functions have been run. * * While jasmine doesn't require any specific functions, not defining a `specDone` will make it impossible for a reporter to know when a spec has failed. * @function * @name Reporter#specDone * @param {SpecResult} result * @param {Function} [done] Used to specify to Jasmine that this callback is asynchronous and Jasmine should wait until it has been called before moving on. * @returns {} Optionally return a Promise instead of using `done` to cause Jasmine to wait for completion. * @see async */ 'specDone' ], function(options) { options.SkipPolicy = j$.NeverSkipPolicy; return queueRunnerFactory(options); }, recordLateError ); runner = new j$.Runner({ topSuite, totalSpecsDefined: () => suiteBuilder.totalSpecsDefined, focusedRunables: () => suiteBuilder.focusedRunables, runableResources, reporter, queueRunnerFactory, getConfig: () => config, reportSpecDone }); /** * Executes the specs. * * If called with no parameters or with a falsy value as the first parameter, * all specs will be executed except those that are excluded by a * [spec filter]{@link Configuration#specFilter} or other mechanism. If the * first parameter is a list of spec/suite IDs, only those specs/suites will * be run. * * Both parameters are optional, but a completion callback is only valid as * the second parameter. To specify a completion callback but not a list of * specs/suites to run, pass null or undefined as the first parameter. The * completion callback is supported for backward compatibility. In most * cases it will be more convenient to use the returned promise instead. * * execute should not be called more than once unless the env has been * configured with `{autoCleanClosures: false}`. * * execute returns a promise. The promise will be resolved to the same * {@link JasmineDoneInfo|overall result} that's passed to a reporter's * `jasmineDone` method, even if the suite did not pass. To determine * whether the suite passed, check the value that the promise resolves to * or use a {@link Reporter}. * * @name Env#execute * @since 2.0.0 * @function * @param {(string[])=} runablesToRun IDs of suites and/or specs to run * @param {Function=} onComplete Function that will be called after all specs have run * @return {Promise} */ this.execute = function(runablesToRun, onComplete) { installGlobalErrors(); return runner.execute(runablesToRun).then(function(jasmineDoneInfo) { if (onComplete) { onComplete(); } return jasmineDoneInfo; }); }; /** * Add a custom reporter to the Jasmine environment. * @name Env#addReporter * @since 2.0.0 * @function * @param {Reporter} reporterToAdd The reporter to be added. * @see custom_reporter */ this.addReporter = function(reporterToAdd) { reporter.addReporter(reporterToAdd); }; /** * Provide a fallback reporter if no other reporters have been specified. * @name Env#provideFallbackReporter * @since 2.5.0 * @function * @param {Reporter} reporterToAdd The reporter * @see custom_reporter */ this.provideFallbackReporter = function(reporterToAdd) { reporter.provideFallbackReporter(reporterToAdd); }; /** * Clear all registered reporters * @name Env#clearReporters * @since 2.5.2 * @function */ this.clearReporters = function() { reporter.clearReporters(); }; /** * Configures whether Jasmine should allow the same function to be spied on * more than once during the execution of a spec. By default, spying on * a function that is already a spy will cause an error. * @name Env#allowRespy * @function * @since 2.5.0 * @param {boolean} allow Whether to allow respying */ this.allowRespy = function(allow) { runableResources.spyRegistry.allowRespy(allow); }; this.spyOn = function() { return runableResources.spyRegistry.spyOn.apply( runableResources.spyRegistry, arguments ); }; this.spyOnProperty = function() { return runableResources.spyRegistry.spyOnProperty.apply( runableResources.spyRegistry, arguments ); }; this.spyOnAllFunctions = function() { return runableResources.spyRegistry.spyOnAllFunctions.apply( runableResources.spyRegistry, arguments ); }; this.createSpy = function(name, originalFn) { return runableResources.spyFactory.createSpy(name, originalFn); }; this.createSpyObj = function(baseName, methodNames, propertyNames) { return runableResources.spyFactory.createSpyObj( baseName, methodNames, propertyNames ); }; this.spyOnGlobalErrorsAsync = async function(fn) { const spy = this.createSpy('global error handler'); const associatedRunable = runner.currentRunable(); let cleanedUp = false; globalErrors.setOverrideListener(spy, () => { if (!cleanedUp) { const message = 'Global error spy was not uninstalled. (Did you ' + 'forget to await the return value of spyOnGlobalErrorsAsync?)'; associatedRunable.addExpectationResult(false, { matcherName: '', passed: false, expected: '', actual: '', message, error: null }); } cleanedUp = true; }); try { const maybePromise = fn(spy); if (!j$.isPromiseLike(maybePromise)) { throw new Error( 'The callback to spyOnGlobalErrorsAsync must be an async or promise-returning function' ); } await maybePromise; } finally { if (!cleanedUp) { cleanedUp = true; globalErrors.removeOverrideListener(); } } }; function ensureIsNotNested(method) { const runable = runner.currentRunable(); if (runable !== null && runable !== undefined) { throw new Error( "'" + method + "' should only be used in 'describe' function" ); } } this.describe = function(description, definitionFn) { ensureIsNotNested('describe'); return suiteBuilder.describe(description, definitionFn).metadata; }; this.xdescribe = function(description, definitionFn) { ensureIsNotNested('xdescribe'); return suiteBuilder.xdescribe(description, definitionFn).metadata; }; this.fdescribe = function(description, definitionFn) { ensureIsNotNested('fdescribe'); return suiteBuilder.fdescribe(description, definitionFn).metadata; }; function specResultCallback(spec, result, next) { runableResources.clearForRunable(spec.id); runner.currentSpec = null; if (result.status === 'failed') { runner.hasFailures = true; } reportSpecDone(spec, result, next); } function specStarted(spec, suite, next) { runner.currentSpec = spec; runableResources.initForRunable(spec.id, suite.id); reporter.specStarted(spec.result).then(next); } function reportSpecDone(spec, result, next) { spec.reportedDone = true; reporter.specDone(result).then(next); } this.it = function(description, fn, timeout) { ensureIsNotNested('it'); return suiteBuilder.it(description, fn, timeout).metadata; }; this.xit = function(description, fn, timeout) { ensureIsNotNested('xit'); return suiteBuilder.xit(description, fn, timeout).metadata; }; this.fit = function(description, fn, timeout) { ensureIsNotNested('fit'); return suiteBuilder.fit(description, fn, timeout).metadata; }; /** * Sets a user-defined property that will be provided to reporters as part of the properties field of {@link SpecResult} * @name Env#setSpecProperty * @since 3.6.0 * @function * @param {String} key The name of the property * @param {*} value The value of the property */ this.setSpecProperty = function(key, value) { if ( !runner.currentRunable() || runner.currentRunable() == runner.currentSuite() ) { throw new Error( "'setSpecProperty' was used when there was no current spec" ); } runner.currentRunable().setSpecProperty(key, value); }; /** * Sets a user-defined property that will be provided to reporters as part of the properties field of {@link SuiteResult} * @name Env#setSuiteProperty * @since 3.6.0 * @function * @param {String} key The name of the property * @param {*} value The value of the property */ this.setSuiteProperty = function(key, value) { if (!runner.currentSuite()) { throw new Error( "'setSuiteProperty' was used when there was no current suite" ); } runner.currentSuite().setSuiteProperty(key, value); }; this.debugLog = function(msg) { const maybeSpec = runner.currentRunable(); if (!maybeSpec || !maybeSpec.debugLog) { throw new Error("'debugLog' was called when there was no current spec"); } maybeSpec.debugLog(msg); }; this.expect = function(actual) { if (!runner.currentRunable()) { throw new Error( "'expect' was used when there was no current spec, this could be because an asynchronous test timed out" ); } return runner.currentRunable().expect(actual); }; this.expectAsync = function(actual) { if (!runner.currentRunable()) { throw new Error( "'expectAsync' was used when there was no current spec, this could be because an asynchronous test timed out" ); } return runner.currentRunable().expectAsync(actual); }; this.beforeEach = function(beforeEachFunction, timeout) { ensureIsNotNested('beforeEach'); suiteBuilder.beforeEach(beforeEachFunction, timeout); }; this.beforeAll = function(beforeAllFunction, timeout) { ensureIsNotNested('beforeAll'); suiteBuilder.beforeAll(beforeAllFunction, timeout); }; this.afterEach = function(afterEachFunction, timeout) { ensureIsNotNested('afterEach'); suiteBuilder.afterEach(afterEachFunction, timeout); }; this.afterAll = function(afterAllFunction, timeout) { ensureIsNotNested('afterAll'); suiteBuilder.afterAll(afterAllFunction, timeout); }; this.pending = function(message) { let fullMessage = j$.Spec.pendingSpecExceptionMessage; if (message) { fullMessage += message; } throw fullMessage; }; this.fail = function(error) { if (!runner.currentRunable()) { throw new Error( "'fail' was used when there was no current spec, this could be because an asynchronous test timed out" ); } let message = 'Failed'; if (error) { message += ': '; if (error.message) { message += error.message; } else if (j$.isString_(error)) { message += error; } else { // pretty print all kind of objects. This includes arrays. const pp = runableResources.makePrettyPrinter(); message += pp(error); } } runner.currentRunable().addExpectationResult(false, { matcherName: '', passed: false, expected: '', actual: '', message: message, error: error && error.message ? error : null }); if (config.stopSpecOnExpectationFailure) { throw new Error(message); } }; this.cleanup_ = function() { if (globalErrors) { globalErrors.uninstall(); } }; } return Env; }; getJasmineRequireObj().JsApiReporter = function(j$) { /** * @name jsApiReporter * @classdesc {@link Reporter} added by default in `boot.js` to record results for retrieval in javascript code. An instance is made available as `jsApiReporter` on the global object. * @class * @hideconstructor */ function JsApiReporter(options) { const timer = options.timer || new j$.Timer(); let status = 'loaded'; this.started = false; this.finished = false; this.runDetails = {}; this.jasmineStarted = function() { this.started = true; status = 'started'; timer.start(); }; let executionTime; this.jasmineDone = function(runDetails) { this.finished = true; this.runDetails = runDetails; executionTime = timer.elapsed(); status = 'done'; }; /** * Get the current status for the Jasmine environment. * @name jsApiReporter#status * @since 2.0.0 * @function * @return {String} - One of `loaded`, `started`, or `done` */ this.status = function() { return status; }; const suites = [], suites_hash = {}; this.suiteStarted = function(result) { suites_hash[result.id] = result; }; this.suiteDone = function(result) { storeSuite(result); }; /** * Get the results for a set of suites. * * Retrievable in slices for easier serialization. * @name jsApiReporter#suiteResults * @since 2.1.0 * @function * @param {Number} index - The position in the suites list to start from. * @param {Number} length - Maximum number of suite results to return. * @return {SuiteResult[]} */ this.suiteResults = function(index, length) { return suites.slice(index, index + length); }; function storeSuite(result) { suites.push(result); suites_hash[result.id] = result; } /** * Get all of the suites in a single object, with their `id` as the key. * @name jsApiReporter#suites * @since 2.0.0 * @function * @return {Object} - Map of suite id to {@link SuiteResult} */ this.suites = function() { return suites_hash; }; const specs = []; this.specDone = function(result) { specs.push(result); }; /** * Get the results for a set of specs. * * Retrievable in slices for easier serialization. * @name jsApiReporter#specResults * @since 2.0.0 * @function * @param {Number} index - The position in the specs list to start from. * @param {Number} length - Maximum number of specs results to return. * @return {SpecResult[]} */ this.specResults = function(index, length) { return specs.slice(index, index + length); }; /** * Get all spec results. * @name jsApiReporter#specs * @since 2.0.0 * @function * @return {SpecResult[]} */ this.specs = function() { return specs; }; /** * Get the number of milliseconds it took for the full Jasmine suite to run. * @name jsApiReporter#executionTime * @since 2.0.0 * @function * @return {Number} */ this.executionTime = function() { return executionTime; }; } return JsApiReporter; }; getJasmineRequireObj().Any = function(j$) { function Any(expectedObject) { if (typeof expectedObject === 'undefined') { throw new TypeError( 'jasmine.any() expects to be passed a constructor function. ' + 'Please pass one or use jasmine.anything() to match any object.' ); } this.expectedObject = expectedObject; } Any.prototype.asymmetricMatch = function(other) { if (this.expectedObject == String) { return typeof other == 'string' || other instanceof String; } if (this.expectedObject == Number) { return typeof other == 'number' || other instanceof Number; } if (this.expectedObject == Function) { return typeof other == 'function' || other instanceof Function; } if (this.expectedObject == Object) { return other !== null && typeof other == 'object'; } if (this.expectedObject == Boolean) { return typeof other == 'boolean'; } if (typeof Symbol != 'undefined' && this.expectedObject == Symbol) { return typeof other == 'symbol'; } return other instanceof this.expectedObject; }; Any.prototype.jasmineToString = function() { return ''; }; return Any; }; getJasmineRequireObj().Anything = function(j$) { function Anything() {} Anything.prototype.asymmetricMatch = function(other) { return !j$.util.isUndefined(other) && other !== null; }; Anything.prototype.jasmineToString = function() { return ''; }; return Anything; }; getJasmineRequireObj().ArrayContaining = function(j$) { function ArrayContaining(sample) { this.sample = sample; } ArrayContaining.prototype.asymmetricMatch = function(other, matchersUtil) { if (!j$.isArray_(this.sample)) { throw new Error( 'You must provide an array to arrayContaining, not ' + j$.basicPrettyPrinter_(this.sample) + '.' ); } // If the actual parameter is not an array, we can fail immediately, since it couldn't // possibly be an "array containing" anything. However, we also want an empty sample // array to match anything, so we need to double-check we aren't in that case if (!j$.isArray_(other) && this.sample.length > 0) { return false; } for (const item of this.sample) { if (!matchersUtil.contains(other, item)) { return false; } } return true; }; ArrayContaining.prototype.jasmineToString = function(pp) { return ''; }; return ArrayContaining; }; getJasmineRequireObj().ArrayWithExactContents = function(j$) { function ArrayWithExactContents(sample) { this.sample = sample; } ArrayWithExactContents.prototype.asymmetricMatch = function( other, matchersUtil ) { if (!j$.isArray_(this.sample)) { throw new Error( 'You must provide an array to arrayWithExactContents, not ' + j$.basicPrettyPrinter_(this.sample) + '.' ); } if (this.sample.length !== other.length) { return false; } for (const item of this.sample) { if (!matchersUtil.contains(other, item)) { return false; } } return true; }; ArrayWithExactContents.prototype.jasmineToString = function(pp) { return ''; }; return ArrayWithExactContents; }; getJasmineRequireObj().Empty = function(j$) { function Empty() {} Empty.prototype.asymmetricMatch = function(other) { if (j$.isString_(other) || j$.isArray_(other) || j$.isTypedArray_(other)) { return other.length === 0; } if (j$.isMap(other) || j$.isSet(other)) { return other.size === 0; } if (j$.isObject_(other)) { return Object.keys(other).length === 0; } return false; }; Empty.prototype.jasmineToString = function() { return ''; }; return Empty; }; getJasmineRequireObj().Falsy = function(j$) { function Falsy() {} Falsy.prototype.asymmetricMatch = function(other) { return !other; }; Falsy.prototype.jasmineToString = function() { return ''; }; return Falsy; }; getJasmineRequireObj().Is = function(j$) { class Is { constructor(expected) { this.expected_ = expected; } asymmetricMatch(actual) { return actual === this.expected_; } jasmineToString(pp) { return ``; } } return Is; }; getJasmineRequireObj().MapContaining = function(j$) { function MapContaining(sample) { if (!j$.isMap(sample)) { throw new Error( 'You must provide a map to `mapContaining`, not ' + j$.basicPrettyPrinter_(sample) ); } this.sample = sample; } MapContaining.prototype.asymmetricMatch = function(other, matchersUtil) { if (!j$.isMap(other)) return false; for (const [key, value] of this.sample) { // for each key/value pair in `sample` // there should be at least one pair in `other` whose key and value both match let hasMatch = false; for (const [oKey, oValue] of other) { if ( matchersUtil.equals(oKey, key) && matchersUtil.equals(oValue, value) ) { hasMatch = true; break; } } if (!hasMatch) { return false; } } return true; }; MapContaining.prototype.jasmineToString = function(pp) { return ''; }; return MapContaining; }; getJasmineRequireObj().NotEmpty = function(j$) { function NotEmpty() {} NotEmpty.prototype.asymmetricMatch = function(other) { if (j$.isString_(other) || j$.isArray_(other) || j$.isTypedArray_(other)) { return other.length !== 0; } if (j$.isMap(other) || j$.isSet(other)) { return other.size !== 0; } if (j$.isObject_(other)) { return Object.keys(other).length !== 0; } return false; }; NotEmpty.prototype.jasmineToString = function() { return ''; }; return NotEmpty; }; getJasmineRequireObj().ObjectContaining = function(j$) { function ObjectContaining(sample) { this.sample = sample; } function hasProperty(obj, property) { if (!obj || typeof obj !== 'object') { return false; } if (Object.prototype.hasOwnProperty.call(obj, property)) { return true; } return hasProperty(Object.getPrototypeOf(obj), property); } ObjectContaining.prototype.asymmetricMatch = function(other, matchersUtil) { if (typeof this.sample !== 'object') { throw new Error( "You must provide an object to objectContaining, not '" + this.sample + "'." ); } if (typeof other !== 'object') { return false; } for (const property in this.sample) { if ( !hasProperty(other, property) || !matchersUtil.equals(this.sample[property], other[property]) ) { return false; } } return true; }; ObjectContaining.prototype.valuesForDiff_ = function(other, pp) { if (!j$.isObject_(other)) { return { self: this.jasmineToString(pp), other: other }; } const filteredOther = {}; Object.keys(this.sample).forEach(function(k) { // eq short-circuits comparison of objects that have different key sets, // so include all keys even if undefined. filteredOther[k] = other[k]; }); return { self: this.sample, other: filteredOther }; }; ObjectContaining.prototype.jasmineToString = function(pp) { return ''; }; return ObjectContaining; }; getJasmineRequireObj().SetContaining = function(j$) { function SetContaining(sample) { if (!j$.isSet(sample)) { throw new Error( 'You must provide a set to `setContaining`, not ' + j$.basicPrettyPrinter_(sample) ); } this.sample = sample; } SetContaining.prototype.asymmetricMatch = function(other, matchersUtil) { if (!j$.isSet(other)) return false; for (const item of this.sample) { // for each item in `sample` there should be at least one matching item in `other` // (not using `matchersUtil.contains` because it compares set members by reference, // not by deep value equality) let hasMatch = false; for (const oItem of other) { if (matchersUtil.equals(oItem, item)) { hasMatch = true; break; } } if (!hasMatch) { return false; } } return true; }; SetContaining.prototype.jasmineToString = function(pp) { return ''; }; return SetContaining; }; getJasmineRequireObj().StringContaining = function(j$) { function StringContaining(expected) { if (!j$.isString_(expected)) { throw new Error('Expected is not a String'); } this.expected = expected; } StringContaining.prototype.asymmetricMatch = function(other) { if (!j$.isString_(other)) { // Arrays, etc. don't match no matter what their indexOf returns. return false; } return other.indexOf(this.expected) !== -1; }; StringContaining.prototype.jasmineToString = function() { return ''; }; return StringContaining; }; getJasmineRequireObj().StringMatching = function(j$) { function StringMatching(expected) { if (!j$.isString_(expected) && !j$.isA_('RegExp', expected)) { throw new Error('Expected is not a String or a RegExp'); } this.regexp = new RegExp(expected); } StringMatching.prototype.asymmetricMatch = function(other) { return this.regexp.test(other); }; StringMatching.prototype.jasmineToString = function() { return ''; }; return StringMatching; }; getJasmineRequireObj().Truthy = function(j$) { function Truthy() {} Truthy.prototype.asymmetricMatch = function(other) { return !!other; }; Truthy.prototype.jasmineToString = function() { return ''; }; return Truthy; }; //TODO: expectation result may make more sense as a presentation of an expectation. getJasmineRequireObj().buildExpectationResult = function(j$) { function buildExpectationResult(options) { const exceptionFormatter = new j$.ExceptionFormatter(); /** * @typedef Expectation * @property {String} matcherName - The name of the matcher that was executed for this expectation. * @property {String} message - The failure message for the expectation. * @property {String} stack - The stack trace for the failure if available. * @property {Boolean} passed - Whether the expectation passed or failed. * @property {Object} expected - If the expectation failed, what was the expected value. * @property {Object} actual - If the expectation failed, what actual value was produced. * @property {String|undefined} globalErrorType - The type of an error that * is reported on the top suite. Valid values are undefined, "afterAll", * "load", "lateExpectation", and "lateError". */ const result = { matcherName: options.matcherName, message: message(), stack: options.omitStackTrace ? '' : stack(), passed: options.passed }; if (!result.passed) { result.expected = options.expected; result.actual = options.actual; if (options.error && !j$.isString_(options.error)) { if ('code' in options.error) { result.code = options.error.code; } if ( options.error.code === 'ERR_ASSERTION' && options.expected === '' && options.actual === '' ) { result.expected = options.error.expected; result.actual = options.error.actual; result.matcherName = 'assert ' + options.error.operator; } } } return result; function message() { if (options.passed) { return 'Passed.'; } else if (options.message) { return options.message; } else if (options.error) { return exceptionFormatter.message(options.error); } return ''; } function stack() { if (options.passed) { return ''; } let error = options.error; if (!error) { if (options.errorForStack) { error = options.errorForStack; } else if (options.stack) { error = options; } else { try { throw new Error(message()); } catch (e) { error = e; } } } // Omit the message from the stack trace because it will be // included elsewhere. return exceptionFormatter.stack(error, { omitMessage: true }); } } return buildExpectationResult; }; getJasmineRequireObj().CallTracker = function(j$) { /** * @namespace Spy#calls * @since 2.0.0 */ function CallTracker() { let calls = []; const opts = {}; this.track = function(context) { if (opts.cloneArgs) { context.args = j$.util.cloneArgs(context.args); } calls.push(context); }; /** * Check whether this spy has been invoked. * @name Spy#calls#any * @since 2.0.0 * @function * @return {Boolean} */ this.any = function() { return !!calls.length; }; /** * Get the number of invocations of this spy. * @name Spy#calls#count * @since 2.0.0 * @function * @return {Integer} */ this.count = function() { return calls.length; }; /** * Get the arguments that were passed to a specific invocation of this spy. * @name Spy#calls#argsFor * @since 2.0.0 * @function * @param {Integer} index The 0-based invocation index. * @return {Array} */ this.argsFor = function(index) { const call = calls[index]; return call ? call.args : []; }; /** * Get the "this" object that was passed to a specific invocation of this spy. * @name Spy#calls#thisFor * @since 3.8.0 * @function * @param {Integer} index The 0-based invocation index. * @return {Object?} */ this.thisFor = function(index) { const call = calls[index]; return call ? call.object : undefined; }; /** * Get the raw calls array for this spy. * @name Spy#calls#all * @since 2.0.0 * @function * @return {Spy.callData[]} */ this.all = function() { return calls; }; /** * Get all of the arguments for each invocation of this spy in the order they were received. * @name Spy#calls#allArgs * @since 2.0.0 * @function * @return {Array} */ this.allArgs = function() { return calls.map(c => c.args); }; /** * Get the first invocation of this spy. * @name Spy#calls#first * @since 2.0.0 * @function * @return {ObjecSpy.callData} */ this.first = function() { return calls[0]; }; /** * Get the most recent invocation of this spy. * @name Spy#calls#mostRecent * @since 2.0.0 * @function * @return {ObjecSpy.callData} */ this.mostRecent = function() { return calls[calls.length - 1]; }; /** * Reset this spy as if it has never been called. * @name Spy#calls#reset * @since 2.0.0 * @function */ this.reset = function() { calls = []; }; /** * Set this spy to do a shallow clone of arguments passed to each invocation. * @name Spy#calls#saveArgumentsByValue * @since 2.5.0 * @function */ this.saveArgumentsByValue = function() { opts.cloneArgs = true; }; } return CallTracker; }; getJasmineRequireObj().clearStack = function(j$) { const maxInlineCallCount = 10; function browserQueueMicrotaskImpl(global) { const { setTimeout, queueMicrotask } = global; let currentCallCount = 0; return function clearStack(fn) { currentCallCount++; if (currentCallCount < maxInlineCallCount) { queueMicrotask(fn); } else { currentCallCount = 0; setTimeout(fn); } }; } function nodeQueueMicrotaskImpl(global) { const { queueMicrotask } = global; return function(fn) { queueMicrotask(fn); }; } function messageChannelImpl(global) { const { MessageChannel, setTimeout } = global; const channel = new MessageChannel(); let head = {}; let tail = head; let taskRunning = false; channel.port1.onmessage = function() { head = head.next; const task = head.task; delete head.task; if (taskRunning) { setTimeout(task, 0); } else { try { taskRunning = true; task(); } finally { taskRunning = false; } } }; let currentCallCount = 0; return function clearStack(fn) { currentCallCount++; if (currentCallCount < maxInlineCallCount) { tail = tail.next = { task: fn }; channel.port2.postMessage(0); } else { currentCallCount = 0; setTimeout(fn); } }; } function getClearStack(global) { const NODE_JS = global.process && global.process.versions && typeof global.process.versions.node === 'string'; const SAFARI = global.navigator && /^((?!chrome|android).)*safari/i.test(global.navigator.userAgent); if (NODE_JS) { // Unlike browsers, Node doesn't require us to do a periodic setTimeout // so we avoid the overhead. return nodeQueueMicrotaskImpl(global); } else if ( SAFARI || j$.util.isUndefined(global.MessageChannel) /* tests */ ) { // queueMicrotask is dramatically faster than MessageChannel in Safari, // at least through version 16. // Some of our own integration tests provide a mock queueMicrotask in all // environments because it's simpler to mock than MessageChannel. return browserQueueMicrotaskImpl(global); } else { // MessageChannel is faster than queueMicrotask in supported browsers // other than Safari. return messageChannelImpl(global); } } return getClearStack; }; getJasmineRequireObj().Clock = function() { /* global process */ const NODE_JS = typeof process !== 'undefined' && process.versions && typeof process.versions.node === 'string'; /** * @class Clock * @since 1.3.0 * @classdesc Jasmine's mock clock is used when testing time dependent code.
* _Note:_ Do not construct this directly. You can get the current clock with * {@link jasmine.clock}. * @hideconstructor */ function Clock(global, delayedFunctionSchedulerFactory, mockDate) { const realTimingFunctions = { setTimeout: global.setTimeout, clearTimeout: global.clearTimeout, setInterval: global.setInterval, clearInterval: global.clearInterval }; const fakeTimingFunctions = { setTimeout: setTimeout, clearTimeout: clearTimeout, setInterval: setInterval, clearInterval: clearInterval }; let installed = false; let delayedFunctionScheduler; let timer; this.FakeTimeout = FakeTimeout; /** * Install the mock clock over the built-in methods. * @name Clock#install * @since 2.0.0 * @function * @return {Clock} */ this.install = function() { if (!originalTimingFunctionsIntact()) { throw new Error( 'Jasmine Clock was unable to install over custom global timer functions. Is the clock already installed?' ); } replace(global, fakeTimingFunctions); timer = fakeTimingFunctions; delayedFunctionScheduler = delayedFunctionSchedulerFactory(); installed = true; return this; }; /** * Uninstall the mock clock, returning the built-in methods to their places. * @name Clock#uninstall * @since 2.0.0 * @function */ this.uninstall = function() { delayedFunctionScheduler = null; mockDate.uninstall(); replace(global, realTimingFunctions); timer = realTimingFunctions; installed = false; }; /** * Execute a function with a mocked Clock * * The clock will be {@link Clock#install|install}ed before the function is called and {@link Clock#uninstall|uninstall}ed in a `finally` after the function completes. * @name Clock#withMock * @since 2.3.0 * @function * @param {Function} closure The function to be called. */ this.withMock = function(closure) { this.install(); try { closure(); } finally { this.uninstall(); } }; /** * Instruct the installed Clock to also mock the date returned by `new Date()` * @name Clock#mockDate * @since 2.1.0 * @function * @param {Date} [initialDate=now] The `Date` to provide. */ this.mockDate = function(initialDate) { mockDate.install(initialDate); }; this.setTimeout = function(fn, delay, params) { return Function.prototype.apply.apply(timer.setTimeout, [ global, arguments ]); }; this.setInterval = function(fn, delay, params) { return Function.prototype.apply.apply(timer.setInterval, [ global, arguments ]); }; this.clearTimeout = function(id) { return Function.prototype.call.apply(timer.clearTimeout, [global, id]); }; this.clearInterval = function(id) { return Function.prototype.call.apply(timer.clearInterval, [global, id]); }; /** * Tick the Clock forward, running any enqueued timeouts along the way * @name Clock#tick * @since 1.3.0 * @function * @param {int} millis The number of milliseconds to tick. */ this.tick = function(millis) { if (installed) { delayedFunctionScheduler.tick(millis, function(millis) { mockDate.tick(millis); }); } else { throw new Error( 'Mock clock is not installed, use jasmine.clock().install()' ); } }; return this; function originalTimingFunctionsIntact() { return ( global.setTimeout === realTimingFunctions.setTimeout && global.clearTimeout === realTimingFunctions.clearTimeout && global.setInterval === realTimingFunctions.setInterval && global.clearInterval === realTimingFunctions.clearInterval ); } function replace(dest, source) { for (const prop in source) { dest[prop] = source[prop]; } } function setTimeout(fn, delay) { if (!NODE_JS) { return delayedFunctionScheduler.scheduleFunction( fn, delay, argSlice(arguments, 2) ); } const timeout = new FakeTimeout(); delayedFunctionScheduler.scheduleFunction( fn, delay, argSlice(arguments, 2), false, timeout ); return timeout; } function clearTimeout(id) { return delayedFunctionScheduler.removeFunctionWithId(id); } function setInterval(fn, interval) { if (!NODE_JS) { return delayedFunctionScheduler.scheduleFunction( fn, interval, argSlice(arguments, 2), true ); } const timeout = new FakeTimeout(); delayedFunctionScheduler.scheduleFunction( fn, interval, argSlice(arguments, 2), true, timeout ); return timeout; } function clearInterval(id) { return delayedFunctionScheduler.removeFunctionWithId(id); } function argSlice(argsObj, n) { return Array.prototype.slice.call(argsObj, n); } } /** * Mocks Node.js Timeout class */ function FakeTimeout() {} FakeTimeout.prototype.ref = function() { return this; }; FakeTimeout.prototype.unref = function() { return this; }; return Clock; }; getJasmineRequireObj().CompleteOnFirstErrorSkipPolicy = function(j$) { function CompleteOnFirstErrorSkipPolicy(queueableFns) { this.queueableFns_ = queueableFns; this.erroredFnIx_ = null; } CompleteOnFirstErrorSkipPolicy.prototype.skipTo = function(lastRanFnIx) { let i; for ( i = lastRanFnIx + 1; i < this.queueableFns_.length && this.shouldSkip_(i); i++ ) {} return i; }; CompleteOnFirstErrorSkipPolicy.prototype.fnErrored = function(fnIx) { this.erroredFnIx_ = fnIx; }; CompleteOnFirstErrorSkipPolicy.prototype.shouldSkip_ = function(fnIx) { if (this.erroredFnIx_ === null) { return false; } const fn = this.queueableFns_[fnIx]; const candidateSuite = fn.suite; const errorSuite = this.queueableFns_[this.erroredFnIx_].suite; const wasCleanupFn = fn.type === 'afterEach' || fn.type === 'afterAll' || fn.type === 'specCleanup'; return ( !wasCleanupFn || (candidateSuite && isDescendent(candidateSuite, errorSuite)) ); }; function isDescendent(candidate, ancestor) { if (!candidate.parentSuite) { return false; } else if (candidate.parentSuite === ancestor) { return true; } else { return isDescendent(candidate.parentSuite, ancestor); } } return CompleteOnFirstErrorSkipPolicy; }; getJasmineRequireObj().DelayedFunctionScheduler = function(j$) { function DelayedFunctionScheduler() { this.scheduledLookup_ = []; this.scheduledFunctions_ = {}; this.currentTime_ = 0; this.delayedFnCount_ = 0; this.deletedKeys_ = []; this.tick = function(millis, tickDate) { millis = millis || 0; const endTime = this.currentTime_ + millis; this.runScheduledFunctions_(endTime, tickDate); }; this.scheduleFunction = function( funcToCall, millis, params, recurring, timeoutKey, runAtMillis ) { let f; if (typeof funcToCall === 'string') { f = function() { // eslint-disable-next-line no-eval return eval(funcToCall); }; } else { f = funcToCall; } millis = millis || 0; timeoutKey = timeoutKey || ++this.delayedFnCount_; runAtMillis = runAtMillis || this.currentTime_ + millis; const funcToSchedule = { runAtMillis: runAtMillis, funcToCall: f, recurring: recurring, params: params, timeoutKey: timeoutKey, millis: millis }; if (runAtMillis in this.scheduledFunctions_) { this.scheduledFunctions_[runAtMillis].push(funcToSchedule); } else { this.scheduledFunctions_[runAtMillis] = [funcToSchedule]; this.scheduledLookup_.push(runAtMillis); this.scheduledLookup_.sort(function(a, b) { return a - b; }); } return timeoutKey; }; this.removeFunctionWithId = function(timeoutKey) { this.deletedKeys_.push(timeoutKey); for (const runAtMillis in this.scheduledFunctions_) { const funcs = this.scheduledFunctions_[runAtMillis]; const i = indexOfFirstToPass(funcs, function(func) { return func.timeoutKey === timeoutKey; }); if (i > -1) { if (funcs.length === 1) { delete this.scheduledFunctions_[runAtMillis]; this.deleteFromLookup_(runAtMillis); } else { funcs.splice(i, 1); } // intervals get rescheduled when executed, so there's never more // than a single scheduled function with a given timeoutKey break; } } }; return this; } DelayedFunctionScheduler.prototype.runScheduledFunctions_ = function( endTime, tickDate ) { tickDate = tickDate || function() {}; if ( this.scheduledLookup_.length === 0 || this.scheduledLookup_[0] > endTime ) { if (endTime >= this.currentTime_) { tickDate(endTime - this.currentTime_); this.currentTime_ = endTime; } return; } do { this.deletedKeys_ = []; const newCurrentTime = this.scheduledLookup_.shift(); if (newCurrentTime >= this.currentTime_) { tickDate(newCurrentTime - this.currentTime_); this.currentTime_ = newCurrentTime; } const funcsToRun = this.scheduledFunctions_[this.currentTime_]; delete this.scheduledFunctions_[this.currentTime_]; for (const fn of funcsToRun) { if (fn.recurring) { this.reschedule_(fn); } } for (const fn of funcsToRun) { if (this.deletedKeys_.includes(fn.timeoutKey)) { // skip a timeoutKey deleted whilst we were running return; } fn.funcToCall.apply(null, fn.params || []); } this.deletedKeys_ = []; } while ( this.scheduledLookup_.length > 0 && // checking first if we're out of time prevents setTimeout(0) // scheduled in a funcToRun from forcing an extra iteration this.currentTime_ !== endTime && this.scheduledLookup_[0] <= endTime ); // ran out of functions to call, but still time left on the clock if (endTime >= this.currentTime_) { tickDate(endTime - this.currentTime_); this.currentTime_ = endTime; } }; DelayedFunctionScheduler.prototype.reschedule_ = function(scheduledFn) { this.scheduleFunction( scheduledFn.funcToCall, scheduledFn.millis, scheduledFn.params, true, scheduledFn.timeoutKey, scheduledFn.runAtMillis + scheduledFn.millis ); }; DelayedFunctionScheduler.prototype.deleteFromLookup_ = function(key) { const value = Number(key); const i = indexOfFirstToPass(this.scheduledLookup_, function(millis) { return millis === value; }); if (i > -1) { this.scheduledLookup_.splice(i, 1); } }; function indexOfFirstToPass(array, testFn) { let index = -1; for (let i = 0; i < array.length; ++i) { if (testFn(array[i])) { index = i; break; } } return index; } return DelayedFunctionScheduler; }; getJasmineRequireObj().Deprecator = function(j$) { function Deprecator(topSuite) { this.topSuite_ = topSuite; this.verbose_ = false; this.toSuppress_ = []; } const verboseNote = 'Note: This message will be shown only once. Set the verboseDeprecations ' + 'config property to true to see every occurrence.'; Deprecator.prototype.verboseDeprecations = function(enabled) { this.verbose_ = enabled; }; // runnable is a spec or a suite. // deprecation is a string or an Error. // See Env#deprecated for a description of the options argument. Deprecator.prototype.addDeprecationWarning = function( runnable, deprecation, options ) { options = options || {}; if (!this.verbose_ && !j$.isError_(deprecation)) { if (this.toSuppress_.indexOf(deprecation) !== -1) { return; } this.toSuppress_.push(deprecation); } this.log_(runnable, deprecation, options); this.report_(runnable, deprecation, options); }; Deprecator.prototype.log_ = function(runnable, deprecation, options) { if (j$.isError_(deprecation)) { console.error(deprecation); return; } let context; if (runnable === this.topSuite_ || options.ignoreRunnable) { context = ''; } else if (runnable.children) { context = ' (in suite: ' + runnable.getFullName() + ')'; } else { context = ' (in spec: ' + runnable.getFullName() + ')'; } if (!options.omitStackTrace) { context += '\n' + this.stackTrace_(); } if (!this.verbose_) { context += '\n' + verboseNote; } console.error('DEPRECATION: ' + deprecation + context); }; Deprecator.prototype.stackTrace_ = function() { const formatter = new j$.ExceptionFormatter(); return formatter.stack(j$.util.errorWithStack()).replace(/^Error\n/m, ''); }; Deprecator.prototype.report_ = function(runnable, deprecation, options) { if (options.ignoreRunnable) { runnable = this.topSuite_; } if (j$.isError_(deprecation)) { runnable.addDeprecationWarning(deprecation); return; } if (!this.verbose_) { deprecation += '\n' + verboseNote; } runnable.addDeprecationWarning({ message: deprecation, omitStackTrace: options.omitStackTrace || false }); }; return Deprecator; }; getJasmineRequireObj().errors = function() { function ExpectationFailed() {} ExpectationFailed.prototype = new Error(); ExpectationFailed.prototype.constructor = ExpectationFailed; return { ExpectationFailed: ExpectationFailed }; }; getJasmineRequireObj().ExceptionFormatter = function(j$) { const ignoredProperties = [ 'name', 'message', 'stack', 'fileName', 'sourceURL', 'line', 'lineNumber', 'column', 'description', 'jasmineMessage' ]; function ExceptionFormatter(options) { const jasmineFile = (options && options.jasmineFile) || j$.util.jasmineFile(); this.message = function(error) { let message = ''; if (error.jasmineMessage) { message += error.jasmineMessage; } else if (error.name && error.message) { message += error.name + ': ' + error.message; } else if (error.message) { message += error.message; } else { message += error.toString() + ' thrown'; } if (error.fileName || error.sourceURL) { message += ' in ' + (error.fileName || error.sourceURL); } if (error.line || error.lineNumber) { message += ' (line ' + (error.line || error.lineNumber) + ')'; } return message; }; this.stack = function(error, { omitMessage } = {}) { if (!error || !error.stack) { return null; } const lines = this.stack_(error, { messageHandling: omitMessage ? 'omit' : undefined }); return lines.join('\n'); }; // messageHandling can be falsy (unspecified), 'omit', or 'require' this.stack_ = function(error, { messageHandling }) { let lines = formatProperties(error).split('\n'); if (lines[lines.length - 1] === '') { lines.pop(); } const stackTrace = new j$.StackTrace(error); lines = lines.concat(filterJasmine(stackTrace)); if (messageHandling === 'require') { lines.unshift(stackTrace.message || 'Error: ' + error.message); } else if (messageHandling !== 'omit' && stackTrace.message) { lines.unshift(stackTrace.message); } if (error.cause) { const substack = this.stack_(error.cause, { messageHandling: 'require' }); substack[0] = 'Caused by: ' + substack[0]; lines = lines.concat(substack); } return lines; }; function filterJasmine(stackTrace) { const result = []; const jasmineMarker = stackTrace.style === 'webkit' ? '' : ' at '; stackTrace.frames.forEach(function(frame) { if (frame.file !== jasmineFile) { result.push(frame.raw); } else if (result[result.length - 1] !== jasmineMarker) { result.push(jasmineMarker); } }); return result; } function formatProperties(error) { if (!(error instanceof Object)) { return; } const result = {}; let empty = true; for (const prop in error) { if (ignoredProperties.includes(prop)) { continue; } result[prop] = error[prop]; empty = false; } if (!empty) { return 'error properties: ' + j$.basicPrettyPrinter_(result) + '\n'; } return ''; } } return ExceptionFormatter; }; getJasmineRequireObj().Expectation = function(j$) { /** * Matchers that come with Jasmine out of the box. * @namespace matchers */ function Expectation(options) { this.expector = new j$.Expector(options); const customMatchers = options.customMatchers || {}; for (const matcherName in customMatchers) { this[matcherName] = wrapSyncCompare( matcherName, customMatchers[matcherName] ); } } /** * Add some context for an {@link expect} * @function * @name matchers#withContext * @since 3.3.0 * @param {String} message - Additional context to show when the matcher fails * @return {matchers} */ Expectation.prototype.withContext = function withContext(message) { return addFilter(this, new ContextAddingFilter(message)); }; /** * Invert the matcher following this {@link expect} * @member * @name matchers#not * @since 1.3.0 * @type {matchers} * @example * expect(something).not.toBe(true); */ Object.defineProperty(Expectation.prototype, 'not', { get: function() { return addFilter(this, syncNegatingFilter); } }); /** * Asynchronous matchers that operate on an actual value which is a promise, * and return a promise. * * Most async matchers will wait indefinitely for the promise to be resolved * or rejected, resulting in a spec timeout if that never happens. If you * expect that the promise will already be resolved or rejected at the time * the matcher is called, you can use the {@link async-matchers#already} * modifier to get a faster failure with a more helpful message. * * Note: Specs must await the result of each async matcher, return the * promise returned by the matcher, or return a promise that's derived from * the one returned by the matcher. Otherwise the matcher will not be * evaluated before the spec completes. * * @example * // Good * await expectAsync(aPromise).toBeResolved(); * @example * // Good * return expectAsync(aPromise).toBeResolved(); * @example * // Good * return expectAsync(aPromise).toBeResolved() * .then(function() { * // more spec code * }); * @example * // Bad * expectAsync(aPromise).toBeResolved(); * @namespace async-matchers */ function AsyncExpectation(options) { this.expector = new j$.Expector(options); const customAsyncMatchers = options.customAsyncMatchers || {}; for (const matcherName in customAsyncMatchers) { this[matcherName] = wrapAsyncCompare( matcherName, customAsyncMatchers[matcherName] ); } } /** * Add some context for an {@link expectAsync} * @function * @name async-matchers#withContext * @since 3.3.0 * @param {String} message - Additional context to show when the async matcher fails * @return {async-matchers} */ AsyncExpectation.prototype.withContext = function withContext(message) { return addFilter(this, new ContextAddingFilter(message)); }; /** * Invert the matcher following this {@link expectAsync} * @member * @name async-matchers#not * @type {async-matchers} * @example * await expectAsync(myPromise).not.toBeResolved(); * @example * return expectAsync(myPromise).not.toBeResolved(); */ Object.defineProperty(AsyncExpectation.prototype, 'not', { get: function() { return addFilter(this, asyncNegatingFilter); } }); /** * Fail as soon as possible if the actual is pending. * Otherwise evaluate the matcher. * @member * @name async-matchers#already * @since 3.8.0 * @type {async-matchers} * @example * await expectAsync(myPromise).already.toBeResolved(); * @example * return expectAsync(myPromise).already.toBeResolved(); */ Object.defineProperty(AsyncExpectation.prototype, 'already', { get: function() { return addFilter(this, expectSettledPromiseFilter); } }); function wrapSyncCompare(name, matcherFactory) { return function() { const result = this.expector.compare(name, matcherFactory, arguments); this.expector.processResult(result); }; } function wrapAsyncCompare(name, matcherFactory) { return function() { // Capture the call stack here, before we go async, so that it will contain // frames that are relevant to the user instead of just parts of Jasmine. const errorForStack = j$.util.errorWithStack(); return this.expector .compare(name, matcherFactory, arguments) .then(result => { this.expector.processResult(result, errorForStack); }); }; } function addCoreMatchers(prototype, matchers, wrapper) { for (const matcherName in matchers) { const matcher = matchers[matcherName]; prototype[matcherName] = wrapper(matcherName, matcher); } } function addFilter(source, filter) { const result = Object.create(source); result.expector = source.expector.addFilter(filter); return result; } function negatedFailureMessage(result, matcherName, args, matchersUtil) { if (result.message) { if (j$.isFunction_(result.message)) { return result.message(); } else { return result.message; } } args = args.slice(); args.unshift(true); args.unshift(matcherName); return matchersUtil.buildFailureMessage.apply(matchersUtil, args); } function negate(result) { result.pass = !result.pass; return result; } const syncNegatingFilter = { selectComparisonFunc: function(matcher) { function defaultNegativeCompare() { return negate(matcher.compare.apply(null, arguments)); } return matcher.negativeCompare || defaultNegativeCompare; }, buildFailureMessage: negatedFailureMessage }; const asyncNegatingFilter = { selectComparisonFunc: function(matcher) { function defaultNegativeCompare() { return matcher.compare.apply(this, arguments).then(negate); } return matcher.negativeCompare || defaultNegativeCompare; }, buildFailureMessage: negatedFailureMessage }; const expectSettledPromiseFilter = { selectComparisonFunc: function(matcher) { return function(actual) { const matcherArgs = arguments; return j$.isPending_(actual).then(function(isPending) { if (isPending) { return { pass: false, message: 'Expected a promise to be settled (via ' + 'expectAsync(...).already) but it was pending.' }; } else { return matcher.compare.apply(null, matcherArgs); } }); }; } }; function ContextAddingFilter(message) { this.message = message; } ContextAddingFilter.prototype.modifyFailureMessage = function(msg) { if (msg.indexOf('\n') === -1) { return this.message + ': ' + msg; } else { return this.message + ':\n' + indent(msg); } }; function indent(s) { return s.replace(/^/gm, ' '); } return { factory: function(options) { return new Expectation(options || {}); }, addCoreMatchers: function(matchers) { addCoreMatchers(Expectation.prototype, matchers, wrapSyncCompare); }, asyncFactory: function(options) { return new AsyncExpectation(options || {}); }, addAsyncCoreMatchers: function(matchers) { addCoreMatchers(AsyncExpectation.prototype, matchers, wrapAsyncCompare); } }; }; getJasmineRequireObj().ExpectationFilterChain = function() { function ExpectationFilterChain(maybeFilter, prev) { this.filter_ = maybeFilter; this.prev_ = prev; } ExpectationFilterChain.prototype.addFilter = function(filter) { return new ExpectationFilterChain(filter, this); }; ExpectationFilterChain.prototype.selectComparisonFunc = function(matcher) { return this.callFirst_('selectComparisonFunc', arguments).result; }; ExpectationFilterChain.prototype.buildFailureMessage = function( result, matcherName, args, matchersUtil ) { return this.callFirst_('buildFailureMessage', arguments).result; }; ExpectationFilterChain.prototype.modifyFailureMessage = function(msg) { const result = this.callFirst_('modifyFailureMessage', arguments).result; return result || msg; }; ExpectationFilterChain.prototype.callFirst_ = function(fname, args) { if (this.prev_) { const prevResult = this.prev_.callFirst_(fname, args); if (prevResult.found) { return prevResult; } } if (this.filter_ && this.filter_[fname]) { return { found: true, result: this.filter_[fname].apply(this.filter_, args) }; } return { found: false }; }; return ExpectationFilterChain; }; getJasmineRequireObj().Expector = function(j$) { function Expector(options) { this.matchersUtil = options.matchersUtil || { buildFailureMessage: function() {} }; this.actual = options.actual; this.addExpectationResult = options.addExpectationResult || function() {}; this.filters = new j$.ExpectationFilterChain(); } Expector.prototype.instantiateMatcher = function( matcherName, matcherFactory, args ) { this.matcherName = matcherName; this.args = Array.prototype.slice.call(args, 0); this.expected = this.args.slice(0); this.args.unshift(this.actual); const matcher = matcherFactory(this.matchersUtil); const comparisonFunc = this.filters.selectComparisonFunc(matcher); return comparisonFunc || matcher.compare; }; Expector.prototype.buildMessage = function(result) { if (result.pass) { return ''; } const defaultMessage = () => { if (!result.message) { const args = this.args.slice(); args.unshift(false); args.unshift(this.matcherName); return this.matchersUtil.buildFailureMessage.apply( this.matchersUtil, args ); } else if (j$.isFunction_(result.message)) { return result.message(); } else { return result.message; } }; const msg = this.filters.buildFailureMessage( result, this.matcherName, this.args, this.matchersUtil, defaultMessage ); return this.filters.modifyFailureMessage(msg || defaultMessage()); }; Expector.prototype.compare = function(matcherName, matcherFactory, args) { const matcherCompare = this.instantiateMatcher( matcherName, matcherFactory, args ); return matcherCompare.apply(null, this.args); }; Expector.prototype.addFilter = function(filter) { const result = Object.create(this); result.filters = this.filters.addFilter(filter); return result; }; Expector.prototype.processResult = function(result, errorForStack) { const message = this.buildMessage(result); if (this.expected.length === 1) { this.expected = this.expected[0]; } this.addExpectationResult(result.pass, { matcherName: this.matcherName, passed: result.pass, message: message, error: errorForStack ? undefined : result.error, errorForStack: errorForStack || undefined, actual: this.actual, expected: this.expected // TODO: this may need to be arrayified/sliced }); }; return Expector; }; getJasmineRequireObj().formatErrorMsg = function() { function generateErrorMsg(domain, usage) { const usageDefinition = usage ? '\nUsage: ' + usage : ''; return function errorMsg(msg) { return domain + ' : ' + msg + usageDefinition; }; } return generateErrorMsg; }; getJasmineRequireObj().GlobalErrors = function(j$) { function GlobalErrors(global) { global = global || j$.getGlobal(); const handlers = []; let overrideHandler = null, onRemoveOverrideHandler = null; function onerror(message, source, lineno, colno, error) { if (overrideHandler) { overrideHandler(error || message); return; } const handler = handlers[handlers.length - 1]; if (handler) { handler.apply(null, Array.prototype.slice.call(arguments, 0)); } else { throw arguments[0]; } } this.originalHandlers = {}; this.jasmineHandlers = {}; this.installOne_ = function installOne_(errorType, jasmineMessage) { function taggedOnError(error) { if (j$.isError_(error)) { error.jasmineMessage = jasmineMessage + ': ' + error; } else { let substituteMsg; if (error) { substituteMsg = jasmineMessage + ': ' + error; } else { substituteMsg = jasmineMessage + ' with no error or message'; } if (errorType === 'unhandledRejection') { substituteMsg += '\n' + '(Tip: to get a useful stack trace, use ' + 'Promise.reject(new Error(...)) instead of Promise.reject(' + (error ? '...' : '') + ').)'; } error = new Error(substituteMsg); } const handler = handlers[handlers.length - 1]; if (overrideHandler) { overrideHandler(error); return; } if (handler) { handler(error); } else { throw error; } } this.originalHandlers[errorType] = global.process.listeners(errorType); this.jasmineHandlers[errorType] = taggedOnError; global.process.removeAllListeners(errorType); global.process.on(errorType, taggedOnError); this.uninstall = function uninstall() { const errorTypes = Object.keys(this.originalHandlers); for (const errorType of errorTypes) { global.process.removeListener( errorType, this.jasmineHandlers[errorType] ); for (let i = 0; i < this.originalHandlers[errorType].length; i++) { global.process.on(errorType, this.originalHandlers[errorType][i]); } delete this.originalHandlers[errorType]; delete this.jasmineHandlers[errorType]; } }; }; this.install = function install() { if ( global.process && global.process.listeners && j$.isFunction_(global.process.on) ) { this.installOne_('uncaughtException', 'Uncaught exception'); this.installOne_('unhandledRejection', 'Unhandled promise rejection'); } else { const originalHandler = global.onerror; global.onerror = onerror; const browserRejectionHandler = function browserRejectionHandler( event ) { if (j$.isError_(event.reason)) { event.reason.jasmineMessage = 'Unhandled promise rejection: ' + event.reason; global.onerror(event.reason); } else { global.onerror('Unhandled promise rejection: ' + event.reason); } }; global.addEventListener('unhandledrejection', browserRejectionHandler); this.uninstall = function uninstall() { global.onerror = originalHandler; global.removeEventListener( 'unhandledrejection', browserRejectionHandler ); }; } }; this.pushListener = function pushListener(listener) { handlers.push(listener); }; this.popListener = function popListener(listener) { if (!listener) { throw new Error('popListener expects a listener'); } handlers.pop(); }; this.setOverrideListener = function(listener, onRemove) { if (overrideHandler) { throw new Error("Can't set more than one override listener at a time"); } overrideHandler = listener; onRemoveOverrideHandler = onRemove; }; this.removeOverrideListener = function() { if (onRemoveOverrideHandler) { onRemoveOverrideHandler(); } overrideHandler = null; onRemoveOverrideHandler = null; }; } return GlobalErrors; }; getJasmineRequireObj().toBePending = function(j$) { /** * Expect a promise to be pending, i.e. the promise is neither resolved nor rejected. * @function * @async * @name async-matchers#toBePending * @since 3.6 * @example * await expectAsync(aPromise).toBePending(); */ return function toBePending() { return { compare: function(actual) { if (!j$.isPromiseLike(actual)) { throw new Error('Expected toBePending to be called on a promise.'); } const want = {}; return Promise.race([actual, Promise.resolve(want)]).then( function(got) { return { pass: want === got }; }, function() { return { pass: false }; } ); } }; }; }; getJasmineRequireObj().toBeRejected = function(j$) { /** * Expect a promise to be rejected. * @function * @async * @name async-matchers#toBeRejected * @since 3.1.0 * @example * await expectAsync(aPromise).toBeRejected(); * @example * return expectAsync(aPromise).toBeRejected(); */ return function toBeRejected() { return { compare: function(actual) { if (!j$.isPromiseLike(actual)) { throw new Error('Expected toBeRejected to be called on a promise.'); } return actual.then( function() { return { pass: false }; }, function() { return { pass: true }; } ); } }; }; }; getJasmineRequireObj().toBeRejectedWith = function(j$) { /** * Expect a promise to be rejected with a value equal to the expected, using deep equality comparison. * @function * @async * @name async-matchers#toBeRejectedWith * @since 3.3.0 * @param {Object} expected - Value that the promise is expected to be rejected with * @example * await expectAsync(aPromise).toBeRejectedWith({prop: 'value'}); * @example * return expectAsync(aPromise).toBeRejectedWith({prop: 'value'}); */ return function toBeRejectedWith(matchersUtil) { return { compare: function(actualPromise, expectedValue) { if (!j$.isPromiseLike(actualPromise)) { throw new Error( 'Expected toBeRejectedWith to be called on a promise.' ); } function prefix(passed) { return ( 'Expected a promise ' + (passed ? 'not ' : '') + 'to be rejected with ' + matchersUtil.pp(expectedValue) ); } return actualPromise.then( function() { return { pass: false, message: prefix(false) + ' but it was resolved.' }; }, function(actualValue) { if (matchersUtil.equals(actualValue, expectedValue)) { return { pass: true, message: prefix(true) + '.' }; } else { return { pass: false, message: prefix(false) + ' but it was rejected with ' + matchersUtil.pp(actualValue) + '.' }; } } ); } }; }; }; getJasmineRequireObj().toBeRejectedWithError = function(j$) { /** * Expect a promise to be rejected with a value matched to the expected * @function * @async * @name async-matchers#toBeRejectedWithError * @since 3.5.0 * @param {Error} [expected] - `Error` constructor the object that was thrown needs to be an instance of. If not provided, `Error` will be used. * @param {RegExp|String} [message] - The message that should be set on the thrown `Error` * @example * await expectAsync(aPromise).toBeRejectedWithError(MyCustomError, 'Error message'); * await expectAsync(aPromise).toBeRejectedWithError(MyCustomError, /Error message/); * await expectAsync(aPromise).toBeRejectedWithError(MyCustomError); * await expectAsync(aPromise).toBeRejectedWithError('Error message'); * return expectAsync(aPromise).toBeRejectedWithError(/Error message/); */ return function toBeRejectedWithError(matchersUtil) { return { compare: function(actualPromise, arg1, arg2) { if (!j$.isPromiseLike(actualPromise)) { throw new Error( 'Expected toBeRejectedWithError to be called on a promise.' ); } const expected = getExpectedFromArgs(arg1, arg2, matchersUtil); return actualPromise.then( function() { return { pass: false, message: 'Expected a promise to be rejected but it was resolved.' }; }, function(actualValue) { return matchError(actualValue, expected, matchersUtil); } ); } }; }; function matchError(actual, expected, matchersUtil) { if (!j$.isError_(actual)) { return fail(expected, 'rejected with ' + matchersUtil.pp(actual)); } if (!(actual instanceof expected.error)) { return fail( expected, 'rejected with type ' + j$.fnNameFor(actual.constructor) ); } const actualMessage = actual.message; if ( actualMessage === expected.message || typeof expected.message === 'undefined' ) { return pass(expected); } if ( expected.message instanceof RegExp && expected.message.test(actualMessage) ) { return pass(expected); } return fail(expected, 'rejected with ' + matchersUtil.pp(actual)); } function pass(expected) { return { pass: true, message: 'Expected a promise not to be rejected with ' + expected.printValue + ', but it was.' }; } function fail(expected, message) { return { pass: false, message: 'Expected a promise to be rejected with ' + expected.printValue + ' but it was ' + message + '.' }; } function getExpectedFromArgs(arg1, arg2, matchersUtil) { let error, message; if (isErrorConstructor(arg1)) { error = arg1; message = arg2; } else { error = Error; message = arg1; } return { error: error, message: message, printValue: j$.fnNameFor(error) + (typeof message === 'undefined' ? '' : ': ' + matchersUtil.pp(message)) }; } function isErrorConstructor(value) { return ( typeof value === 'function' && (value === Error || j$.isError_(value.prototype)) ); } }; getJasmineRequireObj().toBeResolved = function(j$) { /** * Expect a promise to be resolved. * @function * @async * @name async-matchers#toBeResolved * @since 3.1.0 * @example * await expectAsync(aPromise).toBeResolved(); * @example * return expectAsync(aPromise).toBeResolved(); */ return function toBeResolved(matchersUtil) { return { compare: function(actual) { if (!j$.isPromiseLike(actual)) { throw new Error('Expected toBeResolved to be called on a promise.'); } return actual.then( function() { return { pass: true }; }, function(e) { return { pass: false, message: 'Expected a promise to be resolved but it was ' + 'rejected with ' + matchersUtil.pp(e) + '.' }; } ); } }; }; }; getJasmineRequireObj().toBeResolvedTo = function(j$) { /** * Expect a promise to be resolved to a value equal to the expected, using deep equality comparison. * @function * @async * @name async-matchers#toBeResolvedTo * @since 3.1.0 * @param {Object} expected - Value that the promise is expected to resolve to * @example * await expectAsync(aPromise).toBeResolvedTo({prop: 'value'}); * @example * return expectAsync(aPromise).toBeResolvedTo({prop: 'value'}); */ return function toBeResolvedTo(matchersUtil) { return { compare: function(actualPromise, expectedValue) { if (!j$.isPromiseLike(actualPromise)) { throw new Error('Expected toBeResolvedTo to be called on a promise.'); } function prefix(passed) { return ( 'Expected a promise ' + (passed ? 'not ' : '') + 'to be resolved to ' + matchersUtil.pp(expectedValue) ); } return actualPromise.then( function(actualValue) { if (matchersUtil.equals(actualValue, expectedValue)) { return { pass: true, message: prefix(true) + '.' }; } else { return { pass: false, message: prefix(false) + ' but it was resolved to ' + matchersUtil.pp(actualValue) + '.' }; } }, function(e) { return { pass: false, message: prefix(false) + ' but it was rejected with ' + matchersUtil.pp(e) + '.' }; } ); } }; }; }; getJasmineRequireObj().DiffBuilder = function(j$) { class DiffBuilder { constructor(config) { this.prettyPrinter_ = (config || {}).prettyPrinter || j$.makePrettyPrinter(); this.mismatches_ = new j$.MismatchTree(); this.path_ = new j$.ObjectPath(); this.actualRoot_ = undefined; this.expectedRoot_ = undefined; } setRoots(actual, expected) { this.actualRoot_ = actual; this.expectedRoot_ = expected; } recordMismatch(formatter) { this.mismatches_.add(this.path_, formatter); } getMessage() { const messages = []; this.mismatches_.traverse((path, isLeaf, formatter) => { const { actual, expected } = this.dereferencePath_(path); if (formatter) { messages.push(formatter(actual, expected, path, this.prettyPrinter_)); return true; } const actualCustom = this.prettyPrinter_.customFormat_(actual); const expectedCustom = this.prettyPrinter_.customFormat_(expected); const useCustom = !( j$.util.isUndefined(actualCustom) && j$.util.isUndefined(expectedCustom) ); if (useCustom) { messages.push(wrapPrettyPrinted(actualCustom, expectedCustom, path)); return false; // don't recurse further } if (isLeaf) { messages.push(this.defaultFormatter_(actual, expected, path)); } return true; }); return messages.join('\n'); } withPath(pathComponent, block) { const oldPath = this.path_; this.path_ = this.path_.add(pathComponent); block(); this.path_ = oldPath; } dereferencePath_(objectPath) { let actual = this.actualRoot_; let expected = this.expectedRoot_; const handleAsymmetricExpected = () => { if ( j$.isAsymmetricEqualityTester_(expected) && j$.isFunction_(expected.valuesForDiff_) ) { const asymmetricResult = expected.valuesForDiff_( actual, this.prettyPrinter_ ); expected = asymmetricResult.self; actual = asymmetricResult.other; } }; handleAsymmetricExpected(); for (const pc of objectPath.components) { actual = actual[pc]; expected = expected[pc]; handleAsymmetricExpected(); } return { actual: actual, expected: expected }; } defaultFormatter_(actual, expected, path) { return wrapPrettyPrinted( this.prettyPrinter_(actual), this.prettyPrinter_(expected), path ); } } function wrapPrettyPrinted(actual, expected, path) { return ( 'Expected ' + path + (path.depth() ? ' = ' : '') + actual + ' to equal ' + expected + '.' ); } return DiffBuilder; }; getJasmineRequireObj().MatchersUtil = function(j$) { /** * @class MatchersUtil * @classdesc Utilities for use in implementing matchers.
* _Note:_ Do not construct this directly. Jasmine will construct one and * pass it to matchers and asymmetric equality testers. * @hideconstructor */ function MatchersUtil(options) { options = options || {}; this.customTesters_ = options.customTesters || []; /** * Formats a value for use in matcher failure messages and similar contexts, * taking into account the current set of custom value formatters. * @function * @name MatchersUtil#pp * @since 3.6.0 * @param {*} value The value to pretty-print * @return {string} The pretty-printed value */ this.pp = options.pp || function() {}; } /** * Determines whether `haystack` contains `needle`, using the same comparison * logic as {@link MatchersUtil#equals}. * @function * @name MatchersUtil#contains * @since 2.0.0 * @param {*} haystack The collection to search * @param {*} needle The value to search for * @returns {boolean} True if `needle` was found in `haystack` */ MatchersUtil.prototype.contains = function(haystack, needle) { if (!haystack) { return false; } if (j$.isSet(haystack)) { // Try .has() first. It should be faster in cases where // needle === something in haystack. Fall back to .equals() comparison // if that fails. if (haystack.has(needle)) { return true; } } if (j$.isIterable_(haystack) && !j$.isString_(haystack)) { // Arrays, Sets, etc. for (const candidate of haystack) { if (this.equals(candidate, needle)) { return true; } } return false; } if (haystack.indexOf) { // Mainly strings return haystack.indexOf(needle) >= 0; } if (j$.isNumber_(haystack.length)) { // Objects that are shaped like arrays but aren't iterable for (let i = 0; i < haystack.length; i++) { if (this.equals(haystack[i], needle)) { return true; } } } return false; }; MatchersUtil.prototype.buildFailureMessage = function() { const args = Array.prototype.slice.call(arguments, 0), matcherName = args[0], isNot = args[1], actual = args[2], expected = args.slice(3), englishyPredicate = matcherName.replace(/[A-Z]/g, function(s) { return ' ' + s.toLowerCase(); }); let message = 'Expected ' + this.pp(actual) + (isNot ? ' not ' : ' ') + englishyPredicate; if (expected.length > 0) { for (let i = 0; i < expected.length; i++) { if (i > 0) { message += ','; } message += ' ' + this.pp(expected[i]); } } return message + '.'; }; MatchersUtil.prototype.asymmetricDiff_ = function( a, b, aStack, bStack, diffBuilder ) { if (j$.isFunction_(b.valuesForDiff_)) { const values = b.valuesForDiff_(a, this.pp); this.eq_(values.other, values.self, aStack, bStack, diffBuilder); } else { diffBuilder.recordMismatch(); } }; MatchersUtil.prototype.asymmetricMatch_ = function( a, b, aStack, bStack, diffBuilder ) { const asymmetricA = j$.isAsymmetricEqualityTester_(a); const asymmetricB = j$.isAsymmetricEqualityTester_(b); if (asymmetricA === asymmetricB) { return undefined; } let result; if (asymmetricA) { result = a.asymmetricMatch(b, this); if (!result) { diffBuilder.recordMismatch(); } return result; } if (asymmetricB) { result = b.asymmetricMatch(a, this); if (!result) { this.asymmetricDiff_(a, b, aStack, bStack, diffBuilder); } return result; } }; /** * Determines whether two values are deeply equal to each other. * @function * @name MatchersUtil#equals * @since 2.0.0 * @param {*} a The first value to compare * @param {*} b The second value to compare * @returns {boolean} True if the values are equal */ MatchersUtil.prototype.equals = function(a, b, diffBuilder) { diffBuilder = diffBuilder || j$.NullDiffBuilder(); diffBuilder.setRoots(a, b); return this.eq_(a, b, [], [], diffBuilder); }; // Equality function lovingly adapted from isEqual in // [Underscore](http://underscorejs.org) MatchersUtil.prototype.eq_ = function(a, b, aStack, bStack, diffBuilder) { let result = true; const asymmetricResult = this.asymmetricMatch_( a, b, aStack, bStack, diffBuilder ); if (!j$.util.isUndefined(asymmetricResult)) { return asymmetricResult; } for (const tester of this.customTesters_) { const customTesterResult = tester(a, b); if (!j$.util.isUndefined(customTesterResult)) { if (!customTesterResult) { diffBuilder.recordMismatch(); } return customTesterResult; } } if (a instanceof Error && b instanceof Error) { result = a.message == b.message; if (!result) { diffBuilder.recordMismatch(); } return result; } // Identical objects are equal. `0 === -0`, but they aren't identical. // See the [Harmony `egal` proposal](http://wiki.ecmascript.org/doku.php?id=harmony:egal). if (a === b) { result = a !== 0 || 1 / a == 1 / b; if (!result) { diffBuilder.recordMismatch(); } return result; } // A strict comparison is necessary because `null == undefined`. if (a === null || b === null) { result = a === b; if (!result) { diffBuilder.recordMismatch(); } return result; } const className = Object.prototype.toString.call(a); if (className != Object.prototype.toString.call(b)) { diffBuilder.recordMismatch(); return false; } switch (className) { // Strings, numbers, dates, and booleans are compared by value. case '[object String]': // Primitives and their corresponding object wrappers are equivalent; thus, `"5"` is // equivalent to `new String("5")`. result = a == String(b); if (!result) { diffBuilder.recordMismatch(); } return result; case '[object Number]': // `NaN`s are equivalent, but non-reflexive. An `egal` comparison is performed for // other numeric values. result = a != +a ? b != +b : a === 0 && b === 0 ? 1 / a == 1 / b : a == +b; if (!result) { diffBuilder.recordMismatch(); } return result; case '[object Date]': case '[object Boolean]': // Coerce dates and booleans to numeric primitive values. Dates are compared by their // millisecond representations. Note that invalid dates with millisecond representations // of `NaN` are not equivalent. result = +a == +b; if (!result) { diffBuilder.recordMismatch(); } return result; case '[object ArrayBuffer]': // If we have an instance of ArrayBuffer the Uint8Array ctor // will be defined as well return this.eq_( new Uint8Array(a), new Uint8Array(b), aStack, bStack, diffBuilder ); // RegExps are compared by their source patterns and flags. case '[object RegExp]': return ( a.source == b.source && a.global == b.global && a.multiline == b.multiline && a.ignoreCase == b.ignoreCase ); } if (typeof a != 'object' || typeof b != 'object') { diffBuilder.recordMismatch(); return false; } const aIsDomNode = j$.isDomNode(a); const bIsDomNode = j$.isDomNode(b); if (aIsDomNode && bIsDomNode) { // At first try to use DOM3 method isEqualNode result = a.isEqualNode(b); if (!result) { diffBuilder.recordMismatch(); } return result; } if (aIsDomNode || bIsDomNode) { diffBuilder.recordMismatch(); return false; } const aIsPromise = j$.isPromise(a); const bIsPromise = j$.isPromise(b); if (aIsPromise && bIsPromise) { return a === b; } // Assume equality for cyclic structures. The algorithm for detecting cyclic // structures is adapted from ES 5.1 section 15.12.3, abstract operation `JO`. let length = aStack.length; while (length--) { // Linear search. Performance is inversely proportional to the number of // unique nested structures. if (aStack[length] == a) { return bStack[length] == b; } } // Add the first object to the stack of traversed objects. aStack.push(a); bStack.push(b); let size = 0; // Recursively compare objects and arrays. // Compare array lengths to determine if a deep comparison is necessary. if (className == '[object Array]') { const aLength = a.length; const bLength = b.length; diffBuilder.withPath('length', function() { if (aLength !== bLength) { diffBuilder.recordMismatch(); result = false; } }); for (let i = 0; i < aLength || i < bLength; i++) { diffBuilder.withPath(i, () => { if (i >= bLength) { diffBuilder.recordMismatch( actualArrayIsLongerFormatter.bind(null, this.pp) ); result = false; } else { result = this.eq_( i < aLength ? a[i] : void 0, i < bLength ? b[i] : void 0, aStack, bStack, diffBuilder ) && result; } }); } if (!result) { return false; } } else if (j$.isMap(a) && j$.isMap(b)) { if (a.size != b.size) { diffBuilder.recordMismatch(); return false; } const keysA = []; const keysB = []; a.forEach(function(valueA, keyA) { keysA.push(keyA); }); b.forEach(function(valueB, keyB) { keysB.push(keyB); }); // For both sets of keys, check they map to equal values in both maps. // Keep track of corresponding keys (in insertion order) in order to handle asymmetric obj keys. const mapKeys = [keysA, keysB]; const cmpKeys = [keysB, keysA]; for (let i = 0; result && i < mapKeys.length; i++) { const mapIter = mapKeys[i]; const cmpIter = cmpKeys[i]; for (let j = 0; result && j < mapIter.length; j++) { const mapKey = mapIter[j]; const cmpKey = cmpIter[j]; const mapValueA = a.get(mapKey); let mapValueB; // Only use the cmpKey when one of the keys is asymmetric and the corresponding key matches, // otherwise explicitly look up the mapKey in the other Map since we want keys with unique // obj identity (that are otherwise equal) to not match. if ( j$.isAsymmetricEqualityTester_(mapKey) || (j$.isAsymmetricEqualityTester_(cmpKey) && this.eq_(mapKey, cmpKey, aStack, bStack, j$.NullDiffBuilder())) ) { mapValueB = b.get(cmpKey); } else { mapValueB = b.get(mapKey); } result = this.eq_( mapValueA, mapValueB, aStack, bStack, j$.NullDiffBuilder() ); } } if (!result) { diffBuilder.recordMismatch(); return false; } } else if (j$.isSet(a) && j$.isSet(b)) { if (a.size != b.size) { diffBuilder.recordMismatch(); return false; } const valuesA = []; a.forEach(function(valueA) { valuesA.push(valueA); }); const valuesB = []; b.forEach(function(valueB) { valuesB.push(valueB); }); // For both sets, check they are all contained in the other set const setPairs = [[valuesA, valuesB], [valuesB, valuesA]]; const stackPairs = [[aStack, bStack], [bStack, aStack]]; for (let i = 0; result && i < setPairs.length; i++) { const baseValues = setPairs[i][0]; const otherValues = setPairs[i][1]; const baseStack = stackPairs[i][0]; const otherStack = stackPairs[i][1]; // For each value in the base set... for (const baseValue of baseValues) { let found = false; // ... test that it is present in the other set for (let j = 0; !found && j < otherValues.length; j++) { const otherValue = otherValues[j]; const prevStackSize = baseStack.length; // compare by value equality found = this.eq_( baseValue, otherValue, baseStack, otherStack, j$.NullDiffBuilder() ); if (!found && prevStackSize !== baseStack.length) { baseStack.splice(prevStackSize); otherStack.splice(prevStackSize); } } result = result && found; } } if (!result) { diffBuilder.recordMismatch(); return false; } } else if (j$.isURL(a) && j$.isURL(b)) { // URLs have no enumrable properties, so the default object comparison // would consider any two URLs to be equal. return a.toString() === b.toString(); } else { // Objects with different constructors are not equivalent, but `Object`s // or `Array`s from different frames are. const aCtor = a.constructor, bCtor = b.constructor; if ( aCtor !== bCtor && isFunction(aCtor) && isFunction(bCtor) && a instanceof aCtor && b instanceof bCtor && !(aCtor instanceof aCtor && bCtor instanceof bCtor) ) { diffBuilder.recordMismatch( constructorsAreDifferentFormatter.bind(null, this.pp) ); return false; } } // Deep compare objects. const aKeys = MatchersUtil.keys(a, className == '[object Array]'); size = aKeys.length; // Ensure that both objects contain the same number of properties before comparing deep equality. if (MatchersUtil.keys(b, className == '[object Array]').length !== size) { diffBuilder.recordMismatch( objectKeysAreDifferentFormatter.bind(null, this.pp) ); return false; } for (const key of aKeys) { // Deep compare each member if (!j$.util.has(b, key)) { diffBuilder.recordMismatch( objectKeysAreDifferentFormatter.bind(null, this.pp) ); result = false; continue; } diffBuilder.withPath(key, () => { if (!this.eq_(a[key], b[key], aStack, bStack, diffBuilder)) { result = false; } }); } if (!result) { return false; } // Remove the first object from the stack of traversed objects. aStack.pop(); bStack.pop(); return result; }; MatchersUtil.keys = function(obj, isArray) { const allKeys = (function(o) { const keys = []; for (const key in o) { if (j$.util.has(o, key)) { keys.push(key); } } const symbols = Object.getOwnPropertySymbols(o); for (const sym of symbols) { if (o.propertyIsEnumerable(sym)) { keys.push(sym); } } return keys; })(obj); if (!isArray) { return allKeys; } if (allKeys.length === 0) { return allKeys; } const extraKeys = []; for (const k of allKeys) { if (typeof k === 'symbol' || !/^[0-9]+$/.test(k)) { extraKeys.push(k); } } return extraKeys; }; function isFunction(obj) { return typeof obj === 'function'; } // Returns an array of [k, v] pairs for eacch property that's in objA // and not in objB. function extraKeysAndValues(objA, objB) { return MatchersUtil.keys(objA) .filter(key => !j$.util.has(objB, key)) .map(key => [key, objA[key]]); } function objectKeysAreDifferentFormatter(pp, actual, expected, path) { const missingProperties = extraKeysAndValues(expected, actual), extraProperties = extraKeysAndValues(actual, expected), missingPropertiesMessage = formatKeyValuePairs(pp, missingProperties), extraPropertiesMessage = formatKeyValuePairs(pp, extraProperties), messages = []; if (!path.depth()) { path = 'object'; } if (missingPropertiesMessage.length) { messages.push( 'Expected ' + path + ' to have properties' + missingPropertiesMessage ); } if (extraPropertiesMessage.length) { messages.push( 'Expected ' + path + ' not to have properties' + extraPropertiesMessage ); } return messages.join('\n'); } function constructorsAreDifferentFormatter(pp, actual, expected, path) { if (!path.depth()) { path = 'object'; } return ( 'Expected ' + path + ' to be a kind of ' + j$.fnNameFor(expected.constructor) + ', but was ' + pp(actual) + '.' ); } function actualArrayIsLongerFormatter(pp, actual, expected, path) { return ( 'Unexpected ' + path + (path.depth() ? ' = ' : '') + pp(actual) + ' in array.' ); } function formatKeyValuePairs(pp, keyValuePairs) { let formatted = ''; for (const [key, value] of keyValuePairs) { formatted += '\n ' + key.toString() + ': ' + pp(value); } return formatted; } return MatchersUtil; }; /** * @interface AsymmetricEqualityTester * @classdesc An asymmetric equality tester is an object that can match multiple * objects. Examples include jasmine.any() and jasmine.stringMatching(). Jasmine * includes a number of built-in asymmetric equality testers, such as * {@link jasmine.objectContaining}. User-defined asymmetric equality testers are * also supported. * * Asymmetric equality testers work with any matcher, including user-defined * custom matchers, that uses {@link MatchersUtil#equals} or * {@link MatchersUtil#contains}. * * @example * function numberDivisibleBy(divisor) { * return { * asymmetricMatch: function(n) { * return typeof n === 'number' && n % divisor === 0; * }, * jasmineToString: function() { * return ``; * } * }; * } * * const actual = { * n: 2, * otherFields: "don't care" * }; * * expect(actual).toEqual(jasmine.objectContaining({n: numberDivisibleBy(2)})); * @see custom_asymmetric_equality_testers * @since 2.0.0 */ /** * Determines whether a value matches this tester * @function * @name AsymmetricEqualityTester#asymmetricMatch * @param value {any} The value to test * @param matchersUtil {MatchersUtil} utilities for testing equality, etc * @return {Boolean} */ /** * Returns a string representation of this tester to use in matcher failure messages * @function * @name AsymmetricEqualityTester#jasmineToString * @param pp {function} Function that takes a value and returns a pretty-printed representation * @return {String} */ getJasmineRequireObj().MismatchTree = function(j$) { /* To be able to apply custom object formatters at all possible levels of an object graph, DiffBuilder needs to be able to know not just where the mismatch occurred but also all ancestors of the mismatched value in both the expected and actual object graphs. MismatchTree maintains that context and provides it via the traverse method. */ class MismatchTree { constructor(path) { this.path = path || new j$.ObjectPath([]); this.formatter = undefined; this.children = []; this.isMismatch = false; } add(path, formatter) { if (path.depth() === 0) { this.formatter = formatter; this.isMismatch = true; } else { const key = path.components[0]; path = path.shift(); let child = this.child(key); if (!child) { child = new MismatchTree(this.path.add(key)); this.children.push(child); } child.add(path, formatter); } } traverse(visit) { const hasChildren = this.children.length > 0; if (this.isMismatch || hasChildren) { if (visit(this.path, !hasChildren, this.formatter)) { for (const child of this.children) { child.traverse(visit); } } } } child(key) { return this.children.find(child => { const pathEls = child.path.components; return pathEls[pathEls.length - 1] === key; }); } } return MismatchTree; }; getJasmineRequireObj().nothing = function() { /** * {@link expect} nothing explicitly. * @function * @name matchers#nothing * @since 2.8.0 * @example * expect().nothing(); */ function nothing() { return { compare: function() { return { pass: true }; } }; } return nothing; }; getJasmineRequireObj().NullDiffBuilder = function(j$) { return function() { return { withPath: function(_, block) { block(); }, setRoots: function() {}, recordMismatch: function() {} }; }; }; getJasmineRequireObj().ObjectPath = function(j$) { class ObjectPath { constructor(components) { this.components = components || []; } toString() { if (this.components.length) { return '$' + this.components.map(formatPropertyAccess).join(''); } else { return ''; } } add(component) { return new ObjectPath(this.components.concat([component])); } shift() { return new ObjectPath(this.components.slice(1)); } depth() { return this.components.length; } } function formatPropertyAccess(prop) { if (typeof prop === 'number' || typeof prop === 'symbol') { return '[' + prop.toString() + ']'; } if (isValidIdentifier(prop)) { return '.' + prop; } return `['${prop}']`; } function isValidIdentifier(string) { return /^[A-Za-z\$_][A-Za-z0-9\$_]*$/.test(string); } return ObjectPath; }; getJasmineRequireObj().requireAsyncMatchers = function(jRequire, j$) { const availableMatchers = [ 'toBePending', 'toBeResolved', 'toBeRejected', 'toBeResolvedTo', 'toBeRejectedWith', 'toBeRejectedWithError' ], matchers = {}; for (const name of availableMatchers) { matchers[name] = jRequire[name](j$); } return matchers; }; getJasmineRequireObj().toBe = function(j$) { /** * {@link expect} the actual value to be `===` to the expected value. * @function * @name matchers#toBe * @since 1.3.0 * @param {Object} expected - The expected value to compare against. * @example * expect(thing).toBe(realThing); */ function toBe(matchersUtil) { const tip = ' Tip: To check for deep equality, use .toEqual() instead of .toBe().'; return { compare: function(actual, expected) { const result = { pass: actual === expected }; if (typeof expected === 'object') { result.message = matchersUtil.buildFailureMessage( 'toBe', result.pass, actual, expected ) + tip; } return result; } }; } return toBe; }; getJasmineRequireObj().toBeCloseTo = function() { /** * {@link expect} the actual value to be within a specified precision of the expected value. * @function * @name matchers#toBeCloseTo * @since 1.3.0 * @param {Object} expected - The expected value to compare against. * @param {Number} [precision=2] - The number of decimal points to check. * @example * expect(number).toBeCloseTo(42.2, 3); */ function toBeCloseTo() { return { compare: function(actual, expected, precision) { if (precision !== 0) { precision = precision || 2; } if (expected === null || actual === null) { throw new Error( 'Cannot use toBeCloseTo with null. Arguments evaluated to: ' + 'expect(' + actual + ').toBeCloseTo(' + expected + ').' ); } // Infinity is close to Infinity and -Infinity is close to -Infinity, // regardless of the precision. if (expected === Infinity || expected === -Infinity) { return { pass: actual === expected }; } const pow = Math.pow(10, precision + 1); const delta = Math.abs(expected - actual); const maxDelta = Math.pow(10, -precision) / 2; return { pass: Math.round(delta * pow) <= maxDelta * pow }; } }; } return toBeCloseTo; }; getJasmineRequireObj().toBeDefined = function() { /** * {@link expect} the actual value to be defined. (Not `undefined`) * @function * @name matchers#toBeDefined * @since 1.3.0 * @example * expect(result).toBeDefined(); */ function toBeDefined() { return { compare: function(actual) { return { pass: void 0 !== actual }; } }; } return toBeDefined; }; getJasmineRequireObj().toBeFalse = function() { /** * {@link expect} the actual value to be `false`. * @function * @name matchers#toBeFalse * @since 3.5.0 * @example * expect(result).toBeFalse(); */ function toBeFalse() { return { compare: function(actual) { return { pass: actual === false }; } }; } return toBeFalse; }; getJasmineRequireObj().toBeFalsy = function() { /** * {@link expect} the actual value to be falsy * @function * @name matchers#toBeFalsy * @since 2.0.0 * @example * expect(result).toBeFalsy(); */ function toBeFalsy() { return { compare: function(actual) { return { pass: !actual }; } }; } return toBeFalsy; }; getJasmineRequireObj().toBeGreaterThan = function() { /** * {@link expect} the actual value to be greater than the expected value. * @function * @name matchers#toBeGreaterThan * @since 2.0.0 * @param {Number} expected - The value to compare against. * @example * expect(result).toBeGreaterThan(3); */ function toBeGreaterThan() { return { compare: function(actual, expected) { return { pass: actual > expected }; } }; } return toBeGreaterThan; }; getJasmineRequireObj().toBeGreaterThanOrEqual = function() { /** * {@link expect} the actual value to be greater than or equal to the expected value. * @function * @name matchers#toBeGreaterThanOrEqual * @since 2.0.0 * @param {Number} expected - The expected value to compare against. * @example * expect(result).toBeGreaterThanOrEqual(25); */ function toBeGreaterThanOrEqual() { return { compare: function(actual, expected) { return { pass: actual >= expected }; } }; } return toBeGreaterThanOrEqual; }; getJasmineRequireObj().toBeInstanceOf = function(j$) { const usageError = j$.formatErrorMsg( '', 'expect(value).toBeInstanceOf()' ); /** * {@link expect} the actual to be an instance of the expected class * @function * @name matchers#toBeInstanceOf * @since 3.5.0 * @param {Object} expected - The class or constructor function to check for * @example * expect('foo').toBeInstanceOf(String); * expect(3).toBeInstanceOf(Number); * expect(new Error()).toBeInstanceOf(Error); */ function toBeInstanceOf(matchersUtil) { return { compare: function(actual, expected) { const actualType = actual && actual.constructor ? j$.fnNameFor(actual.constructor) : matchersUtil.pp(actual); const expectedType = expected ? j$.fnNameFor(expected) : matchersUtil.pp(expected); let expectedMatcher; let pass; try { expectedMatcher = new j$.Any(expected); pass = expectedMatcher.asymmetricMatch(actual); } catch (error) { throw new Error( usageError('Expected value is not a constructor function') ); } if (pass) { return { pass: true, message: 'Expected instance of ' + actualType + ' not to be an instance of ' + expectedType }; } else { return { pass: false, message: 'Expected instance of ' + actualType + ' to be an instance of ' + expectedType }; } } }; } return toBeInstanceOf; }; getJasmineRequireObj().toBeLessThan = function() { /** * {@link expect} the actual value to be less than the expected value. * @function * @name matchers#toBeLessThan * @since 2.0.0 * @param {Number} expected - The expected value to compare against. * @example * expect(result).toBeLessThan(0); */ function toBeLessThan() { return { compare: function(actual, expected) { return { pass: actual < expected }; } }; } return toBeLessThan; }; getJasmineRequireObj().toBeLessThanOrEqual = function() { /** * {@link expect} the actual value to be less than or equal to the expected value. * @function * @name matchers#toBeLessThanOrEqual * @since 2.0.0 * @param {Number} expected - The expected value to compare against. * @example * expect(result).toBeLessThanOrEqual(123); */ function toBeLessThanOrEqual() { return { compare: function(actual, expected) { return { pass: actual <= expected }; } }; } return toBeLessThanOrEqual; }; getJasmineRequireObj().toBeNaN = function(j$) { /** * {@link expect} the actual value to be `NaN` (Not a Number). * @function * @name matchers#toBeNaN * @since 1.3.0 * @example * expect(thing).toBeNaN(); */ function toBeNaN(matchersUtil) { return { compare: function(actual) { const result = { pass: actual !== actual }; if (result.pass) { result.message = 'Expected actual not to be NaN.'; } else { result.message = function() { return 'Expected ' + matchersUtil.pp(actual) + ' to be NaN.'; }; } return result; } }; } return toBeNaN; }; getJasmineRequireObj().toBeNegativeInfinity = function(j$) { /** * {@link expect} the actual value to be `-Infinity` (-infinity). * @function * @name matchers#toBeNegativeInfinity * @since 2.6.0 * @example * expect(thing).toBeNegativeInfinity(); */ function toBeNegativeInfinity(matchersUtil) { return { compare: function(actual) { const result = { pass: actual === Number.NEGATIVE_INFINITY }; if (result.pass) { result.message = 'Expected actual not to be -Infinity.'; } else { result.message = function() { return 'Expected ' + matchersUtil.pp(actual) + ' to be -Infinity.'; }; } return result; } }; } return toBeNegativeInfinity; }; getJasmineRequireObj().toBeNull = function() { /** * {@link expect} the actual value to be `null`. * @function * @name matchers#toBeNull * @since 1.3.0 * @example * expect(result).toBeNull(); */ function toBeNull() { return { compare: function(actual) { return { pass: actual === null }; } }; } return toBeNull; }; getJasmineRequireObj().toBePositiveInfinity = function(j$) { /** * {@link expect} the actual value to be `Infinity` (infinity). * @function * @name matchers#toBePositiveInfinity * @since 2.6.0 * @example * expect(thing).toBePositiveInfinity(); */ function toBePositiveInfinity(matchersUtil) { return { compare: function(actual) { const result = { pass: actual === Number.POSITIVE_INFINITY }; if (result.pass) { result.message = 'Expected actual not to be Infinity.'; } else { result.message = function() { return 'Expected ' + matchersUtil.pp(actual) + ' to be Infinity.'; }; } return result; } }; } return toBePositiveInfinity; }; getJasmineRequireObj().toBeTrue = function() { /** * {@link expect} the actual value to be `true`. * @function * @name matchers#toBeTrue * @since 3.5.0 * @example * expect(result).toBeTrue(); */ function toBeTrue() { return { compare: function(actual) { return { pass: actual === true }; } }; } return toBeTrue; }; getJasmineRequireObj().toBeTruthy = function() { /** * {@link expect} the actual value to be truthy. * @function * @name matchers#toBeTruthy * @since 2.0.0 * @example * expect(thing).toBeTruthy(); */ function toBeTruthy() { return { compare: function(actual) { return { pass: !!actual }; } }; } return toBeTruthy; }; getJasmineRequireObj().toBeUndefined = function() { /** * {@link expect} the actual value to be `undefined`. * @function * @name matchers#toBeUndefined * @since 1.3.0 * @example * expect(result).toBeUndefined(): */ function toBeUndefined() { return { compare: function(actual) { return { pass: void 0 === actual }; } }; } return toBeUndefined; }; getJasmineRequireObj().toContain = function() { /** * {@link expect} the actual value to contain a specific value. * @function * @name matchers#toContain * @since 2.0.0 * @param {Object} expected - The value to look for. * @example * expect(array).toContain(anElement); * expect(string).toContain(substring); */ function toContain(matchersUtil) { return { compare: function(actual, expected) { return { pass: matchersUtil.contains(actual, expected) }; } }; } return toContain; }; getJasmineRequireObj().toEqual = function(j$) { /** * {@link expect} the actual value to be equal to the expected, using deep equality comparison. * @function * @name matchers#toEqual * @since 1.3.0 * @param {Object} expected - Expected value * @example * expect(bigObject).toEqual({"foo": ['bar', 'baz']}); */ function toEqual(matchersUtil) { return { compare: function(actual, expected) { const result = { pass: false }, diffBuilder = new j$.DiffBuilder({ prettyPrinter: matchersUtil.pp }); result.pass = matchersUtil.equals(actual, expected, diffBuilder); // TODO: only set error message if test fails result.message = diffBuilder.getMessage(); return result; } }; } return toEqual; }; getJasmineRequireObj().toHaveBeenCalled = function(j$) { const getErrorMsg = j$.formatErrorMsg( '', 'expect().toHaveBeenCalled()' ); /** * {@link expect} the actual (a {@link Spy}) to have been called. * @function * @name matchers#toHaveBeenCalled * @since 1.3.0 * @example * expect(mySpy).toHaveBeenCalled(); * expect(mySpy).not.toHaveBeenCalled(); */ function toHaveBeenCalled(matchersUtil) { return { compare: function(actual) { const result = {}; if (!j$.isSpy(actual)) { throw new Error( getErrorMsg( 'Expected a spy, but got ' + matchersUtil.pp(actual) + '.' ) ); } if (arguments.length > 1) { throw new Error( getErrorMsg('Does not take arguments, use toHaveBeenCalledWith') ); } result.pass = actual.calls.any(); result.message = result.pass ? 'Expected spy ' + actual.and.identity + ' not to have been called.' : 'Expected spy ' + actual.and.identity + ' to have been called.'; return result; } }; } return toHaveBeenCalled; }; getJasmineRequireObj().toHaveBeenCalledBefore = function(j$) { const getErrorMsg = j$.formatErrorMsg( '', 'expect().toHaveBeenCalledBefore()' ); /** * {@link expect} the actual value (a {@link Spy}) to have been called before another {@link Spy}. * @function * @name matchers#toHaveBeenCalledBefore * @since 2.6.0 * @param {Spy} expected - {@link Spy} that should have been called after the `actual` {@link Spy}. * @example * expect(mySpy).toHaveBeenCalledBefore(otherSpy); */ function toHaveBeenCalledBefore(matchersUtil) { return { compare: function(firstSpy, latterSpy) { if (!j$.isSpy(firstSpy)) { throw new Error( getErrorMsg( 'Expected a spy, but got ' + matchersUtil.pp(firstSpy) + '.' ) ); } if (!j$.isSpy(latterSpy)) { throw new Error( getErrorMsg( 'Expected a spy, but got ' + matchersUtil.pp(latterSpy) + '.' ) ); } const result = { pass: false }; if (!firstSpy.calls.count()) { result.message = 'Expected spy ' + firstSpy.and.identity + ' to have been called.'; return result; } if (!latterSpy.calls.count()) { result.message = 'Expected spy ' + latterSpy.and.identity + ' to have been called.'; return result; } const latest1stSpyCall = firstSpy.calls.mostRecent().invocationOrder; const first2ndSpyCall = latterSpy.calls.first().invocationOrder; result.pass = latest1stSpyCall < first2ndSpyCall; if (result.pass) { result.message = 'Expected spy ' + firstSpy.and.identity + ' to not have been called before spy ' + latterSpy.and.identity + ', but it was'; } else { const first1stSpyCall = firstSpy.calls.first().invocationOrder; const latest2ndSpyCall = latterSpy.calls.mostRecent().invocationOrder; if (first1stSpyCall < first2ndSpyCall) { result.message = 'Expected latest call to spy ' + firstSpy.and.identity + ' to have been called before first call to spy ' + latterSpy.and.identity + ' (no interleaved calls)'; } else if (latest2ndSpyCall > latest1stSpyCall) { result.message = 'Expected first call to spy ' + latterSpy.and.identity + ' to have been called after latest call to spy ' + firstSpy.and.identity + ' (no interleaved calls)'; } else { result.message = 'Expected spy ' + firstSpy.and.identity + ' to have been called before spy ' + latterSpy.and.identity; } } return result; } }; } return toHaveBeenCalledBefore; }; getJasmineRequireObj().toHaveBeenCalledOnceWith = function(j$) { const getErrorMsg = j$.formatErrorMsg( '', 'expect().toHaveBeenCalledOnceWith(...arguments)' ); /** * {@link expect} the actual (a {@link Spy}) to have been called exactly once, and exactly with the particular arguments. * @function * @name matchers#toHaveBeenCalledOnceWith * @since 3.6.0 * @param {...Object} - The arguments to look for * @example * expect(mySpy).toHaveBeenCalledOnceWith('foo', 'bar', 2); */ function toHaveBeenCalledOnceWith(util) { return { compare: function() { const args = Array.prototype.slice.call(arguments, 0), actual = args[0], expectedArgs = args.slice(1); if (!j$.isSpy(actual)) { throw new Error( getErrorMsg('Expected a spy, but got ' + util.pp(actual) + '.') ); } const prettyPrintedCalls = actual.calls .allArgs() .map(function(argsForCall) { return ' ' + util.pp(argsForCall); }); if ( actual.calls.count() === 1 && util.contains(actual.calls.allArgs(), expectedArgs) ) { return { pass: true, message: 'Expected spy ' + actual.and.identity + ' to have been called 0 times, multiple times, or once, but with arguments different from:\n' + ' ' + util.pp(expectedArgs) + '\n' + 'But the actual call was:\n' + prettyPrintedCalls.join(',\n') + '.\n\n' }; } function getDiffs() { return actual.calls.allArgs().map(function(argsForCall, callIx) { const diffBuilder = new j$.DiffBuilder(); util.equals(argsForCall, expectedArgs, diffBuilder); return diffBuilder.getMessage(); }); } function butString() { switch (actual.calls.count()) { case 0: return 'But it was never called.\n\n'; case 1: return ( 'But the actual call was:\n' + prettyPrintedCalls.join(',\n') + '.\n' + getDiffs().join('\n') + '\n\n' ); default: return ( 'But the actual calls were:\n' + prettyPrintedCalls.join(',\n') + '.\n\n' ); } } return { pass: false, message: 'Expected spy ' + actual.and.identity + ' to have been called only once, and with given args:\n' + ' ' + util.pp(expectedArgs) + '\n' + butString() }; } }; } return toHaveBeenCalledOnceWith; }; getJasmineRequireObj().toHaveBeenCalledTimes = function(j$) { const getErrorMsg = j$.formatErrorMsg( '', 'expect().toHaveBeenCalledTimes()' ); /** * {@link expect} the actual (a {@link Spy}) to have been called the specified number of times. * @function * @name matchers#toHaveBeenCalledTimes * @since 2.4.0 * @param {Number} expected - The number of invocations to look for. * @example * expect(mySpy).toHaveBeenCalledTimes(3); */ function toHaveBeenCalledTimes(matchersUtil) { return { compare: function(actual, expected) { if (!j$.isSpy(actual)) { throw new Error( getErrorMsg( 'Expected a spy, but got ' + matchersUtil.pp(actual) + '.' ) ); } const args = Array.prototype.slice.call(arguments, 0), result = { pass: false }; if (!j$.isNumber_(expected)) { throw new Error( getErrorMsg( 'The expected times failed is a required argument and must be a number.' ) ); } actual = args[0]; const calls = actual.calls.count(); const timesMessage = expected === 1 ? 'once' : expected + ' times'; result.pass = calls === expected; result.message = result.pass ? 'Expected spy ' + actual.and.identity + ' not to have been called ' + timesMessage + '. It was called ' + calls + ' times.' : 'Expected spy ' + actual.and.identity + ' to have been called ' + timesMessage + '. It was called ' + calls + ' times.'; return result; } }; } return toHaveBeenCalledTimes; }; getJasmineRequireObj().toHaveBeenCalledWith = function(j$) { const getErrorMsg = j$.formatErrorMsg( '', 'expect().toHaveBeenCalledWith(...arguments)' ); /** * {@link expect} the actual (a {@link Spy}) to have been called with particular arguments at least once. * @function * @name matchers#toHaveBeenCalledWith * @since 1.3.0 * @param {...Object} - The arguments to look for * @example * expect(mySpy).toHaveBeenCalledWith('foo', 'bar', 2); */ function toHaveBeenCalledWith(matchersUtil) { return { compare: function() { const args = Array.prototype.slice.call(arguments, 0), actual = args[0], expectedArgs = args.slice(1), result = { pass: false }; if (!j$.isSpy(actual)) { throw new Error( getErrorMsg( 'Expected a spy, but got ' + matchersUtil.pp(actual) + '.' ) ); } if (!actual.calls.any()) { result.message = function() { return ( 'Expected spy ' + actual.and.identity + ' to have been called with:\n' + ' ' + matchersUtil.pp(expectedArgs) + '\nbut it was never called.' ); }; return result; } if (matchersUtil.contains(actual.calls.allArgs(), expectedArgs)) { result.pass = true; result.message = function() { return ( 'Expected spy ' + actual.and.identity + ' not to have been called with:\n' + ' ' + matchersUtil.pp(expectedArgs) + '\nbut it was.' ); }; } else { result.message = function() { const prettyPrintedCalls = actual.calls .allArgs() .map(function(argsForCall) { return ' ' + matchersUtil.pp(argsForCall); }); const diffs = actual.calls .allArgs() .map(function(argsForCall, callIx) { const diffBuilder = new j$.DiffBuilder(); matchersUtil.equals(argsForCall, expectedArgs, diffBuilder); return ( 'Call ' + callIx + ':\n' + diffBuilder.getMessage().replace(/^/gm, ' ') ); }); return ( 'Expected spy ' + actual.and.identity + ' to have been called with:\n' + ' ' + matchersUtil.pp(expectedArgs) + '\n' + '' + 'but actual calls were:\n' + prettyPrintedCalls.join(',\n') + '.\n\n' + diffs.join('\n') ); }; } return result; } }; } return toHaveBeenCalledWith; }; getJasmineRequireObj().toHaveClass = function(j$) { /** * {@link expect} the actual value to be a DOM element that has the expected class * @function * @name matchers#toHaveClass * @since 3.0.0 * @param {Object} expected - The class name to test for * @example * const el = document.createElement('div'); * el.className = 'foo bar baz'; * expect(el).toHaveClass('bar'); */ function toHaveClass(matchersUtil) { return { compare: function(actual, expected) { if (!isElement(actual)) { throw new Error(matchersUtil.pp(actual) + ' is not a DOM element'); } return { pass: actual.classList.contains(expected) }; } }; } function isElement(maybeEl) { return ( maybeEl && maybeEl.classList && j$.isFunction_(maybeEl.classList.contains) ); } return toHaveClass; }; getJasmineRequireObj().toHaveSize = function(j$) { /** * {@link expect} the actual size to be equal to the expected, using array-like length or object keys size. * @function * @name matchers#toHaveSize * @since 3.6.0 * @param {Object} expected - Expected size * @example * array = [1,2]; * expect(array).toHaveSize(2); */ function toHaveSize() { return { compare: function(actual, expected) { const result = { pass: false }; if ( j$.isA_('WeakSet', actual) || j$.isWeakMap(actual) || j$.isDataView(actual) ) { throw new Error('Cannot get size of ' + actual + '.'); } if (j$.isSet(actual) || j$.isMap(actual)) { result.pass = actual.size === expected; } else if (isLength(actual.length)) { result.pass = actual.length === expected; } else { result.pass = Object.keys(actual).length === expected; } return result; } }; } const MAX_SAFE_INTEGER = Number.MAX_SAFE_INTEGER || 9007199254740991; function isLength(value) { return ( typeof value == 'number' && value > -1 && value % 1 === 0 && value <= MAX_SAFE_INTEGER ); } return toHaveSize; }; getJasmineRequireObj().toHaveSpyInteractions = function(j$) { const getErrorMsg = j$.formatErrorMsg( '', 'expect().toHaveSpyInteractions()' ); /** * {@link expect} the actual (a {@link SpyObj}) spies to have been called. * @function * @name matchers#toHaveSpyInteractions * @since 4.1.0 * @example * expect(mySpyObj).toHaveSpyInteractions(); * expect(mySpyObj).not.toHaveSpyInteractions(); */ function toHaveSpyInteractions(matchersUtil) { return { compare: function(actual) { const result = {}; if (!j$.isObject_(actual)) { throw new Error( getErrorMsg('Expected a spy object, but got ' + typeof actual + '.') ); } if (arguments.length > 1) { throw new Error(getErrorMsg('Does not take arguments')); } result.pass = false; let hasSpy = false; const calledSpies = []; for (const spy of Object.values(actual)) { if (!j$.isSpy(spy)) continue; hasSpy = true; if (spy.calls.any()) { result.pass = true; calledSpies.push([spy.and.identity, spy.calls.count()]); } } if (!hasSpy) { throw new Error( getErrorMsg( 'Expected a spy object with spies, but object has no spies.' ) ); } let resultMessage; if (result.pass) { resultMessage = 'Expected spy object spies not to have been called, ' + 'but the following spies were called: '; resultMessage += calledSpies .map(([spyName, spyCount]) => { return `${spyName} called ${spyCount} time(s)`; }) .join(', '); } else { resultMessage = 'Expected spy object spies to have been called, ' + 'but no spies were called.'; } result.message = resultMessage; return result; } }; } return toHaveSpyInteractions; }; getJasmineRequireObj().toMatch = function(j$) { const getErrorMsg = j$.formatErrorMsg( '', 'expect().toMatch( || )' ); /** * {@link expect} the actual value to match a regular expression * @function * @name matchers#toMatch * @since 1.3.0 * @param {RegExp|String} expected - Value to look for in the string. * @example * expect("my string").toMatch(/string$/); * expect("other string").toMatch("her"); */ function toMatch() { return { compare: function(actual, expected) { if (!j$.isString_(expected) && !j$.isA_('RegExp', expected)) { throw new Error(getErrorMsg('Expected is not a String or a RegExp')); } const regexp = new RegExp(expected); return { pass: regexp.test(actual) }; } }; } return toMatch; }; getJasmineRequireObj().toThrow = function(j$) { const getErrorMsg = j$.formatErrorMsg( '', 'expect(function() {}).toThrow()' ); /** * {@link expect} a function to `throw` something. * @function * @name matchers#toThrow * @since 2.0.0 * @param {Object} [expected] - Value that should be thrown. If not provided, simply the fact that something was thrown will be checked. * @example * expect(function() { return 'things'; }).toThrow('foo'); * expect(function() { return 'stuff'; }).toThrow(); */ function toThrow(matchersUtil) { return { compare: function(actual, expected) { const result = { pass: false }; let threw = false; let thrown; if (typeof actual != 'function') { throw new Error(getErrorMsg('Actual is not a Function')); } try { actual(); } catch (e) { threw = true; thrown = e; } if (!threw) { result.message = 'Expected function to throw an exception.'; return result; } if (arguments.length == 1) { result.pass = true; result.message = function() { return ( 'Expected function not to throw, but it threw ' + matchersUtil.pp(thrown) + '.' ); }; return result; } if (matchersUtil.equals(thrown, expected)) { result.pass = true; result.message = function() { return ( 'Expected function not to throw ' + matchersUtil.pp(expected) + '.' ); }; } else { result.message = function() { return ( 'Expected function to throw ' + matchersUtil.pp(expected) + ', but it threw ' + matchersUtil.pp(thrown) + '.' ); }; } return result; } }; } return toThrow; }; getJasmineRequireObj().toThrowError = function(j$) { const getErrorMsg = j$.formatErrorMsg( '', 'expect(function() {}).toThrowError(, )' ); /** * {@link expect} a function to `throw` an `Error`. * @function * @name matchers#toThrowError * @since 2.0.0 * @param {Error} [expected] - `Error` constructor the object that was thrown needs to be an instance of. If not provided, `Error` will be used. * @param {RegExp|String} [message] - The message that should be set on the thrown `Error` * @example * expect(function() { return 'things'; }).toThrowError(MyCustomError, 'message'); * expect(function() { return 'things'; }).toThrowError(MyCustomError, /bar/); * expect(function() { return 'stuff'; }).toThrowError(MyCustomError); * expect(function() { return 'other'; }).toThrowError(/foo/); * expect(function() { return 'other'; }).toThrowError(); */ function toThrowError(matchersUtil) { return { compare: function(actual) { const errorMatcher = getMatcher.apply(null, arguments); if (typeof actual != 'function') { throw new Error(getErrorMsg('Actual is not a Function')); } let thrown; try { actual(); return fail('Expected function to throw an Error.'); } catch (e) { thrown = e; } if (!j$.isError_(thrown)) { return fail(function() { return ( 'Expected function to throw an Error, but it threw ' + matchersUtil.pp(thrown) + '.' ); }); } return errorMatcher.match(thrown); } }; function getMatcher() { let expected, errorType; if (arguments[2]) { errorType = arguments[1]; expected = arguments[2]; if (!isAnErrorType(errorType)) { throw new Error(getErrorMsg('Expected error type is not an Error.')); } return exactMatcher(expected, errorType); } else if (arguments[1]) { expected = arguments[1]; if (isAnErrorType(arguments[1])) { return exactMatcher(null, arguments[1]); } else { return exactMatcher(arguments[1], null); } } else { return anyMatcher(); } } function anyMatcher() { return { match: function(error) { return pass( 'Expected function not to throw an Error, but it threw ' + j$.fnNameFor(error) + '.' ); } }; } function exactMatcher(expected, errorType) { if (expected && !isStringOrRegExp(expected)) { if (errorType) { throw new Error( getErrorMsg('Expected error message is not a string or RegExp.') ); } else { throw new Error( getErrorMsg('Expected is not an Error, string, or RegExp.') ); } } function messageMatch(message) { if (typeof expected == 'string') { return expected == message; } else { return expected.test(message); } } const errorTypeDescription = errorType ? j$.fnNameFor(errorType) : 'an exception'; function thrownDescription(thrown) { const thrownName = errorType ? j$.fnNameFor(thrown.constructor) : 'an exception'; let thrownMessage = ''; if (expected) { thrownMessage = ' with message ' + matchersUtil.pp(thrown.message); } return thrownName + thrownMessage; } function messageDescription() { if (expected === null) { return ''; } else if (expected instanceof RegExp) { return ' with a message matching ' + matchersUtil.pp(expected); } else { return ' with message ' + matchersUtil.pp(expected); } } function matches(error) { return ( (errorType === null || error instanceof errorType) && (expected === null || messageMatch(error.message)) ); } return { match: function(thrown) { if (matches(thrown)) { return pass(function() { return ( 'Expected function not to throw ' + errorTypeDescription + messageDescription() + '.' ); }); } else { return fail(function() { return ( 'Expected function to throw ' + errorTypeDescription + messageDescription() + ', but it threw ' + thrownDescription(thrown) + '.' ); }); } } }; } function isStringOrRegExp(potential) { return potential instanceof RegExp || typeof potential == 'string'; } function isAnErrorType(type) { if (typeof type !== 'function') { return false; } const Surrogate = function() {}; Surrogate.prototype = type.prototype; return j$.isError_(new Surrogate()); } } function pass(message) { return { pass: true, message: message }; } function fail(message) { return { pass: false, message: message }; } return toThrowError; }; getJasmineRequireObj().toThrowMatching = function(j$) { const usageError = j$.formatErrorMsg( '', 'expect(function() {}).toThrowMatching()' ); /** * {@link expect} a function to `throw` something matching a predicate. * @function * @name matchers#toThrowMatching * @since 3.0.0 * @param {Function} predicate - A function that takes the thrown exception as its parameter and returns true if it matches. * @example * expect(function() { throw new Error('nope'); }).toThrowMatching(function(thrown) { return thrown.message === 'nope'; }); */ function toThrowMatching(matchersUtil) { return { compare: function(actual, predicate) { if (typeof actual !== 'function') { throw new Error(usageError('Actual is not a Function')); } if (typeof predicate !== 'function') { throw new Error(usageError('Predicate is not a Function')); } let thrown; try { actual(); return fail('Expected function to throw an exception.'); } catch (e) { thrown = e; } if (predicate(thrown)) { return pass( 'Expected function not to throw an exception matching a predicate.' ); } else { return fail(function() { return ( 'Expected function to throw an exception matching a predicate, ' + 'but it threw ' + thrownDescription(thrown) + '.' ); }); } } }; function thrownDescription(thrown) { if (thrown && thrown.constructor) { return ( j$.fnNameFor(thrown.constructor) + ' with message ' + matchersUtil.pp(thrown.message) ); } else { return matchersUtil.pp(thrown); } } } function pass(message) { return { pass: true, message: message }; } function fail(message) { return { pass: false, message: message }; } return toThrowMatching; }; getJasmineRequireObj().MockDate = function(j$) { function MockDate(global) { let currentTime = 0; if (!global || !global.Date) { this.install = function() {}; this.tick = function() {}; this.uninstall = function() {}; return this; } const GlobalDate = global.Date; this.install = function(mockDate) { if (mockDate instanceof GlobalDate) { currentTime = mockDate.getTime(); } else { if (!j$.util.isUndefined(mockDate)) { throw new Error( 'The argument to jasmine.clock().mockDate(), if specified, ' + 'should be a Date instance.' ); } currentTime = new GlobalDate().getTime(); } global.Date = FakeDate; }; this.tick = function(millis) { millis = millis || 0; currentTime = currentTime + millis; }; this.uninstall = function() { currentTime = 0; global.Date = GlobalDate; }; createDateProperties(); return this; function FakeDate() { switch (arguments.length) { case 0: return new GlobalDate(currentTime); case 1: return new GlobalDate(arguments[0]); case 2: return new GlobalDate(arguments[0], arguments[1]); case 3: return new GlobalDate(arguments[0], arguments[1], arguments[2]); case 4: return new GlobalDate( arguments[0], arguments[1], arguments[2], arguments[3] ); case 5: return new GlobalDate( arguments[0], arguments[1], arguments[2], arguments[3], arguments[4] ); case 6: return new GlobalDate( arguments[0], arguments[1], arguments[2], arguments[3], arguments[4], arguments[5] ); default: return new GlobalDate( arguments[0], arguments[1], arguments[2], arguments[3], arguments[4], arguments[5], arguments[6] ); } } function createDateProperties() { FakeDate.prototype = GlobalDate.prototype; FakeDate.now = function() { return currentTime; }; FakeDate.toSource = GlobalDate.toSource; FakeDate.toString = GlobalDate.toString; FakeDate.parse = GlobalDate.parse; FakeDate.UTC = GlobalDate.UTC; } } return MockDate; }; getJasmineRequireObj().NeverSkipPolicy = function(j$) { function NeverSkipPolicy(queueableFns) {} NeverSkipPolicy.prototype.skipTo = function(lastRanFnIx) { return lastRanFnIx + 1; }; NeverSkipPolicy.prototype.fnErrored = function(fnIx) {}; return NeverSkipPolicy; }; getJasmineRequireObj().makePrettyPrinter = function(j$) { class SinglePrettyPrintRun { constructor(customObjectFormatters, pp) { this.customObjectFormatters_ = customObjectFormatters; this.ppNestLevel_ = 0; this.seen = []; this.length = 0; this.stringParts = []; this.pp_ = pp; } format(value) { this.ppNestLevel_++; try { const customFormatResult = this.applyCustomFormatters_(value); if (customFormatResult) { this.emitScalar(customFormatResult); } else if (j$.util.isUndefined(value)) { this.emitScalar('undefined'); } else if (value === null) { this.emitScalar('null'); } else if (value === 0 && 1 / value === -Infinity) { this.emitScalar('-0'); } else if (value === j$.getGlobal()) { this.emitScalar(''); } else if (value.jasmineToString) { this.emitScalar(value.jasmineToString(this.pp_)); } else if (j$.isString_(value)) { this.emitString(value); } else if (j$.isSpy(value)) { this.emitScalar('spy on ' + value.and.identity); } else if (j$.isSpy(value.toString)) { this.emitScalar('spy on ' + value.toString.and.identity); } else if (value instanceof RegExp) { this.emitScalar(value.toString()); } else if (typeof value === 'function') { this.emitScalar('Function'); } else if (j$.isDomNode(value)) { if (value.tagName) { this.emitDomElement(value); } else { this.emitScalar('HTMLNode'); } } else if (value instanceof Date) { this.emitScalar('Date(' + value + ')'); } else if (j$.isSet(value)) { this.emitSet(value); } else if (j$.isMap(value)) { this.emitMap(value); } else if (j$.isTypedArray_(value)) { this.emitTypedArray(value); } else if ( value.toString && typeof value === 'object' && !j$.isArray_(value) && hasCustomToString(value) ) { try { this.emitScalar(value.toString()); } catch (e) { this.emitScalar('has-invalid-toString-method'); } } else if (this.seen.includes(value)) { this.emitScalar( '' ); } else if (j$.isArray_(value) || j$.isA_('Object', value)) { this.seen.push(value); if (j$.isArray_(value)) { this.emitArray(value); } else { this.emitObject(value); } this.seen.pop(); } else { this.emitScalar(value.toString()); } } catch (e) { if (this.ppNestLevel_ > 1 || !(e instanceof MaxCharsReachedError)) { throw e; } } finally { this.ppNestLevel_--; } } applyCustomFormatters_(value) { return customFormat(value, this.customObjectFormatters_); } iterateObject(obj, fn) { const objKeys = j$.MatchersUtil.keys(obj, j$.isArray_(obj)); const length = Math.min(objKeys.length, j$.MAX_PRETTY_PRINT_ARRAY_LENGTH); for (let i = 0; i < length; i++) { fn(objKeys[i]); } return objKeys.length > length; } emitScalar(value) { this.append(value); } emitString(value) { this.append("'" + value + "'"); } emitArray(array) { if (this.ppNestLevel_ > j$.MAX_PRETTY_PRINT_DEPTH) { this.append('Array'); return; } const length = Math.min(array.length, j$.MAX_PRETTY_PRINT_ARRAY_LENGTH); this.append('[ '); for (let i = 0; i < length; i++) { if (i > 0) { this.append(', '); } this.format(array[i]); } if (array.length > length) { this.append(', ...'); } let first = array.length === 0; const wasTruncated = this.iterateObject(array, property => { if (first) { first = false; } else { this.append(', '); } this.formatProperty(array, property); }); if (wasTruncated) { this.append(', ...'); } this.append(' ]'); } emitSet(set) { if (this.ppNestLevel_ > j$.MAX_PRETTY_PRINT_DEPTH) { this.append('Set'); return; } this.append('Set( '); const size = Math.min(set.size, j$.MAX_PRETTY_PRINT_ARRAY_LENGTH); let i = 0; set.forEach(function(value, key) { if (i >= size) { return; } if (i > 0) { this.append(', '); } this.format(value); i++; }, this); if (set.size > size) { this.append(', ...'); } this.append(' )'); } emitMap(map) { if (this.ppNestLevel_ > j$.MAX_PRETTY_PRINT_DEPTH) { this.append('Map'); return; } this.append('Map( '); const size = Math.min(map.size, j$.MAX_PRETTY_PRINT_ARRAY_LENGTH); let i = 0; map.forEach(function(value, key) { if (i >= size) { return; } if (i > 0) { this.append(', '); } this.format([key, value]); i++; }, this); if (map.size > size) { this.append(', ...'); } this.append(' )'); } emitObject(obj) { const ctor = obj.constructor; const constructorName = typeof ctor === 'function' && obj instanceof ctor ? j$.fnNameFor(obj.constructor) : 'null'; this.append(constructorName); if (this.ppNestLevel_ > j$.MAX_PRETTY_PRINT_DEPTH) { return; } this.append('({ '); let first = true; const wasTruncated = this.iterateObject(obj, property => { if (first) { first = false; } else { this.append(', '); } this.formatProperty(obj, property); }); if (wasTruncated) { this.append(', ...'); } this.append(' })'); } emitTypedArray(arr) { const constructorName = j$.fnNameFor(arr.constructor); const limitedArray = Array.prototype.slice.call( arr, 0, j$.MAX_PRETTY_PRINT_ARRAY_LENGTH ); let itemsString = Array.prototype.join.call(limitedArray, ', '); if (limitedArray.length !== arr.length) { itemsString += ', ...'; } this.append(constructorName + ' [ ' + itemsString + ' ]'); } emitDomElement(el) { const tagName = el.tagName.toLowerCase(); let out = '<' + tagName; for (const attr of el.attributes) { out += ' ' + attr.name; if (attr.value !== '') { out += '="' + attr.value + '"'; } } out += '>'; if (el.childElementCount !== 0 || el.textContent !== '') { out += '...'; } this.append(out); } formatProperty(obj, property) { if (typeof property === 'symbol') { this.append(property.toString()); } else { this.append(property); } this.append(': '); this.format(obj[property]); } append(value) { // This check protects us from the rare case where an object has overriden // `toString()` with an invalid implementation (returning a non-string). if (typeof value !== 'string') { value = Object.prototype.toString.call(value); } const result = truncate(value, j$.MAX_PRETTY_PRINT_CHARS - this.length); this.length += result.value.length; this.stringParts.push(result.value); if (result.truncated) { throw new MaxCharsReachedError(); } } } function hasCustomToString(value) { // value.toString !== Object.prototype.toString if value has no custom toString but is from another context (e.g. // iframe, web worker) try { return ( j$.isFunction_(value.toString) && value.toString !== Object.prototype.toString && value.toString() !== Object.prototype.toString.call(value) ); } catch (e) { // The custom toString() threw. return true; } } function truncate(s, maxlen) { if (s.length <= maxlen) { return { value: s, truncated: false }; } s = s.substring(0, maxlen - 4) + ' ...'; return { value: s, truncated: true }; } function MaxCharsReachedError() { this.message = 'Exceeded ' + j$.MAX_PRETTY_PRINT_CHARS + ' characters while pretty-printing a value'; } MaxCharsReachedError.prototype = new Error(); function customFormat(value, customObjectFormatters) { for (const formatter of customObjectFormatters) { const result = formatter(value); if (result !== undefined) { return result; } } } return function(customObjectFormatters) { customObjectFormatters = customObjectFormatters || []; const pp = function(value) { const prettyPrinter = new SinglePrettyPrintRun( customObjectFormatters, pp ); prettyPrinter.format(value); return prettyPrinter.stringParts.join(''); }; pp.customFormat_ = function(value) { return customFormat(value, customObjectFormatters); }; return pp; }; }; getJasmineRequireObj().QueueRunner = function(j$) { let nextid = 1; function StopExecutionError() {} StopExecutionError.prototype = new Error(); j$.StopExecutionError = StopExecutionError; function once(fn, onTwice) { let called = false; return function(arg) { if (called) { if (onTwice) { onTwice(); } } else { called = true; // Direct call using single parameter, because cleanup/next does not need more fn(arg); } return null; }; } function fallbackOnMultipleDone() { console.error( new Error( "An asynchronous function called its 'done' " + 'callback more than once, in a QueueRunner without a onMultipleDone ' + 'handler.' ) ); } function emptyFn() {} function QueueRunner(attrs) { this.id_ = nextid++; this.queueableFns = attrs.queueableFns || []; this.onComplete = attrs.onComplete || emptyFn; this.clearStack = attrs.clearStack || function(fn) { fn(); }; this.onException = attrs.onException || emptyFn; this.onMultipleDone = attrs.onMultipleDone || fallbackOnMultipleDone; this.userContext = attrs.userContext || new j$.UserContext(); this.timeout = attrs.timeout || { setTimeout: setTimeout, clearTimeout: clearTimeout }; this.fail = attrs.fail || emptyFn; this.globalErrors = attrs.globalErrors || { pushListener: emptyFn, popListener: emptyFn }; const SkipPolicy = attrs.SkipPolicy || j$.NeverSkipPolicy; this.skipPolicy_ = new SkipPolicy(this.queueableFns); this.errored_ = false; if (typeof this.onComplete !== 'function') { throw new Error('invalid onComplete ' + JSON.stringify(this.onComplete)); } this.deprecated = attrs.deprecated; } QueueRunner.prototype.execute = function() { this.handleFinalError = (message, source, lineno, colno, error) => { // Older browsers would send the error as the first parameter. HTML5 // specifies the the five parameters above. The error instance should // be preffered, otherwise the call stack would get lost. this.onException(error || message); }; this.globalErrors.pushListener(this.handleFinalError); this.run(0); }; QueueRunner.prototype.clearTimeout = function(timeoutId) { Function.prototype.apply.apply(this.timeout.clearTimeout, [ j$.getGlobal(), [timeoutId] ]); }; QueueRunner.prototype.setTimeout = function(fn, timeout) { return Function.prototype.apply.apply(this.timeout.setTimeout, [ j$.getGlobal(), [fn, timeout] ]); }; QueueRunner.prototype.attempt = function attempt(iterativeIndex) { let timeoutId; let timedOut; let completedSynchronously = true; const onException = e => { this.onException(e); this.recordError_(iterativeIndex); }; function handleError(error) { // TODO probably shouldn't next() right away here. // That makes debugging async failures much more confusing. onException(error); } const cleanup = once(() => { if (timeoutId !== void 0) { this.clearTimeout(timeoutId); } this.globalErrors.popListener(handleError); }); const next = once( err => { cleanup(); if (typeof err !== 'undefined') { if (!(err instanceof StopExecutionError) && !err.jasmineMessage) { this.fail(err); } this.recordError_(iterativeIndex); } const runNext = () => { this.run(this.nextFnIx_(iterativeIndex)); }; if (completedSynchronously) { this.setTimeout(runNext); } else { runNext(); } }, () => { try { if (!timedOut) { this.onMultipleDone(); } } catch (error) { // Any error we catch here is probably due to a bug in Jasmine, // and it's not likely to end up anywhere useful if we let it // propagate. Log it so it can at least show up when debugging. console.error(error); } } ); timedOut = false; const queueableFn = this.queueableFns[iterativeIndex]; next.fail = function nextFail() { this.fail.apply(null, arguments); this.recordError_(iterativeIndex); next(); }.bind(this); this.globalErrors.pushListener(handleError); if (queueableFn.timeout !== undefined) { const timeoutInterval = queueableFn.timeout || j$.DEFAULT_TIMEOUT_INTERVAL; timeoutId = this.setTimeout(function() { timedOut = true; const error = new Error( 'Timeout - Async function did not complete within ' + timeoutInterval + 'ms ' + (queueableFn.timeout ? '(custom timeout)' : '(set by jasmine.DEFAULT_TIMEOUT_INTERVAL)') ); // TODO Need to decide what to do about a successful completion after a // timeout. That should probably not be a deprecation, and maybe not // an error in 4.0. (But a diagnostic of some sort might be helpful.) onException(error); next(); }, timeoutInterval); } try { let maybeThenable; if (queueableFn.fn.length === 0) { maybeThenable = queueableFn.fn.call(this.userContext); if (maybeThenable && j$.isFunction_(maybeThenable.then)) { maybeThenable.then( wrapInPromiseResolutionHandler(next), onPromiseRejection ); completedSynchronously = false; return { completedSynchronously: false }; } } else { maybeThenable = queueableFn.fn.call(this.userContext, next); this.diagnoseConflictingAsync_(queueableFn.fn, maybeThenable); completedSynchronously = false; return { completedSynchronously: false }; } } catch (e) { onException(e); this.recordError_(iterativeIndex); } cleanup(); return { completedSynchronously: true }; function onPromiseRejection(e) { onException(e); next(); } }; QueueRunner.prototype.run = function(recursiveIndex) { const length = this.queueableFns.length; for ( let iterativeIndex = recursiveIndex; iterativeIndex < length; iterativeIndex = this.nextFnIx_(iterativeIndex) ) { const result = this.attempt(iterativeIndex); if (!result.completedSynchronously) { return; } } this.clearStack(() => { this.globalErrors.popListener(this.handleFinalError); if (this.errored_) { this.onComplete(new StopExecutionError()); } else { this.onComplete(); } }); }; QueueRunner.prototype.nextFnIx_ = function(currentFnIx) { const result = this.skipPolicy_.skipTo(currentFnIx); if (result === currentFnIx) { throw new Error("Can't skip to the same queueable fn that just finished"); } return result; }; QueueRunner.prototype.recordError_ = function(currentFnIx) { this.errored_ = true; this.skipPolicy_.fnErrored(currentFnIx); }; QueueRunner.prototype.diagnoseConflictingAsync_ = function(fn, retval) { if (retval && j$.isFunction_(retval.then)) { // Issue a warning that matches the user's code. // Omit the stack trace because there's almost certainly no user code // on the stack at this point. if (j$.isAsyncFunction_(fn)) { this.onException( 'An asynchronous before/it/after ' + 'function was defined with the async keyword but also took a ' + 'done callback. Either remove the done callback (recommended) or ' + 'remove the async keyword.' ); } else { this.onException( '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.' ); } } }; function wrapInPromiseResolutionHandler(fn) { return function(maybeArg) { if (j$.isError_(maybeArg)) { fn(maybeArg); } else { fn(); } }; } return QueueRunner; }; getJasmineRequireObj().ReportDispatcher = function(j$) { function ReportDispatcher(methods, queueRunnerFactory, onLateError) { const dispatchedMethods = methods || []; for (const method of dispatchedMethods) { this[method] = (function(m) { return function() { return dispatch(m, arguments); }; })(method); } let reporters = []; let fallbackReporter = null; this.addReporter = function(reporter) { reporters.push(reporter); }; this.provideFallbackReporter = function(reporter) { fallbackReporter = reporter; }; this.clearReporters = function() { reporters = []; }; return this; function dispatch(method, args) { if (reporters.length === 0 && fallbackReporter !== null) { reporters.push(fallbackReporter); } const fns = []; for (const reporter of reporters) { addFn(fns, reporter, method, args); } return new Promise(function(resolve) { queueRunnerFactory({ queueableFns: fns, onComplete: resolve, isReporter: true, onMultipleDone: function() { onLateError( new Error( "An asynchronous reporter callback called its 'done' callback " + 'more than once.' ) ); } }); }); } function addFn(fns, reporter, method, args) { const fn = reporter[method]; if (!fn) { return; } const thisArgs = j$.util.cloneArgs(args); if (fn.length <= 1) { fns.push({ fn: function() { return fn.apply(reporter, thisArgs); } }); } else { fns.push({ fn: function(done) { return fn.apply(reporter, thisArgs.concat([done])); } }); } } } return ReportDispatcher; }; getJasmineRequireObj().interface = function(jasmine, env) { const jasmineInterface = { /** * Callback passed to parts of the Jasmine base interface. * * By default Jasmine assumes this function completes synchronously. * If you have code that you need to test asynchronously, you can declare that you receive a `done` callback, return a Promise, or use the `async` keyword if it is supported in your environment. * @callback implementationCallback * @param {Function} [done] Used to specify to Jasmine that this callback is asynchronous and Jasmine should wait until it has been called before moving on. * @returns {} Optionally return a Promise instead of using `done` to cause Jasmine to wait for completion. */ /** * Create a group of specs (often called a suite). * * Calls to `describe` can be nested within other calls to compose your suite as a tree. * @name describe * @since 1.3.0 * @function * @global * @param {String} description Textual description of the group * @param {Function} specDefinitions Function for Jasmine to invoke that will define inner suites and specs */ describe: function(description, specDefinitions) { return env.describe(description, specDefinitions); }, /** * A temporarily disabled [`describe`]{@link describe} * * Specs within an `xdescribe` will be marked pending and not executed * @name xdescribe * @since 1.3.0 * @function * @global * @param {String} description Textual description of the group * @param {Function} specDefinitions Function for Jasmine to invoke that will define inner suites and specs */ xdescribe: function(description, specDefinitions) { return env.xdescribe(description, specDefinitions); }, /** * A focused [`describe`]{@link describe} * * If suites or specs are focused, only those that are focused will be executed * @see fit * @name fdescribe * @since 2.1.0 * @function * @global * @param {String} description Textual description of the group * @param {Function} specDefinitions Function for Jasmine to invoke that will define inner suites and specs */ fdescribe: function(description, specDefinitions) { return env.fdescribe(description, specDefinitions); }, /** * Define a single spec. A spec should contain one or more {@link expect|expectations} that test the state of the code. * * A spec whose expectations all succeed will be passing and a spec with any failures will fail. * The name `it` is a pronoun for the test target, not an abbreviation of anything. It makes the * spec more readable by connecting the function name `it` and the argument `description` as a * complete sentence. * @name it * @since 1.3.0 * @function * @global * @param {String} description Textual description of what this spec is checking * @param {implementationCallback} [testFunction] Function that contains the code of your test. If not provided the test will be `pending`. * @param {Int} [timeout={@link jasmine.DEFAULT_TIMEOUT_INTERVAL}] Custom timeout for an async spec. * @see async */ it: function() { return env.it.apply(env, arguments); }, /** * A temporarily disabled [`it`]{@link it} * * The spec will report as `pending` and will not be executed. * @name xit * @since 1.3.0 * @function * @global * @param {String} description Textual description of what this spec is checking. * @param {implementationCallback} [testFunction] Function that contains the code of your test. Will not be executed. */ xit: function() { return env.xit.apply(env, arguments); }, /** * A focused [`it`]{@link it} * * If suites or specs are focused, only those that are focused will be executed. * @name fit * @since 2.1.0 * @function * @global * @param {String} description Textual description of what this spec is checking. * @param {implementationCallback} testFunction Function that contains the code of your test. * @param {Int} [timeout={@link jasmine.DEFAULT_TIMEOUT_INTERVAL}] Custom timeout for an async spec. * @see async */ fit: function() { return env.fit.apply(env, arguments); }, /** * Run some shared setup before each of the specs in the {@link describe} in which it is called. * @name beforeEach * @since 1.3.0 * @function * @global * @param {implementationCallback} [function] Function that contains the code to setup your specs. * @param {Int} [timeout={@link jasmine.DEFAULT_TIMEOUT_INTERVAL}] Custom timeout for an async beforeEach. * @see async */ beforeEach: function() { return env.beforeEach.apply(env, arguments); }, /** * Run some shared teardown after each of the specs in the {@link describe} in which it is called. * @name afterEach * @since 1.3.0 * @function * @global * @param {implementationCallback} [function] Function that contains the code to teardown your specs. * @param {Int} [timeout={@link jasmine.DEFAULT_TIMEOUT_INTERVAL}] Custom timeout for an async afterEach. * @see async */ afterEach: function() { return env.afterEach.apply(env, arguments); }, /** * Run some shared setup once before all of the specs in the {@link describe} are run. * * _Note:_ Be careful, sharing the setup from a beforeAll makes it easy to accidentally leak state between your specs so that they erroneously pass or fail. * @name beforeAll * @since 2.1.0 * @function * @global * @param {implementationCallback} [function] Function that contains the code to setup your specs. * @param {Int} [timeout={@link jasmine.DEFAULT_TIMEOUT_INTERVAL}] Custom timeout for an async beforeAll. * @see async */ beforeAll: function() { return env.beforeAll.apply(env, arguments); }, /** * Run some shared teardown once after all of the specs in the {@link describe} are run. * * _Note:_ Be careful, sharing the teardown from a afterAll makes it easy to accidentally leak state between your specs so that they erroneously pass or fail. * @name afterAll * @since 2.1.0 * @function * @global * @param {implementationCallback} [function] Function that contains the code to teardown your specs. * @param {Int} [timeout={@link jasmine.DEFAULT_TIMEOUT_INTERVAL}] Custom timeout for an async afterAll. * @see async */ afterAll: function() { return env.afterAll.apply(env, arguments); }, /** * Sets a user-defined property that will be provided to reporters as part of the properties field of {@link SpecResult} * @name setSpecProperty * @since 3.6.0 * @function * @param {String} key The name of the property * @param {*} value The value of the property */ setSpecProperty: function(key, value) { return env.setSpecProperty(key, value); }, /** * Sets a user-defined property that will be provided to reporters as part of the properties field of {@link SuiteResult} * @name setSuiteProperty * @since 3.6.0 * @function * @param {String} key The name of the property * @param {*} value The value of the property */ setSuiteProperty: function(key, value) { return env.setSuiteProperty(key, value); }, /** * Create an expectation for a spec. * @name expect * @since 1.3.0 * @function * @global * @param {Object} actual - Actual computed value to test expectations against. * @return {matchers} */ expect: function(actual) { return env.expect(actual); }, /** * Create an asynchronous expectation for a spec. Note that the matchers * that are provided by an asynchronous expectation all return promises * which must be either returned from the spec or waited for using `await` * in order for Jasmine to associate them with the correct spec. * @name expectAsync * @since 3.3.0 * @function * @global * @param {Object} actual - Actual computed value to test expectations against. * @return {async-matchers} * @example * await expectAsync(somePromise).toBeResolved(); * @example * return expectAsync(somePromise).toBeResolved(); */ expectAsync: function(actual) { return env.expectAsync(actual); }, /** * Mark a spec as pending, expectation results will be ignored. * @name pending * @since 2.0.0 * @function * @global * @param {String} [message] - Reason the spec is pending. */ pending: function() { return env.pending.apply(env, arguments); }, /** * Explicitly mark a spec as failed. * @name fail * @since 2.1.0 * @function * @global * @param {String|Error} [error] - Reason for the failure. */ fail: function() { return env.fail.apply(env, arguments); }, /** * Install a spy onto an existing object. * @name spyOn * @since 1.3.0 * @function * @global * @param {Object} obj - The object upon which to install the {@link Spy}. * @param {String} methodName - The name of the method to replace with a {@link Spy}. * @returns {Spy} */ spyOn: function(obj, methodName) { return env.spyOn(obj, methodName); }, /** * Install a spy on a property installed with `Object.defineProperty` onto an existing object. * @name spyOnProperty * @since 2.6.0 * @function * @global * @param {Object} obj - The object upon which to install the {@link Spy} * @param {String} propertyName - The name of the property to replace with a {@link Spy}. * @param {String} [accessType=get] - The access type (get|set) of the property to {@link Spy} on. * @returns {Spy} */ spyOnProperty: function(obj, methodName, accessType) { return env.spyOnProperty(obj, methodName, accessType); }, /** * Installs spies on all writable and configurable properties of an object. * @name spyOnAllFunctions * @since 3.2.1 * @function * @global * @param {Object} obj - The object upon which to install the {@link Spy}s * @param {boolean} includeNonEnumerable - Whether or not to add spies to non-enumerable properties * @returns {Object} the spied object */ spyOnAllFunctions: function(obj, includeNonEnumerable) { return env.spyOnAllFunctions(obj, includeNonEnumerable); }, jsApiReporter: new jasmine.JsApiReporter({ timer: new jasmine.Timer() }), /** * @namespace jasmine */ jasmine: jasmine }; /** * Add a custom equality tester for the current scope of specs. * * _Note:_ This is only callable from within a {@link beforeEach}, {@link it}, or {@link beforeAll}. * @name jasmine.addCustomEqualityTester * @since 2.0.0 * @function * @param {Function} tester - A function which takes two arguments to compare and returns a `true` or `false` comparison result if it knows how to compare them, and `undefined` otherwise. * @see custom_equality */ jasmine.addCustomEqualityTester = function(tester) { env.addCustomEqualityTester(tester); }; /** * Add custom matchers for the current scope of specs. * * _Note:_ This is only callable from within a {@link beforeEach}, {@link it}, or {@link beforeAll}. * @name jasmine.addMatchers * @since 2.0.0 * @function * @param {Object} matchers - Keys from this object will be the new matcher names. * @see custom_matcher */ jasmine.addMatchers = function(matchers) { return env.addMatchers(matchers); }; /** * Add custom async matchers for the current scope of specs. * * _Note:_ This is only callable from within a {@link beforeEach}, {@link it}, or {@link beforeAll}. * @name jasmine.addAsyncMatchers * @since 3.5.0 * @function * @param {Object} matchers - Keys from this object will be the new async matcher names. * @see custom_matcher */ jasmine.addAsyncMatchers = function(matchers) { return env.addAsyncMatchers(matchers); }; /** * Add a custom object formatter for the current scope of specs. * * _Note:_ This is only callable from within a {@link beforeEach}, {@link it}, or {@link beforeAll}. * @name jasmine.addCustomObjectFormatter * @since 3.6.0 * @function * @param {Function} formatter - A function which takes a value to format and returns a string if it knows how to format it, and `undefined` otherwise. * @see custom_object_formatters */ jasmine.addCustomObjectFormatter = function(formatter) { return env.addCustomObjectFormatter(formatter); }; /** * Get the currently booted mock {Clock} for this Jasmine environment. * @name jasmine.clock * @since 2.0.0 * @function * @returns {Clock} */ jasmine.clock = function() { return env.clock; }; /** * Create a bare {@link Spy} object. This won't be installed anywhere and will not have any implementation behind it. * @name jasmine.createSpy * @since 1.3.0 * @function * @param {String} [name] - Name to give the spy. This will be displayed in failure messages. * @param {Function} [originalFn] - Function to act as the real implementation. * @return {Spy} */ jasmine.createSpy = function(name, originalFn) { return env.createSpy(name, originalFn); }; /** * Create an object with multiple {@link Spy}s as its members. * @name jasmine.createSpyObj * @since 1.3.0 * @function * @param {String} [baseName] - Base name for the spies in the object. * @param {String[]|Object} methodNames - Array of method names to create spies for, or Object whose keys will be method names and values the {@link Spy#and#returnValue|returnValue}. * @param {String[]|Object} [propertyNames] - Array of property names to create spies for, or Object whose keys will be propertynames and values the {@link Spy#and#returnValue|returnValue}. * @return {Object} */ jasmine.createSpyObj = function(baseName, methodNames, propertyNames) { return env.createSpyObj(baseName, methodNames, propertyNames); }; /** * Add a custom spy strategy for the current scope of specs. * * _Note:_ This is only callable from within a {@link beforeEach}, {@link it}, or {@link beforeAll}. * @name jasmine.addSpyStrategy * @since 3.5.0 * @function * @param {String} name - The name of the strategy (i.e. what you call from `and`) * @param {Function} factory - Factory function that returns the plan to be executed. */ jasmine.addSpyStrategy = function(name, factory) { return env.addSpyStrategy(name, factory); }; /** * Set the default spy strategy for the current scope of specs. * * _Note:_ This is only callable from within a {@link beforeEach}, {@link it}, or {@link beforeAll}. * @name jasmine.setDefaultSpyStrategy * @function * @param {Function} defaultStrategyFn - a function that assigns a strategy * @example * beforeEach(function() { * jasmine.setDefaultSpyStrategy(and => and.returnValue(true)); * }); */ jasmine.setDefaultSpyStrategy = function(defaultStrategyFn) { return env.setDefaultSpyStrategy(defaultStrategyFn); }; return jasmineInterface; }; getJasmineRequireObj().RunableResources = function(j$) { class RunableResources { constructor(options) { this.byRunableId_ = {}; this.getCurrentRunableId_ = options.getCurrentRunableId; this.globalErrors_ = options.globalErrors; this.spyFactory = new j$.SpyFactory( () => { if (this.getCurrentRunableId_()) { return this.customSpyStrategies(); } else { return {}; } }, () => this.defaultSpyStrategy(), () => this.makeMatchersUtil() ); this.spyRegistry = new j$.SpyRegistry({ currentSpies: () => this.spies(), createSpy: (name, originalFn) => this.spyFactory.createSpy(name, originalFn) }); } initForRunable(runableId, parentId) { const newRes = (this.byRunableId_[runableId] = { customEqualityTesters: [], customMatchers: {}, customAsyncMatchers: {}, customSpyStrategies: {}, customObjectFormatters: [], defaultSpyStrategy: undefined, spies: [] }); const parentRes = this.byRunableId_[parentId]; if (parentRes) { newRes.defaultSpyStrategy = parentRes.defaultSpyStrategy; const toClone = [ 'customEqualityTesters', 'customMatchers', 'customAsyncMatchers', 'customObjectFormatters', 'customSpyStrategies' ]; for (const k of toClone) { newRes[k] = j$.util.clone(parentRes[k]); } } } clearForRunable(runableId) { this.globalErrors_.removeOverrideListener(); this.spyRegistry.clearSpies(); delete this.byRunableId_[runableId]; } spies() { return this.forCurrentRunable_( 'Spies must be created in a before function or a spec' ).spies; } defaultSpyStrategy() { if (!this.getCurrentRunableId_()) { return undefined; } return this.byRunableId_[this.getCurrentRunableId_()].defaultSpyStrategy; } setDefaultSpyStrategy(fn) { this.forCurrentRunable_( 'Default spy strategy must be set in a before function or a spec' ).defaultSpyStrategy = fn; } customSpyStrategies() { return this.forCurrentRunable_( 'Custom spy strategies must be added in a before function or a spec' ).customSpyStrategies; } customEqualityTesters() { return this.forCurrentRunable_( 'Custom Equalities must be added in a before function or a spec' ).customEqualityTesters; } customMatchers() { return this.forCurrentRunable_( 'Matchers must be added in a before function or a spec' ).customMatchers; } addCustomMatchers(matchersToAdd) { const matchers = this.customMatchers(); for (const name in matchersToAdd) { matchers[name] = matchersToAdd[name]; } } customAsyncMatchers() { return this.forCurrentRunable_( 'Async Matchers must be added in a before function or a spec' ).customAsyncMatchers; } addCustomAsyncMatchers(matchersToAdd) { const matchers = this.customAsyncMatchers(); for (const name in matchersToAdd) { matchers[name] = matchersToAdd[name]; } } customObjectFormatters() { return this.forCurrentRunable_( 'Custom object formatters must be added in a before function or a spec' ).customObjectFormatters; } makePrettyPrinter() { return j$.makePrettyPrinter(this.customObjectFormatters()); } makeMatchersUtil() { if (this.getCurrentRunableId_()) { return new j$.MatchersUtil({ customTesters: this.customEqualityTesters(), pp: this.makePrettyPrinter() }); } else { return new j$.MatchersUtil({ pp: j$.basicPrettyPrinter_ }); } } forCurrentRunable_(errorMsg) { const resources = this.byRunableId_[this.getCurrentRunableId_()]; if (!resources && errorMsg) { throw new Error(errorMsg); } return resources; } } return RunableResources; }; getJasmineRequireObj().Runner = function(j$) { class Runner { constructor(options) { this.topSuite_ = options.topSuite; this.totalSpecsDefined_ = options.totalSpecsDefined; this.focusedRunables_ = options.focusedRunables; this.runableResources_ = options.runableResources; this.queueRunnerFactory_ = options.queueRunnerFactory; this.reporter_ = options.reporter; this.getConfig_ = options.getConfig; this.reportSpecDone_ = options.reportSpecDone; this.hasFailures = false; this.executedBefore_ = false; this.currentlyExecutingSuites_ = []; this.currentSpec = null; } currentRunable() { return this.currentSpec || this.currentSuite(); } currentSuite() { return this.currentlyExecutingSuites_[ this.currentlyExecutingSuites_.length - 1 ]; } // Although execute returns a promise, it isn't async for backwards // compatibility: The "Invalid order" exception needs to be propagated // synchronously from Env#execute. // TODO: make this and Env#execute async in the next major release execute(runablesToRun) { if (this.executedBefore_) { this.topSuite_.reset(); } this.executedBefore_ = true; this.hasFailures = false; const focusedRunables = this.focusedRunables_(); const config = this.getConfig_(); if (!runablesToRun) { if (focusedRunables.length) { runablesToRun = focusedRunables; } else { runablesToRun = [this.topSuite_.id]; } } const order = new j$.Order({ random: config.random, seed: j$.isNumber_(config.seed) ? config.seed + '' : config.seed }); const processor = new j$.TreeProcessor({ tree: this.topSuite_, runnableIds: runablesToRun, queueRunnerFactory: options => { if (options.isLeaf) { // A spec options.SkipPolicy = j$.CompleteOnFirstErrorSkipPolicy; } else { // A suite if (config.stopOnSpecFailure) { options.SkipPolicy = j$.CompleteOnFirstErrorSkipPolicy; } else { options.SkipPolicy = j$.SkipAfterBeforeAllErrorPolicy; } } return this.queueRunnerFactory_(options); }, failSpecWithNoExpectations: config.failSpecWithNoExpectations, nodeStart: (suite, next) => { this.currentlyExecutingSuites_.push(suite); this.runableResources_.initForRunable(suite.id, suite.parentSuite.id); this.reporter_.suiteStarted(suite.result).then(next); suite.startTimer(); }, nodeComplete: (suite, result, next) => { if (suite !== this.currentSuite()) { throw new Error('Tried to complete the wrong suite'); } this.runableResources_.clearForRunable(suite.id); this.currentlyExecutingSuites_.pop(); if (result.status === 'failed') { this.hasFailures = true; } suite.endTimer(); if (suite.hadBeforeAllFailure) { this.reportChildrenOfBeforeAllFailure_(suite).then(() => { this.reportSuiteDone_(suite, result, next); }); } else { this.reportSuiteDone_(suite, result, next); } }, orderChildren: function(node) { return order.sort(node.children); }, excludeNode: function(spec) { return !config.specFilter(spec); } }); if (!processor.processTree().valid) { throw new Error( 'Invalid order: would cause a beforeAll or afterAll to be run multiple times' ); } return this.execute2_(runablesToRun, order, processor); } async execute2_(runablesToRun, order, processor) { const totalSpecsDefined = this.totalSpecsDefined_(); this.runableResources_.initForRunable(this.topSuite_.id); const jasmineTimer = new j$.Timer(); jasmineTimer.start(); /** * Information passed to the {@link Reporter#jasmineStarted} event. * @typedef JasmineStartedInfo * @property {Int} totalSpecsDefined - The total number of specs defined in this suite. * @property {Order} order - Information about the ordering (random or not) of this execution of the suite. * @since 2.0.0 */ await this.reporter_.jasmineStarted({ totalSpecsDefined, order: order }); this.currentlyExecutingSuites_.push(this.topSuite_); await processor.execute(); if (this.topSuite_.hadBeforeAllFailure) { await this.reportChildrenOfBeforeAllFailure_(this.topSuite_); } this.runableResources_.clearForRunable(this.topSuite_.id); this.currentlyExecutingSuites_.pop(); let overallStatus, incompleteReason; if ( this.hasFailures || this.topSuite_.result.failedExpectations.length > 0 ) { overallStatus = 'failed'; } else if (this.focusedRunables_().length > 0) { overallStatus = 'incomplete'; incompleteReason = 'fit() or fdescribe() was found'; } else if (totalSpecsDefined === 0) { overallStatus = 'incomplete'; incompleteReason = 'No specs found'; } else { overallStatus = 'passed'; } /** * Information passed to the {@link Reporter#jasmineDone} event. * @typedef JasmineDoneInfo * @property {OverallStatus} overallStatus - The overall result of the suite: 'passed', 'failed', or 'incomplete'. * @property {Int} totalTime - The total time (in ms) that it took to execute the suite * @property {IncompleteReason} incompleteReason - Explanation of why the suite was incomplete. * @property {Order} order - Information about the ordering (random or not) of this execution of the suite. * @property {Expectation[]} failedExpectations - List of expectations that failed in an {@link afterAll} at the global level. * @property {Expectation[]} deprecationWarnings - List of deprecation warnings that occurred at the global level. * @since 2.4.0 */ const jasmineDoneInfo = { overallStatus: overallStatus, totalTime: jasmineTimer.elapsed(), incompleteReason: incompleteReason, order: order, failedExpectations: this.topSuite_.result.failedExpectations, deprecationWarnings: this.topSuite_.result.deprecationWarnings }; this.topSuite_.reportedDone = true; await this.reporter_.jasmineDone(jasmineDoneInfo); return jasmineDoneInfo; } reportSuiteDone_(suite, result, next) { suite.reportedDone = true; this.reporter_.suiteDone(result).then(next); } async reportChildrenOfBeforeAllFailure_(suite) { for (const child of suite.children) { if (child instanceof j$.Suite) { await this.reporter_.suiteStarted(child.result); await this.reportChildrenOfBeforeAllFailure_(child); // Marking the suite passed is consistent with how suites that // contain failed specs but no suite-level failures are reported. child.result.status = 'passed'; await this.reporter_.suiteDone(child.result); } else { /* a spec */ await this.reporter_.specStarted(child.result); child.addExpectationResult( false, { passed: false, message: 'Not run because a beforeAll function failed. The ' + 'beforeAll failure will be reported on the suite that ' + 'caused it.' }, true ); child.result.status = 'failed'; await new Promise(resolve => { this.reportSpecDone_(child, child.result, resolve); }); } } } } return Runner; }; getJasmineRequireObj().SkipAfterBeforeAllErrorPolicy = function(j$) { function SkipAfterBeforeAllErrorPolicy(queueableFns) { this.queueableFns_ = queueableFns; this.skipping_ = false; } SkipAfterBeforeAllErrorPolicy.prototype.skipTo = function(lastRanFnIx) { if (this.skipping_) { return this.nextAfterAllAfter_(lastRanFnIx); } else { return lastRanFnIx + 1; } }; SkipAfterBeforeAllErrorPolicy.prototype.nextAfterAllAfter_ = function(i) { for ( i++; i < this.queueableFns_.length && this.queueableFns_[i].type !== 'afterAll'; i++ ) {} return i; }; SkipAfterBeforeAllErrorPolicy.prototype.fnErrored = function(fnIx) { if (this.queueableFns_[fnIx].type === 'beforeAll') { this.skipping_ = true; // Failures need to be reported for each contained spec. But we can't do // that from here because reporting is async. This function isn't async // (and can't be without greatly complicating QueueRunner). Mark the // failure so that the code that reports the suite result (which is // already async) can detect the failure and report the specs. this.queueableFns_[fnIx].suite.hadBeforeAllFailure = true; } }; return SkipAfterBeforeAllErrorPolicy; }; getJasmineRequireObj().Spy = function(j$) { const nextOrder = (function() { let order = 0; return function() { return order++; }; })(); /** * @classdesc _Note:_ Do not construct this directly. Use {@link spyOn}, * {@link spyOnProperty}, {@link jasmine.createSpy}, or * {@link jasmine.createSpyObj} instead. * @class Spy * @hideconstructor */ function Spy(name, matchersUtil, optionals) { const spy = function(context, args, invokeNew) { /** * @name Spy.callData * @property {object} object - `this` context for the invocation. * @property {number} invocationOrder - Order of the invocation. * @property {Array} args - The arguments passed for this invocation. * @property returnValue - The value that was returned from this invocation. */ const callData = { object: context, invocationOrder: nextOrder(), args: Array.prototype.slice.apply(args) }; callTracker.track(callData); const returnValue = strategyDispatcher.exec(context, args, invokeNew); callData.returnValue = returnValue; return returnValue; }; const { originalFn, customStrategies, defaultStrategyFn } = optionals || {}; const numArgs = typeof originalFn === 'function' ? originalFn.length : 0, wrapper = makeFunc(numArgs, function(context, args, invokeNew) { return spy(context, args, invokeNew); }), strategyDispatcher = new SpyStrategyDispatcher( { name: name, fn: originalFn, getSpy: function() { return wrapper; }, customStrategies: customStrategies }, matchersUtil ), callTracker = new j$.CallTracker(); function makeFunc(length, fn) { switch (length) { case 1: return function wrap1(a) { return fn(this, arguments, this instanceof wrap1); }; case 2: return function wrap2(a, b) { return fn(this, arguments, this instanceof wrap2); }; case 3: return function wrap3(a, b, c) { return fn(this, arguments, this instanceof wrap3); }; case 4: return function wrap4(a, b, c, d) { return fn(this, arguments, this instanceof wrap4); }; case 5: return function wrap5(a, b, c, d, e) { return fn(this, arguments, this instanceof wrap5); }; case 6: return function wrap6(a, b, c, d, e, f) { return fn(this, arguments, this instanceof wrap6); }; case 7: return function wrap7(a, b, c, d, e, f, g) { return fn(this, arguments, this instanceof wrap7); }; case 8: return function wrap8(a, b, c, d, e, f, g, h) { return fn(this, arguments, this instanceof wrap8); }; case 9: return function wrap9(a, b, c, d, e, f, g, h, i) { return fn(this, arguments, this instanceof wrap9); }; default: return function wrap() { return fn(this, arguments, this instanceof wrap); }; } } for (const prop in originalFn) { if (prop === 'and' || prop === 'calls') { throw new Error( "Jasmine spies would overwrite the 'and' and 'calls' properties on the object being spied upon" ); } wrapper[prop] = originalFn[prop]; } /** * @member {SpyStrategy} - Accesses the default strategy for the spy. This strategy will be used * whenever the spy is called with arguments that don't match any strategy * created with {@link Spy#withArgs}. * @name Spy#and * @since 2.0.0 * @example * spyOn(someObj, 'func').and.returnValue(42); */ wrapper.and = strategyDispatcher.and; /** * Specifies a strategy to be used for calls to the spy that have the * specified arguments. * @name Spy#withArgs * @since 3.0.0 * @function * @param {...*} args - The arguments to match * @type {SpyStrategy} * @example * spyOn(someObj, 'func').withArgs(1, 2, 3).and.returnValue(42); * someObj.func(1, 2, 3); // returns 42 */ wrapper.withArgs = function() { return strategyDispatcher.withArgs.apply(strategyDispatcher, arguments); }; wrapper.calls = callTracker; if (defaultStrategyFn) { defaultStrategyFn(wrapper.and); } return wrapper; } function SpyStrategyDispatcher(strategyArgs, matchersUtil) { const baseStrategy = new j$.SpyStrategy(strategyArgs); const argsStrategies = new StrategyDict(function() { return new j$.SpyStrategy(strategyArgs); }, matchersUtil); this.and = baseStrategy; this.exec = function(spy, args, invokeNew) { let strategy = argsStrategies.get(args); if (!strategy) { if (argsStrategies.any() && !baseStrategy.isConfigured()) { throw new Error( "Spy '" + strategyArgs.name + "' received a call with arguments " + j$.basicPrettyPrinter_(Array.prototype.slice.call(args)) + ' but all configured strategies specify other arguments.' ); } else { strategy = baseStrategy; } } return strategy.exec(spy, args, invokeNew); }; this.withArgs = function() { return { and: argsStrategies.getOrCreate(arguments) }; }; } function StrategyDict(strategyFactory, matchersUtil) { this.strategies = []; this.strategyFactory = strategyFactory; this.matchersUtil = matchersUtil; } StrategyDict.prototype.any = function() { return this.strategies.length > 0; }; StrategyDict.prototype.getOrCreate = function(args) { let strategy = this.get(args); if (!strategy) { strategy = this.strategyFactory(); this.strategies.push({ args: args, strategy: strategy }); } return strategy; }; StrategyDict.prototype.get = function(args) { for (let i = 0; i < this.strategies.length; i++) { if (this.matchersUtil.equals(args, this.strategies[i].args)) { return this.strategies[i].strategy; } } }; return Spy; }; getJasmineRequireObj().SpyFactory = function(j$) { function SpyFactory( getCustomStrategies, getDefaultStrategyFn, getMatchersUtil ) { this.createSpy = function(name, originalFn) { if (j$.isFunction_(name) && originalFn === undefined) { originalFn = name; name = originalFn.name; } return j$.Spy(name, getMatchersUtil(), { originalFn, customStrategies: getCustomStrategies(), defaultStrategyFn: getDefaultStrategyFn() }); }; this.createSpyObj = function(baseName, methodNames, propertyNames) { const baseNameIsCollection = j$.isObject_(baseName) || j$.isArray_(baseName); if (baseNameIsCollection) { propertyNames = methodNames; methodNames = baseName; baseName = 'unknown'; } const obj = {}; const methods = normalizeKeyValues(methodNames); for (let i = 0; i < methods.length; i++) { const spy = (obj[methods[i][0]] = this.createSpy( baseName + '.' + methods[i][0] )); if (methods[i].length > 1) { spy.and.returnValue(methods[i][1]); } } const properties = normalizeKeyValues(propertyNames); for (let i = 0; i < properties.length; i++) { const descriptor = { enumerable: true, get: this.createSpy(baseName + '.' + properties[i][0] + '.get'), set: this.createSpy(baseName + '.' + properties[i][0] + '.set') }; if (properties[i].length > 1) { descriptor.get.and.returnValue(properties[i][1]); descriptor.set.and.returnValue(properties[i][1]); } Object.defineProperty(obj, properties[i][0], descriptor); } if (methods.length === 0 && properties.length === 0) { throw 'createSpyObj requires a non-empty array or object of method names to create spies for'; } return obj; }; } function normalizeKeyValues(object) { const result = []; if (j$.isArray_(object)) { for (let i = 0; i < object.length; i++) { result.push([object[i]]); } } else if (j$.isObject_(object)) { for (const key in object) { if (object.hasOwnProperty(key)) { result.push([key, object[key]]); } } } return result; } return SpyFactory; }; getJasmineRequireObj().SpyRegistry = function(j$) { const spyOnMsg = j$.formatErrorMsg( '', 'spyOn(, )' ); const spyOnPropertyMsg = j$.formatErrorMsg( '', 'spyOnProperty(, , [accessType])' ); function SpyRegistry(options) { options = options || {}; const global = options.global || j$.getGlobal(); const createSpy = options.createSpy; const currentSpies = options.currentSpies || function() { return []; }; this.allowRespy = function(allow) { this.respy = allow; }; this.spyOn = function(obj, methodName) { const getErrorMsg = spyOnMsg; if (j$.util.isUndefined(obj) || obj === null) { throw new Error( getErrorMsg( 'could not find an object to spy upon for ' + methodName + '()' ) ); } if (j$.util.isUndefined(methodName) || methodName === null) { throw new Error(getErrorMsg('No method name supplied')); } if (j$.util.isUndefined(obj[methodName])) { throw new Error(getErrorMsg(methodName + '() method does not exist')); } if (obj[methodName] && j$.isSpy(obj[methodName])) { if (this.respy) { return obj[methodName]; } else { throw new Error( getErrorMsg(methodName + ' has already been spied upon') ); } } const descriptor = Object.getOwnPropertyDescriptor(obj, methodName); if (descriptor && !(descriptor.writable || descriptor.set)) { throw new Error( getErrorMsg(methodName + ' is not declared writable or has no setter') ); } const originalMethod = obj[methodName]; const spiedMethod = createSpy(methodName, originalMethod); let restoreStrategy; if ( Object.prototype.hasOwnProperty.call(obj, methodName) || (obj === global && methodName === 'onerror') ) { restoreStrategy = function() { obj[methodName] = originalMethod; }; } else { restoreStrategy = function() { if (!delete obj[methodName]) { obj[methodName] = originalMethod; } }; } currentSpies().push({ restoreObjectToOriginalState: restoreStrategy }); obj[methodName] = spiedMethod; return spiedMethod; }; this.spyOnProperty = function(obj, propertyName, accessType) { const getErrorMsg = spyOnPropertyMsg; accessType = accessType || 'get'; if (j$.util.isUndefined(obj)) { throw new Error( getErrorMsg( 'spyOn could not find an object to spy upon for ' + propertyName + '' ) ); } if (j$.util.isUndefined(propertyName)) { throw new Error(getErrorMsg('No property name supplied')); } const descriptor = j$.util.getPropertyDescriptor(obj, propertyName); if (!descriptor) { throw new Error(getErrorMsg(propertyName + ' property does not exist')); } if (!descriptor.configurable) { throw new Error( getErrorMsg(propertyName + ' is not declared configurable') ); } if (!descriptor[accessType]) { throw new Error( getErrorMsg( 'Property ' + propertyName + ' does not have access type ' + accessType ) ); } if (j$.isSpy(descriptor[accessType])) { if (this.respy) { return descriptor[accessType]; } else { throw new Error( getErrorMsg( propertyName + '#' + accessType + ' has already been spied upon' ) ); } } const originalDescriptor = j$.util.clone(descriptor); const spy = createSpy(propertyName, descriptor[accessType]); let restoreStrategy; if (Object.prototype.hasOwnProperty.call(obj, propertyName)) { restoreStrategy = function() { Object.defineProperty(obj, propertyName, originalDescriptor); }; } else { restoreStrategy = function() { delete obj[propertyName]; }; } currentSpies().push({ restoreObjectToOriginalState: restoreStrategy }); descriptor[accessType] = spy; Object.defineProperty(obj, propertyName, descriptor); return spy; }; this.spyOnAllFunctions = function(obj, includeNonEnumerable) { if (j$.util.isUndefined(obj)) { throw new Error( 'spyOnAllFunctions could not find an object to spy upon' ); } let pointer = obj, propsToSpyOn = [], properties, propertiesToSkip = []; while ( pointer && (!includeNonEnumerable || pointer !== Object.prototype) ) { properties = getProps(pointer, includeNonEnumerable); properties = properties.filter(function(prop) { return propertiesToSkip.indexOf(prop) === -1; }); propertiesToSkip = propertiesToSkip.concat(properties); propsToSpyOn = propsToSpyOn.concat( getSpyableFunctionProps(pointer, properties) ); pointer = Object.getPrototypeOf(pointer); } for (const prop of propsToSpyOn) { this.spyOn(obj, prop); } return obj; }; this.clearSpies = function() { const spies = currentSpies(); for (let i = spies.length - 1; i >= 0; i--) { const spyEntry = spies[i]; spyEntry.restoreObjectToOriginalState(); } }; } function getProps(obj, includeNonEnumerable) { const enumerableProperties = Object.keys(obj); if (!includeNonEnumerable) { return enumerableProperties; } return Object.getOwnPropertyNames(obj).filter(function(prop) { return ( prop !== 'constructor' || enumerableProperties.indexOf('constructor') > -1 ); }); } function getSpyableFunctionProps(obj, propertiesToCheck) { const props = []; for (const prop of propertiesToCheck) { if ( Object.prototype.hasOwnProperty.call(obj, prop) && isSpyableProp(obj, prop) ) { props.push(prop); } } return props; } function isSpyableProp(obj, prop) { let value; try { value = obj[prop]; } catch (e) { return false; } if (value instanceof Function) { const descriptor = Object.getOwnPropertyDescriptor(obj, prop); return (descriptor.writable || descriptor.set) && descriptor.configurable; } return false; } return SpyRegistry; }; getJasmineRequireObj().SpyStrategy = function(j$) { /** * @interface SpyStrategy */ function SpyStrategy(options) { options = options || {}; /** * Get the identifying information for the spy. * @name SpyStrategy#identity * @since 3.0.0 * @member * @type {String} */ this.identity = options.name || 'unknown'; this.originalFn = options.fn || function() {}; this.getSpy = options.getSpy || function() {}; this.plan = this._defaultPlan = function() {}; const cs = options.customStrategies || {}; for (const k in cs) { if (j$.util.has(cs, k) && !this[k]) { this[k] = createCustomPlan(cs[k]); } } /** * Tell the spy to return a promise resolving to the specified value when invoked. * @name SpyStrategy#resolveTo * @since 3.5.0 * @function * @param {*} value The value to return. */ this.resolveTo = function(value) { this.plan = function() { return Promise.resolve(value); }; return this.getSpy(); }; /** * Tell the spy to return a promise rejecting with the specified value when invoked. * @name SpyStrategy#rejectWith * @since 3.5.0 * @function * @param {*} value The value to return. */ this.rejectWith = function(value) { this.plan = function() { return Promise.reject(value); }; return this.getSpy(); }; } function createCustomPlan(factory) { return function() { const plan = factory.apply(null, arguments); if (!j$.isFunction_(plan)) { throw new Error('Spy strategy must return a function'); } this.plan = plan; return this.getSpy(); }; } /** * Execute the current spy strategy. * @name SpyStrategy#exec * @since 2.0.0 * @function */ SpyStrategy.prototype.exec = function(context, args, invokeNew) { const contextArgs = [context].concat( args ? Array.prototype.slice.call(args) : [] ); const target = this.plan.bind.apply(this.plan, contextArgs); return invokeNew ? new target() : target(); }; /** * Tell the spy to call through to the real implementation when invoked. * @name SpyStrategy#callThrough * @since 2.0.0 * @function */ SpyStrategy.prototype.callThrough = function() { this.plan = this.originalFn; return this.getSpy(); }; /** * Tell the spy to return the value when invoked. * @name SpyStrategy#returnValue * @since 2.0.0 * @function * @param {*} value The value to return. */ SpyStrategy.prototype.returnValue = function(value) { this.plan = function() { return value; }; return this.getSpy(); }; /** * Tell the spy to return one of the specified values (sequentially) each time the spy is invoked. * @name SpyStrategy#returnValues * @since 2.1.0 * @function * @param {...*} values - Values to be returned on subsequent calls to the spy. */ SpyStrategy.prototype.returnValues = function() { const values = Array.prototype.slice.call(arguments); this.plan = function() { return values.shift(); }; return this.getSpy(); }; /** * Tell the spy to throw an error when invoked. * @name SpyStrategy#throwError * @since 2.0.0 * @function * @param {Error|Object|String} something Thing to throw */ SpyStrategy.prototype.throwError = function(something) { const error = j$.isString_(something) ? new Error(something) : something; this.plan = function() { throw error; }; return this.getSpy(); }; /** * Tell the spy to call a fake implementation when invoked. * @name SpyStrategy#callFake * @since 2.0.0 * @function * @param {Function} fn The function to invoke with the passed parameters. */ SpyStrategy.prototype.callFake = function(fn) { if ( !( j$.isFunction_(fn) || j$.isAsyncFunction_(fn) || j$.isGeneratorFunction_(fn) ) ) { throw new Error( 'Argument passed to callFake should be a function, got ' + fn ); } this.plan = fn; return this.getSpy(); }; /** * Tell the spy to do nothing when invoked. This is the default. * @name SpyStrategy#stub * @since 2.0.0 * @function */ SpyStrategy.prototype.stub = function(fn) { this.plan = function() {}; return this.getSpy(); }; SpyStrategy.prototype.isConfigured = function() { return this.plan !== this._defaultPlan; }; return SpyStrategy; }; getJasmineRequireObj().StackTrace = function(j$) { function StackTrace(error) { let lines = error.stack.split('\n').filter(function(line) { return line !== ''; }); const extractResult = extractMessage(error.message, lines); if (extractResult) { this.message = extractResult.message; lines = extractResult.remainder; } const parseResult = tryParseFrames(lines); this.frames = parseResult.frames; this.style = parseResult.style; } const framePatterns = [ // Node, Chrome, Edge // e.g. " at QueueRunner.run (http://localhost:8888/__jasmine__/jasmine.js:4320:20)" // Note that the "function name" can include a surprisingly large set of // characters, including angle brackets and square brackets. { re: /^\s*at ([^\)]+) \(([^\)]+)\)$/, fnIx: 1, fileLineColIx: 2, style: 'v8' }, // NodeJS alternate form, often mixed in with the Chrome style // e.g. " at /some/path:4320:20 { re: /\s*at (.+)$/, fileLineColIx: 1, style: 'v8' }, // PhantomJS on OS X, Safari, Firefox // e.g. "run@http://localhost:8888/__jasmine__/jasmine.js:4320:27" // or "http://localhost:8888/__jasmine__/jasmine.js:4320:27" { re: /^(?:(([^@\s]+)@)|@)?([^\s]+)$/, fnIx: 2, fileLineColIx: 3, style: 'webkit' } ]; // regexes should capture the function name (if any) as group 1 // and the file, line, and column as group 2. function tryParseFrames(lines) { let style = null; const frames = lines.map(function(line) { const convertedLine = first(framePatterns, function(pattern) { const overallMatch = line.match(pattern.re); if (!overallMatch) { return null; } const fileLineColMatch = overallMatch[pattern.fileLineColIx].match( /^(.*):(\d+):\d+$/ ); if (!fileLineColMatch) { return null; } style = style || pattern.style; return { raw: line, file: fileLineColMatch[1], line: parseInt(fileLineColMatch[2], 10), func: overallMatch[pattern.fnIx] }; }); return convertedLine || { raw: line }; }); return { style: style, frames: frames }; } function first(items, fn) { for (const item of items) { const result = fn(item); if (result) { return result; } } } function extractMessage(message, stackLines) { const len = messagePrefixLength(message, stackLines); if (len > 0) { return { message: stackLines.slice(0, len).join('\n'), remainder: stackLines.slice(len) }; } } function messagePrefixLength(message, stackLines) { if (!stackLines[0].match(/^\w*Error/)) { return 0; } const messageLines = message.split('\n'); for (let i = 1; i < messageLines.length; i++) { if (messageLines[i] !== stackLines[i]) { return 0; } } return messageLines.length; } return StackTrace; }; getJasmineRequireObj().Suite = function(j$) { function Suite(attrs) { this.env = attrs.env; this.id = attrs.id; this.parentSuite = attrs.parentSuite; this.description = attrs.description; this.expectationFactory = attrs.expectationFactory; this.asyncExpectationFactory = attrs.asyncExpectationFactory; this.throwOnExpectationFailure = !!attrs.throwOnExpectationFailure; this.autoCleanClosures = attrs.autoCleanClosures === undefined ? true : !!attrs.autoCleanClosures; this.onLateError = attrs.onLateError || function() {}; this.beforeFns = []; this.afterFns = []; this.beforeAllFns = []; this.afterAllFns = []; this.timer = attrs.timer || new j$.Timer(); this.children = []; this.reset(); } Suite.prototype.setSuiteProperty = function(key, value) { this.result.properties = this.result.properties || {}; this.result.properties[key] = value; }; Suite.prototype.expect = function(actual) { return this.expectationFactory(actual, this); }; Suite.prototype.expectAsync = function(actual) { return this.asyncExpectationFactory(actual, this); }; Suite.prototype.getFullName = function() { const fullName = []; for ( let parentSuite = this; parentSuite; parentSuite = parentSuite.parentSuite ) { if (parentSuite.parentSuite) { fullName.unshift(parentSuite.description); } } return fullName.join(' '); }; /* * Mark the suite with "pending" status */ Suite.prototype.pend = function() { this.markedPending = true; }; /* * Like {@link Suite#pend}, but pending state will survive {@link Spec#reset} * Useful for fdescribe, xdescribe, where pending state should remain. */ Suite.prototype.exclude = function() { this.pend(); this.markedExcluding = true; }; Suite.prototype.beforeEach = function(fn) { this.beforeFns.unshift({ ...fn, suite: this }); }; Suite.prototype.beforeAll = function(fn) { this.beforeAllFns.push({ ...fn, type: 'beforeAll', suite: this }); }; Suite.prototype.afterEach = function(fn) { this.afterFns.unshift({ ...fn, suite: this, type: 'afterEach' }); }; Suite.prototype.afterAll = function(fn) { this.afterAllFns.unshift({ ...fn, type: 'afterAll' }); }; Suite.prototype.startTimer = function() { this.timer.start(); }; Suite.prototype.endTimer = function() { this.result.duration = this.timer.elapsed(); }; function removeFns(queueableFns) { for (const qf of queueableFns) { qf.fn = null; } } Suite.prototype.cleanupBeforeAfter = function() { if (this.autoCleanClosures) { removeFns(this.beforeAllFns); removeFns(this.afterAllFns); removeFns(this.beforeFns); removeFns(this.afterFns); } }; Suite.prototype.reset = function() { /** * @typedef SuiteResult * @property {String} id - The unique id of this suite. * @property {String} description - The description text passed to the {@link describe} that made this suite. * @property {String} fullName - The full description including all ancestors of this suite. * @property {Expectation[]} failedExpectations - The list of expectations that failed in an {@link afterAll} for this suite. * @property {Expectation[]} deprecationWarnings - The list of deprecation warnings that occurred on this suite. * @property {String} status - Once the suite has completed, this string represents the pass/fail status of this suite. * @property {number} duration - The time in ms for Suite execution, including any before/afterAll, before/afterEach. * @property {Object} properties - User-supplied properties, if any, that were set using {@link Env#setSuiteProperty} * @since 2.0.0 */ this.result = { id: this.id, description: this.description, fullName: this.getFullName(), failedExpectations: [], deprecationWarnings: [], duration: null, properties: null }; this.markedPending = this.markedExcluding; this.children.forEach(function(child) { child.reset(); }); this.reportedDone = false; }; Suite.prototype.addChild = function(child) { this.children.push(child); }; Suite.prototype.status = function() { if (this.markedPending) { return 'pending'; } if (this.result.failedExpectations.length > 0) { return 'failed'; } else { return 'passed'; } }; Suite.prototype.canBeReentered = function() { return this.beforeAllFns.length === 0 && this.afterAllFns.length === 0; }; Suite.prototype.getResult = function() { this.result.status = this.status(); return this.result; }; Suite.prototype.sharedUserContext = function() { if (!this.sharedContext) { this.sharedContext = this.parentSuite ? this.parentSuite.clonedSharedUserContext() : new j$.UserContext(); } return this.sharedContext; }; Suite.prototype.clonedSharedUserContext = function() { return j$.UserContext.fromExisting(this.sharedUserContext()); }; Suite.prototype.handleException = function() { if (arguments[0] instanceof j$.errors.ExpectationFailed) { return; } const data = { matcherName: '', passed: false, expected: '', actual: '', error: arguments[0] }; const failedExpectation = j$.buildExpectationResult(data); if (!this.parentSuite) { failedExpectation.globalErrorType = 'afterAll'; } if (this.reportedDone) { this.onLateError(failedExpectation); } else { this.result.failedExpectations.push(failedExpectation); } }; Suite.prototype.onMultipleDone = function() { let msg; // Issue a deprecation. Include the context ourselves and pass // ignoreRunnable: true, since getting here always means that we've already // moved on and the current runnable isn't the one that caused the problem. if (this.parentSuite) { msg = "An asynchronous beforeAll or afterAll function called its 'done' " + 'callback more than once.\n' + '(in suite: ' + this.getFullName() + ')'; } else { msg = 'A top-level beforeAll or afterAll function called its ' + "'done' callback more than once."; } this.onLateError(new Error(msg)); }; Suite.prototype.addExpectationResult = function() { if (isFailure(arguments)) { const data = arguments[1]; const expectationResult = j$.buildExpectationResult(data); if (this.reportedDone) { this.onLateError(expectationResult); } else { this.result.failedExpectations.push(expectationResult); // TODO: refactor so that we don't need to override cached status if (this.result.status) { this.result.status = 'failed'; } } if (this.throwOnExpectationFailure) { throw new j$.errors.ExpectationFailed(); } } }; Suite.prototype.addDeprecationWarning = function(deprecation) { if (typeof deprecation === 'string') { deprecation = { message: deprecation }; } this.result.deprecationWarnings.push( j$.buildExpectationResult(deprecation) ); }; Object.defineProperty(Suite.prototype, 'metadata', { get: function() { if (!this.metadata_) { this.metadata_ = new SuiteMetadata(this); } return this.metadata_; } }); /** * @interface Suite * @see Env#topSuite * @since 2.0.0 */ function SuiteMetadata(suite) { this.suite_ = suite; /** * The unique ID of this suite. * @name Suite#id * @readonly * @type {string} * @since 2.0.0 */ this.id = suite.id; /** * The parent of this suite, or null if this is the top suite. * @name Suite#parentSuite * @readonly * @type {Suite} */ this.parentSuite = suite.parentSuite ? suite.parentSuite.metadata : null; /** * The description passed to the {@link describe} that created this suite. * @name Suite#description * @readonly * @type {string} * @since 2.0.0 */ this.description = suite.description; } /** * The full description including all ancestors of this suite. * @name Suite#getFullName * @function * @returns {string} * @since 2.0.0 */ SuiteMetadata.prototype.getFullName = function() { return this.suite_.getFullName(); }; /** * The suite's children. * @name Suite#children * @type {Array.<(Spec|Suite)>} * @since 2.0.0 */ Object.defineProperty(SuiteMetadata.prototype, 'children', { get: function() { return this.suite_.children.map(child => child.metadata); } }); function isFailure(args) { return !args[0]; } return Suite; }; getJasmineRequireObj().SuiteBuilder = function(j$) { class SuiteBuilder { constructor(options) { this.env_ = options.env; this.expectationFactory_ = options.expectationFactory; this.suiteAsyncExpectationFactory_ = function(actual, suite) { return options.asyncExpectationFactory(actual, suite, 'Suite'); }; this.specAsyncExpectationFactory_ = function(actual, suite) { return options.asyncExpectationFactory(actual, suite, 'Spec'); }; this.onLateError_ = options.onLateError; this.specResultCallback_ = options.specResultCallback; this.specStarted_ = options.specStarted; this.nextSuiteId_ = 0; this.nextSpecId_ = 0; this.topSuite = this.suiteFactory_('Jasmine__TopLevel__Suite'); this.currentDeclarationSuite_ = this.topSuite; this.totalSpecsDefined = 0; this.focusedRunables = []; } describe(description, definitionFn) { ensureIsFunction(definitionFn, 'describe'); const suite = this.suiteFactory_(description); if (definitionFn.length > 0) { throw new Error('describe does not expect any arguments'); } if (this.currentDeclarationSuite_.markedExcluding) { suite.exclude(); } this.addSpecsToSuite_(suite, definitionFn); return suite; } fdescribe(description, definitionFn) { ensureIsFunction(definitionFn, 'fdescribe'); const suite = this.suiteFactory_(description); suite.isFocused = true; this.focusedRunables.push(suite.id); this.unfocusAncestor_(); this.addSpecsToSuite_(suite, definitionFn); return suite; } xdescribe(description, definitionFn) { ensureIsFunction(definitionFn, 'xdescribe'); const suite = this.suiteFactory_(description); suite.exclude(); this.addSpecsToSuite_(suite, definitionFn); return suite; } it(description, fn, timeout) { // it() sometimes doesn't have a fn argument, so only check the type if // it's given. if (arguments.length > 1 && typeof fn !== 'undefined') { ensureIsFunctionOrAsync(fn, 'it'); } return this.it_(description, fn, timeout); } xit(description, fn, timeout) { // xit(), like it(), doesn't always have a fn argument, so only check the // type when needed. if (arguments.length > 1 && typeof fn !== 'undefined') { ensureIsFunctionOrAsync(fn, 'xit'); } const spec = this.it_(description, fn, timeout); spec.exclude('Temporarily disabled with xit'); return spec; } fit(description, fn, timeout) { // Unlike it and xit, the function is required because it doesn't make // sense to focus on nothing. ensureIsFunctionOrAsync(fn, 'fit'); if (timeout) { j$.util.validateTimeout(timeout); } const spec = this.specFactory_(description, fn, timeout); this.currentDeclarationSuite_.addChild(spec); this.focusedRunables.push(spec.id); this.unfocusAncestor_(); return spec; } beforeEach(beforeEachFunction, timeout) { ensureIsFunctionOrAsync(beforeEachFunction, 'beforeEach'); if (timeout) { j$.util.validateTimeout(timeout); } this.currentDeclarationSuite_.beforeEach({ fn: beforeEachFunction, timeout: timeout || 0 }); } beforeAll(beforeAllFunction, timeout) { ensureIsFunctionOrAsync(beforeAllFunction, 'beforeAll'); if (timeout) { j$.util.validateTimeout(timeout); } this.currentDeclarationSuite_.beforeAll({ fn: beforeAllFunction, timeout: timeout || 0 }); } afterEach(afterEachFunction, timeout) { ensureIsFunctionOrAsync(afterEachFunction, 'afterEach'); if (timeout) { j$.util.validateTimeout(timeout); } afterEachFunction.isCleanup = true; this.currentDeclarationSuite_.afterEach({ fn: afterEachFunction, timeout: timeout || 0 }); } afterAll(afterAllFunction, timeout) { ensureIsFunctionOrAsync(afterAllFunction, 'afterAll'); if (timeout) { j$.util.validateTimeout(timeout); } this.currentDeclarationSuite_.afterAll({ fn: afterAllFunction, timeout: timeout || 0 }); } it_(description, fn, timeout) { if (timeout) { j$.util.validateTimeout(timeout); } const spec = this.specFactory_(description, fn, timeout); if (this.currentDeclarationSuite_.markedExcluding) { spec.exclude(); } this.currentDeclarationSuite_.addChild(spec); return spec; } suiteFactory_(description) { const config = this.env_.configuration(); return new j$.Suite({ id: 'suite' + this.nextSuiteId_++, description, parentSuite: this.currentDeclarationSuite_, timer: new j$.Timer(), expectationFactory: this.expectationFactory_, asyncExpectationFactory: this.suiteAsyncExpectationFactory_, throwOnExpectationFailure: config.stopSpecOnExpectationFailure, autoCleanClosures: config.autoCleanClosures, onLateError: this.onLateError_ }); } addSpecsToSuite_(suite, definitionFn) { const parentSuite = this.currentDeclarationSuite_; parentSuite.addChild(suite); this.currentDeclarationSuite_ = suite; let threw = false; try { definitionFn(); } catch (e) { suite.handleException(e); threw = true; } if (suite.parentSuite && !suite.children.length && !threw) { throw new Error( `describe with no children (describe() or it()): ${suite.getFullName()}` ); } this.currentDeclarationSuite_ = parentSuite; } specFactory_(description, fn, timeout) { this.totalSpecsDefined++; const config = this.env_.configuration(); const suite = this.currentDeclarationSuite_; const spec = new j$.Spec({ id: 'spec' + this.nextSpecId_++, beforeAndAfterFns: beforeAndAfterFns(suite), expectationFactory: this.expectationFactory_, asyncExpectationFactory: this.specAsyncExpectationFactory_, onLateError: this.onLateError_, resultCallback: (result, next) => { this.specResultCallback_(spec, result, next); }, getSpecName: function(spec) { return getSpecName(spec, suite); }, onStart: (spec, next) => this.specStarted_(spec, suite, next), description: description, userContext: function() { return suite.clonedSharedUserContext(); }, queueableFn: { fn: fn, timeout: timeout || 0 }, throwOnExpectationFailure: config.stopSpecOnExpectationFailure, autoCleanClosures: config.autoCleanClosures, timer: new j$.Timer() }); return spec; } unfocusAncestor_() { const focusedAncestor = findFocusedAncestor( this.currentDeclarationSuite_ ); if (focusedAncestor) { for (let i = 0; i < this.focusedRunables.length; i++) { if (this.focusedRunables[i] === focusedAncestor) { this.focusedRunables.splice(i, 1); break; } } } } } function findFocusedAncestor(suite) { while (suite) { if (suite.isFocused) { return suite.id; } suite = suite.parentSuite; } return null; } function ensureIsFunction(fn, caller) { if (!j$.isFunction_(fn)) { throw new Error( caller + ' expects a function argument; received ' + j$.getType_(fn) ); } } function ensureIsFunctionOrAsync(fn, caller) { if (!j$.isFunction_(fn) && !j$.isAsyncFunction_(fn)) { throw new Error( caller + ' expects a function argument; received ' + j$.getType_(fn) ); } } function beforeAndAfterFns(targetSuite) { return function() { let befores = [], afters = [], suite = targetSuite; while (suite) { befores = befores.concat(suite.beforeFns); afters = afters.concat(suite.afterFns); suite = suite.parentSuite; } return { befores: befores.reverse(), afters: afters }; }; } function getSpecName(spec, suite) { const fullName = [spec.description], suiteFullName = suite.getFullName(); if (suiteFullName !== '') { fullName.unshift(suiteFullName); } return fullName.join(' '); } return SuiteBuilder; }; getJasmineRequireObj().Timer = function() { const defaultNow = (function(Date) { return function() { return new Date().getTime(); }; })(Date); function Timer(options) { options = options || {}; const now = options.now || defaultNow; let startTime; this.start = function() { startTime = now(); }; this.elapsed = function() { return now() - startTime; }; } return Timer; }; getJasmineRequireObj().TreeProcessor = function() { function TreeProcessor(attrs) { const tree = attrs.tree; const runnableIds = attrs.runnableIds; const queueRunnerFactory = attrs.queueRunnerFactory; const nodeStart = attrs.nodeStart || function() {}; const nodeComplete = attrs.nodeComplete || function() {}; const failSpecWithNoExpectations = !!attrs.failSpecWithNoExpectations; const orderChildren = attrs.orderChildren || function(node) { return node.children; }; const excludeNode = attrs.excludeNode || function(node) { return false; }; let stats = { valid: true }; let processed = false; const defaultMin = Infinity; const defaultMax = 1 - Infinity; this.processTree = function() { processNode(tree, true); processed = true; return stats; }; this.execute = async function() { if (!processed) { this.processTree(); } if (!stats.valid) { throw 'invalid order'; } const childFns = wrapChildren(tree, 0); await new Promise(function(resolve) { queueRunnerFactory({ queueableFns: childFns, userContext: tree.sharedUserContext(), onException: function() { tree.handleException.apply(tree, arguments); }, onComplete: resolve, onMultipleDone: tree.onMultipleDone ? tree.onMultipleDone.bind(tree) : null }); }); }; function runnableIndex(id) { for (let i = 0; i < runnableIds.length; i++) { if (runnableIds[i] === id) { return i; } } } function processNode(node, parentExcluded) { const executableIndex = runnableIndex(node.id); if (executableIndex !== undefined) { parentExcluded = false; } if (!node.children) { const excluded = parentExcluded || excludeNode(node); stats[node.id] = { excluded: excluded, willExecute: !excluded && !node.markedPending, segments: [ { index: 0, owner: node, nodes: [node], min: startingMin(executableIndex), max: startingMax(executableIndex) } ] }; } else { let hasExecutableChild = false; const orderedChildren = orderChildren(node); for (let i = 0; i < orderedChildren.length; i++) { const child = orderedChildren[i]; processNode(child, parentExcluded); if (!stats.valid) { return; } const childStats = stats[child.id]; hasExecutableChild = hasExecutableChild || childStats.willExecute; } stats[node.id] = { excluded: parentExcluded, willExecute: hasExecutableChild }; segmentChildren(node, orderedChildren, stats[node.id], executableIndex); if (!node.canBeReentered() && stats[node.id].segments.length > 1) { stats = { valid: false }; } } } function startingMin(executableIndex) { return executableIndex === undefined ? defaultMin : executableIndex; } function startingMax(executableIndex) { return executableIndex === undefined ? defaultMax : executableIndex; } function segmentChildren( node, orderedChildren, nodeStats, executableIndex ) { let currentSegment = { index: 0, owner: node, nodes: [], min: startingMin(executableIndex), max: startingMax(executableIndex) }, result = [currentSegment], lastMax = defaultMax, orderedChildSegments = orderChildSegments(orderedChildren); function isSegmentBoundary(minIndex) { return ( lastMax !== defaultMax && minIndex !== defaultMin && lastMax < minIndex - 1 ); } for (let i = 0; i < orderedChildSegments.length; i++) { const childSegment = orderedChildSegments[i], maxIndex = childSegment.max, minIndex = childSegment.min; if (isSegmentBoundary(minIndex)) { currentSegment = { index: result.length, owner: node, nodes: [], min: defaultMin, max: defaultMax }; result.push(currentSegment); } currentSegment.nodes.push(childSegment); currentSegment.min = Math.min(currentSegment.min, minIndex); currentSegment.max = Math.max(currentSegment.max, maxIndex); lastMax = maxIndex; } nodeStats.segments = result; } function orderChildSegments(children) { const specifiedOrder = [], unspecifiedOrder = []; for (let i = 0; i < children.length; i++) { const child = children[i], segments = stats[child.id].segments; for (let j = 0; j < segments.length; j++) { const seg = segments[j]; if (seg.min === defaultMin) { unspecifiedOrder.push(seg); } else { specifiedOrder.push(seg); } } } specifiedOrder.sort(function(a, b) { return a.min - b.min; }); return specifiedOrder.concat(unspecifiedOrder); } function executeNode(node, segmentNumber) { if (node.children) { return { fn: function(done) { const onStart = { fn: function(next) { nodeStart(node, next); } }; queueRunnerFactory({ onComplete: function() { const args = Array.prototype.slice.call(arguments, [0]); node.cleanupBeforeAfter(); nodeComplete(node, node.getResult(), function() { done.apply(undefined, args); }); }, queueableFns: [onStart].concat(wrapChildren(node, segmentNumber)), userContext: node.sharedUserContext(), onException: function() { node.handleException.apply(node, arguments); }, onMultipleDone: node.onMultipleDone ? node.onMultipleDone.bind(node) : null }); } }; } else { return { fn: function(done) { node.execute( queueRunnerFactory, done, stats[node.id].excluded, failSpecWithNoExpectations ); } }; } } function wrapChildren(node, segmentNumber) { const result = [], segmentChildren = stats[node.id].segments[segmentNumber].nodes; for (let i = 0; i < segmentChildren.length; i++) { result.push( executeNode(segmentChildren[i].owner, segmentChildren[i].index) ); } if (!stats[node.id].willExecute) { return result; } return node.beforeAllFns.concat(result).concat(node.afterAllFns); } } return TreeProcessor; }; getJasmineRequireObj().UserContext = function(j$) { function UserContext() {} UserContext.fromExisting = function(oldContext) { const context = new UserContext(); for (const prop in oldContext) { if (oldContext.hasOwnProperty(prop)) { context[prop] = oldContext[prop]; } } return context; }; return UserContext; }; getJasmineRequireObj().version = function() { return '4.4.0'; }; jasmine-4.5.0/lib/jasmine-core/node_boot.js000066400000000000000000000027351432731766000206160ustar00rootroot00000000000000/* Copyright (c) 2008-2022 Pivotal Labs Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ module.exports = function(jasmineRequire) { const jasmine = jasmineRequire.core(jasmineRequire); const env = jasmine.getEnv({ suppressLoadErrors: true }); const jasmineInterface = jasmineRequire.interface(jasmine, env); extend(global, jasmineInterface); function extend(destination, source) { for (const property in source) destination[property] = source[property]; return destination; } return jasmine; }; jasmine-4.5.0/package.json000066400000000000000000000052141432731766000154470ustar00rootroot00000000000000{ "name": "jasmine-core", "license": "MIT", "version": "4.5.0", "repository": { "type": "git", "url": "https://github.com/jasmine/jasmine.git" }, "keywords": [ "test", "testing", "jasmine", "tdd", "bdd" ], "scripts": { "posttest": "eslint \"src/**/*.js\" \"spec/**/*.js\" && prettier --check \"src/**/*.js\" \"spec/**/*.js\"", "test": "grunt --stack execSpecsInNode", "cleanup": "prettier --write \"src/**/*.js\" \"spec/**/*.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", "ci:performance": "node spec/support/ci.js jasmine-browser-performance.json" }, "description": "Simple JavaScript testing framework for browsers and node.js", "homepage": "https://jasmine.github.io", "main": "./lib/jasmine-core.js", "files": [ "MIT.LICENSE", "README.md", "images/*.{png,svg}", "lib/**/*.{js,css}", "package.json" ], "devDependencies": { "eslint": "^7.32.0", "eslint-plugin-compat": "^4.0.0", "glob": "^7.2.0", "grunt": "^1.0.4", "grunt-cli": "^1.3.2", "grunt-contrib-compress": "^2.0.0", "grunt-contrib-concat": "^2.0.0", "grunt-css-url-embed": "^1.11.1", "grunt-sass": "^3.0.2", "jasmine": "^4.1.0", "jasmine-browser-runner": "^1.0.0", "jsdom": "^19.0.0", "load-grunt-tasks": "^5.1.0", "prettier": "1.17.1", "sass": "^1.45.1", "shelljs": "^0.8.3", "temp": "^0.9.0" }, "prettier": { "singleQuote": true }, "eslintConfig": { "extends": [ "plugin:compat/recommended" ], "env": { "browser": true, "node": true, "es2017": true }, "parserOptions": { "ecmaVersion": 2018 }, "rules": { "quotes": [ "error", "single", { "avoidEscape": true } ], "no-unused-vars": [ "error", { "args": "none" } ], "no-implicit-globals": "error", "block-spacing": "error", "func-call-spacing": [ "error", "never" ], "key-spacing": "error", "no-tabs": "error", "no-trailing-spaces": "error", "no-whitespace-before-property": "error", "semi": [ "error", "always" ], "space-before-blocks": "error", "no-eval": "error", "no-var": "error" } }, "browserslist": [ "Safari >= 14", "last 2 Chrome versions", "last 2 Firefox versions", "Firefox >= 91", "last 2 Edge versions" ] } jasmine-4.5.0/release_notes/000077500000000000000000000000001432731766000160075ustar00rootroot00000000000000jasmine-4.5.0/release_notes/1.3.0.md000066400000000000000000000014571432731766000167770ustar00rootroot00000000000000# Jasmine Core 1.3.0 Release Notes ## Summary This version was a very incremental release that merged in some pull requests for bug fixes. ## Features * HTML Runner exposes UI to not swallow Exceptions, instead raising as soon as thrown * Migrated homepage content to Wiki * Made a far more useful [tutorial page](http://pivotal.github.com/jasmine) * Added a `toBeNaN` matcher ## Fixes * Better detection of in-browser vs. not * `afterEach` functions will run now even when there is a timeout * `toBeCloseTo` matcher is more accurate * More explicit matcher messages for spies * Better equality comparisons for regular expressions * Improvements to the Pretty Printer: controllable recursion depth, don't include inherited properties * Fix where `for` was being used as a property on an object (failed on IE) jasmine-4.5.0/release_notes/1.3.1.md000066400000000000000000000002441432731766000167710ustar00rootroot00000000000000# Jasmine Core 1.3.1 Release Notes ## Summary This release is for browser compatibility fixes ## Changes ### Features Fixing test runner failures in IE 6/7/8 jasmine-4.5.0/release_notes/2.0.1.md000066400000000000000000000073411432731766000167740ustar00rootroot00000000000000# Jasmine Core 2.0.1 Release Notes ## Summary This release is for small bug fixes and enhancements ahead of a real-soon-now 2.1. ## Changes ### Features * NodeJS is now supported with a jasmine-core npm * [Support browsers that don't supply a `Date.now()` by having a `mockDate` object](http://www.pivotaltracker.com/story/66606132) - Closes #361 * [Show message if no specs where loaded](http://www.pivotaltracker.com/story/12784235) * When using `jasmine.any`, the `class` will now be included in the error message * Reporters now receive the number of passed expectations in a spec * Use default failure message for `toBeNaN` * Use the latest `jasmine_selenium_runner` so we use the fix for printing objects with cycles * Add jasmine logo image to HTML runner * Stop Jasmine's CSS affecting the style of the body tag - Closes #600 * Standardized location of the standalone distributions - they now live in the repo in `/dist` as well as on the Releases page ### Bugs * Don't allow calling the same done callback multiple times - Fixes #523 * [Remove 'empty' as an option as a spec result](http://www.pivotaltracker.com/story/73741032) as this was a breaking change * Instead, we determine if a spec has no expectations using the added key of `passedExpectations` in combination of the `failedExpectations` to determine that there a spec is 'empty' * Fix build in IE8 (IE8 doesn't support `Object.freeze`) * Fix `ObjectContaining` to match recursively ### Documentation * Update release doc to use GitHub releases * Add installation instructions to README - Merges #621 * Add Ruby Gem and Python Egg to docs * Add detailed steps on how to contribute - Merges #580 from @pablofiu ## Pull Requests and Issues * Contains is explicitly false if actual is `undefined` or `null` - Fixes #627 * namespace `html-reporter` -> `jasmine_html-reporter` - Fixes #600 * Throw a more specific error when `expect` is used without a `currentSpec` - Fixes #602 * Reduced size of logo with PNG Gauntlet - Merges #588 * HTML Reporter resets previous DOM when re-initialized - Merges #594 from @plukevdh * Narrow down raise exceptions query selector; Finding by any input tag is a little bit broad - Closes #605 * Pass through custom equality testers in toHaveBeenCalledWith - Fixes #536 * Fix outdated copyright year (update to 2014) - Merges #550 from @slothmonster * [Add package.json to Python egg to get correct version number](http://www.pivotaltracker.com/story/67556148) - Fixes #551 * Allow users to set the maximum length of array that the pretty-printer will print out - Fixes #323 @mikemoraned and #374 @futuraprime * `matchersUtil.equals()` does not expect a matcher as its first argument, so send the "actual" value first and the "expected" value second. - Merges #538 from @cbandy * Add single quote check to `jshint` and fix src files for that - Closes #522 * Remove an `eval` in order to support running jasmine within CSP - Closes #503 * Allow matcher custom failure messages to be a function - Closes #520 * More color blind friendly CSS from @dleppik - Closes #463 & #509 * Use `load-grunt-tasks` Merges #521 from @robinboehm * Special case printing `-0` - Closes #496 * Allow stub or spy Date object safely using a closure to get a clean copy - Closes #506 * [Use `\d7` instead of plain 'x' for more square appearance](http://www.pivotaltracker.com/story/48434179) * Better support in pretty printer when an object has null prototype - Fixes #500 * Update link at top of README to improve access to Jasmine 2.0 docs - Merges #486 from @nextmat * Force query selector to seek within the html-reporter element - Merges #479 from @shprink * Netbeans files are in gitignore - Merges #478 from @shprink ------ _Release Notes generated with [Anchorman](http://github.com/infews/anchorman)_ jasmine-4.5.0/release_notes/2.0.2.md000066400000000000000000000013261432731766000167720ustar00rootroot00000000000000# Release Notes ## Summary ## Changes * keep the files for running in a webpage around in the npm package * Expose files and paths necessary to embed jasmine in an html page for nodejs * Pull out the building of the jasmine interface so node and web both get the same one. * Show a dot with color of pending spec when no expectations * Console reporter prints out failed expectation's message ### Bugs * Allow mocked Date constructor to be called with a subset of full params ## Pull Requests and Issues * a disabled suite should call resultCallback with status being disabled * disabled suite should still call onStart callback ------ _Release Notes generated with _[Anchorman](http://github.com/infews/anchorman)_ jasmine-4.5.0/release_notes/2.1.0.md000066400000000000000000000075231432731766000167760ustar00rootroot00000000000000# Jasmine Core 2.1.0 Release Notes ## Summary This is the release of Jasmine 2.1. ## Features - Support for focused specs via `fit` and `fdescribe` - Support for `beforeAll` and `afterAll` - Support for an explicit `fail` function, both in synchronous and asynchronous specs - Allow custom timeout for `beforeEach`, `afterEach`, `beforeAll`, `afterAll` and `it` - Spies now track return values - Specs can now specify their own timeouts - Testing in Node.js via the official Jasmine Node Module - Spec results now have `suiteResults` method that behaves similarly to to `specResults` - HtmlReporter shows error alerts for afterAllExceptions ## Bugs - CI now works for IE8 (this was releated to `ConsoleReporter` below) - Detect global object properly when getting the jasmine require obj - Fixes Issue #[569][issue_569] - [Tracker Story #73684570](http://www.pivotaltracker.com/story/73684570) ## Deprecations ### `ConsoleReporter` as part of Jasmine core The Console Reporter exists nearly entirely for the old manner of running Jasmine's own specs in node.js. As we are now supporting node.js officially, this reporter code no longer needs to be in this repo and instead will be in the Jasmine npm. For now you will see a deprecation message. It will be removed entirely in Jasmine 3.0. ## Documentation - Release Notes from previous releases now live at [Jasmine's GitHub release page][releases]. See Tracker Story #[54582902][tracker_1] - Better instructions for releasing new documentation [releases]: https://github.com/pivotal/jasmine/releases [tracker_1]: http://www.pivotaltracker.com/story/54582902 ## Pull Requests and Issues - Simplification of HTMLtags in SpecRunner.html - Merges #[700][issue_700] from @tkrotoff - `toContain` works with array-like objects (Arguments, HTMLCollections, etc) - Merges #[699][issue_699] from @charleshansen - Fixed isPendingSpecException test title - Merges #[691][issue_691] from @ertrzyiks - Fixed an issue with example code in the npm - Merges #[686][issue_686] from @akoptsov - When the Jasmine clock is installed and date is mocked, `new Date() instanceof Date` should equal `true` Issue #[678][issue_678] & Issue #[679][issue_679] from @chernetsov - Support for an explicit `fail` function, both in synchronous and asynchronous specs - Fixes Issue #[567][issue_567], Issue #[568][issue_568], and Issue #[563][issue_563] - Allow custom timeout for `beforeEach`, `afterEach`, `beforeAll`, `afterAll` and `it` - Fixes Issue #[483][issue_483] - specs can now specify their own timeouts - Spies can track return values - Fixes Issue #[660][issue_660], Merged Issue #[669][issue_669] from @mkhanal - Interval handlers can now clear their interval - Fixes Issue #[655][issue_655] Merged Issue #[658][issue_658] from @tgirardi - Updated to the latest `json2.js` - Merges #[616][issue_616] from @apaladox2015 ------ _Release Notes generated with [Anchorman](http://github.com/infews/anchorman)_ [issue_569]: http://github.com/pivotal/jasmine/issues/569 [issue_700]: http://github.com/pivotal/jasmine/issues/700 [issue_699]: http://github.com/pivotal/jasmine/issues/699 [issue_691]: http://github.com/pivotal/jasmine/issues/691 [issue_678]: http://github.com/pivotal/jasmine/issues/678 [issue_679]: http://github.com/pivotal/jasmine/issues/679 [issue_567]: http://github.com/pivotal/jasmine/issues/567 [issue_568]: http://github.com/pivotal/jasmine/issues/568 [issue_563]: http://github.com/pivotal/jasmine/issues/563 [issue_483]: http://github.com/pivotal/jasmine/issues/483 [issue_660]: http://github.com/pivotal/jasmine/issues/660 [issue_669]: http://github.com/pivotal/jasmine/issues/669 [issue_655]: http://github.com/pivotal/jasmine/issues/655 [issue_658]: http://github.com/pivotal/jasmine/issues/658 [issue_616]: http://github.com/pivotal/jasmine/issues/616 [issue_686]: http://github.com/pivotal/jasmine/issues/686 jasmine-4.5.0/release_notes/2.1.1.md000066400000000000000000000006111432731766000167660ustar00rootroot00000000000000# Jasmine Core 2.1.1 Release Notes ## Summary This is a hotfix release of jasmine core to fix a breaking change with events emitted by the top-level suite ## Issues - Top-level suite triggers suiteStarted and suiteEnd to be consistent - Fixes [#706](http://github.com/pivotal/jasmine/issues/706) ------ _Release Notes generated with _[Anchorman](http://github.com/infews/anchorman)_ jasmine-4.5.0/release_notes/2.1.2.md000066400000000000000000000006241432731766000167730ustar00rootroot00000000000000# Jasmine Core 2.1.2 Release Notes ## Summary This is a hotfix release of jasmine core to fix a breaking change with reporting when all of the specs in a suite are pending. ## Changes - Suites still run their children even if none are executable - Fixes [#707](http://github.com/pivotal/jasmine/issues/707) ------ _Release Notes generated with _[Anchorman](http://github.com/infews/anchorman)_ jasmine-4.5.0/release_notes/2.1.3.md000066400000000000000000000012521432731766000167720ustar00rootroot00000000000000# Jasmine Core 2.1.3 Release Notes ## Summary This release is primarily a bug-fix release to clean up some recent regressions. ## Pull Requests & Issues * Top level suite no longer reports suiteStart and suiteDone - Fixes [#716](https://github.com/jasmine/jasmine/issues/716) * Don't keep the expected and actual for a passed expectation - Fixes [#640](https://github.com/jasmine/jasmine/issues/640) - Fixes [#690](https://github.com/jasmine/jasmine/issues/690) * Use the new build env on Travis - Merges [#712](https://github.com/jasmine/jasmine/issues/712) from @joshk ------ _Release Notes generated with _[Anchorman](http://github.com/infews/anchorman)_ jasmine-4.5.0/release_notes/2.2.0.md000066400000000000000000000102301432731766000167640ustar00rootroot00000000000000# Jasmine Core 2.2.0 Release Notes ## Changes * `ObjectContaining` no longer tries to track exact mismatches * HTML reporter keeps extra query params when focusing on a spec or suite * Check custom properties on Arrays when computing equality * Better error message if `spyOn` is called without a method name * Rename `jasmineMatches` to `asymmetricMatch` * Don't double escape focus spec links * Jasmine equality checks if either side implements `asymmetricMatch` * Add asymmetric equality tester to match a string with a RegExp * Add jshint to node build on Travis for pull request builds * Restructure node examples directory to look more realistic ## Pull Requests & Issues * Add a basic bower config - Fixes [#719](https://github.com/jasmine/jasmine/issues/719) * Allow `pending` to take a reason and show it in the HtmlReporter - Fixes [#671](https://github.com/jasmine/jasmine/issues/671) * Set jasmineGlobal correctly in GJS - Merges [#757](https://github.com/jasmine/jasmine/issues/757) from @ptomato - Fixes [#751](https://github.com/jasmine/jasmine/issues/751) * Fix some SpiderMonkey lint warnings - Merges [#752](https://github.com/jasmine/jasmine/issues/752) from @ptomato - Fixes [#751](https://github.com/jasmine/jasmine/issues/751) * Prevents *Alls from running when runnables are explicitly set - Fixes [#732](https://github.com/jasmine/jasmine/issues/732) * Update contribution guide to mention possible ffi dependencies for Ubuntu - Fixes [#755](https://github.com/jasmine/jasmine/issues/755) * Fix spelling mistake in contributors guide - Merges [#746](https://github.com/jasmine/jasmine/issues/746) from @swirlycheetah * Use new jasmine github repo url - Merges [#745](https://github.com/jasmine/jasmine/issues/745) rohit * Allow `createSpyObj` to be called with just an array of method names - Fixes [#321](https://github.com/jasmine/jasmine/issues/321) * Add `arrayContaining` matcher - Merges [#440](https://github.com/jasmine/jasmine/issues/440) from @slackersoft * Use the stack trace from the Error object if supplied - Fixes [#734](https://github.com/jasmine/jasmine/issues/734) * Update readme with link to upgrading doc and mention browser support. - Fixes [#739](https://github.com/jasmine/jasmine/issues/739) * Link to the Jasmine NPM module - Merges [#736](https://github.com/jasmine/jasmine/issues/736) from @moonmaster9000 * Allow null prototype objects to be compared for equality - Merges [#731](https://github.com/jasmine/jasmine/issues/731) from @rohit - Fixes [#729](https://github.com/jasmine/jasmine/issues/729) * Fix URL's of Jasmine repositories on Github - Merges [#730](https://github.com/jasmine/jasmine/issues/730) from @rohit * Add `anything` matcher to match any value that is neither null or undefined - Fixes [#186](https://github.com/jasmine/jasmine/issues/186) * Allow asymmetric equality testers to preempt their symmetric brethren - Fixes [#540](https://github.com/jasmine/jasmine/issues/540) * Check for `ObjectContaining` on either side of equality - Fixes [#682](https://github.com/jasmine/jasmine/issues/682) * Display the name of the constructor when pretty printing objects - Fixes [#598](https://github.com/jasmine/jasmine/issues/598) * `toMatch` requires the `expected` to be a String or RegExp - Fixes [#723](https://github.com/jasmine/jasmine/issues/723) * Better equality comparison of Dom nodes - Merges [#657](https://github.com/jasmine/jasmine/issues/657) from @alexeibs * Hide unnecessary files from the npm package - Fixes [#726](https://github.com/jasmine/jasmine/issues/726) * Properly record finishing an `xdescribe` so further cleanup works - Fixes [#724](https://github.com/jasmine/jasmine/issues/724) * Reschedule all functions for a tick before executing any. This allows any function run during a tick to cancel any other in the same tick. - Fixes [#708](https://github.com/jasmine/jasmine/issues/708) * Pass through all args from external interface for befores, afters, its - Fixes [#483](https://github.com/jasmine/jasmine/issues/483) ------ _Release Notes generated with _[Anchorman](http://github.com/infews/anchorman)_ jasmine-4.5.0/release_notes/2.2.1.md000066400000000000000000000006341432731766000167740ustar00rootroot00000000000000# Jasmine Core 2.2.1 Release Notes ## Summary This is a hotfix release to fix the packaging for bower ## Changes * Fix missing comma on bower.json - Merges [#763](https://github.com/jasmine/jasmine/issues/763) from @gabrielhpugliese - Merges [#764](https://github.com/jasmine/jasmine/issues/764) from @joshuacc ------ _Release Notes generated with _[Anchorman](http://github.com/infews/anchorman)_ jasmine-4.5.0/release_notes/2.3.0.md000066400000000000000000000071271432731766000170000ustar00rootroot00000000000000# Jasmine Core 2.3.0 Release Notes ## Changes * Style disabled specs in the results list * Use `onclick` directly to better support older webkit * Don't use deprecated `onComplete` syntax for jasmine-npm * Allow the clock to be installed for the duration of a single closure * Add safari 7 & 8 to browser matrix * Remove unused standaloneBuilder var from Gruntfile * Add test script to package.json * Update bower.json keywords to match package.json keywords * Add keywords to package.json * refuse to execute an order if it would cause a suite with a beforeAll or afterAll to be re-entered after leaving once ## Pull Requests & Issues * Specify a main entry point for bower so it can be loaded easier - Merges [#827](https://github.com/jasmine/jasmine/issues/827) from @davetron5000 * Use `instanceof` when checking Error types in toThrowError - Fixes [#819](https://github.com/jasmine/jasmine/issues/819) * Remove periods from bullet points for consistency with rest of document - Merge [#818](https://github.com/jasmine/jasmine/issues/818) from @lpww * Subjective readability improvements to CONTRIBUTING.md - Merge [#815](https://github.com/jasmine/jasmine/issues/815) from @jhamon * Don't install the clock if the current timing functions aren't the originals - Fixes [#782](https://github.com/jasmine/jasmine/issues/782) * Properly pass `j$` to `Any` so it can use other jasmine stuff - Fixes [#806](https://github.com/jasmine/jasmine/issues/806) * Correctly handle functions that are scheduled after the clock is uninstalled and reinstalled from within Clock#tick. - Merges [#804](https://github.com/jasmine/jasmine/issues/804) from @sgravrock - Fixes [#790](https://github.com/jasmine/jasmine/issues/790). * Allow user to stop a specs execution when an expectation fails - Fixes [#577](https://github.com/jasmine/jasmine/issues/577) * Remove unnecessary conditional - Merges [#788](https://github.com/jasmine/jasmine/issues/788) from @toddbranch * Show the name of the constructor function when printing an `any` instead of a `toString` of the entire constructor - Fixes [#796](https://github.com/jasmine/jasmine/issues/796) * Don't use hardcoded temporary directory paths - Merges [#789](https://github.com/jasmine/jasmine/issues/789) from sgravrock * Execute beforeAll/afterAll once per suite instead of once per child when running focused specs/suites - Fixes [#773](https://github.com/jasmine/jasmine/issues/773) * Report children of an xdescribe similarly to how they would be reported if they were themselves x'd out - Fixes [#774](https://github.com/jasmine/jasmine/issues/774) - Fixes [#776](https://github.com/jasmine/jasmine/issues/776) * Fixes issue where mock clock was being used by QueueRunner - Fixes [#783](https://github.com/jasmine/jasmine/issues/783) - Fixes [#792](https://github.com/jasmine/jasmine/issues/792) * add missing semicolon - Merges [#775](https://github.com/jasmine/jasmine/issues/775) from @joscha * ObjectContaining matches prototype properties - Fixes [#769](https://github.com/jasmine/jasmine/issues/769) * Updates pretty printer to include array properties - Fixes [#766](https://github.com/jasmine/jasmine/issues/766) * Update year copyright - Merges [#768](https://github.com/jasmine/jasmine/issues/768) from @danilovaz * Allow arrays from different frames or contexts to be equal - Merges [#767](https://github.com/jasmine/jasmine/issues/767) from @juliemr - Fixes [#765](https://github.com/jasmine/jasmine/issues/765) ------ _Release Notes generated with _[Anchorman](http://github.com/infews/anchorman)_ jasmine-4.5.0/release_notes/2.3.1.md000066400000000000000000000005451432731766000167760ustar00rootroot00000000000000# Jasmine 2.3.1 Release Notes ## Summary This release is a packaging update for bower only. ## Pull Requests & Issues * Point Bower's main field to jasmine.js, which is browser-friendly. - Merge [#843](https://github.com/jasmine/jasmine/issues/843) from @evoL ------ _Release Notes generated with _[Anchorman](http://github.com/infews/anchorman)_ jasmine-4.5.0/release_notes/2.3.2.md000066400000000000000000000006031432731766000167720ustar00rootroot00000000000000# Jasmine 2.3.2 Release Notes ## Summary This is a hotfix release to fix a regression with specs declared without a function body ## Pull Requests & Issues * A spec without a function provided should be `pending` not `disabled` - Fixes [#840](https://github.com/jasmine/jasmine/issues/840) ------ _Release Notes generated with _[Anchorman](http://github.com/infews/anchorman)_ jasmine-4.5.0/release_notes/2.3.3.md000066400000000000000000000006051432731766000167750ustar00rootroot00000000000000# Jasmine 2.3.3 Release Notes ## Summary This is a hotfix release to fix a regression with the execution context for `beforeAll` ## Pull Requests & Issues * Set the shared user context correctly when executing the top level suite - Fixes [#846](https://github.com/jasmine/jasmine/issues/846) ------ _Release Notes generated with _[Anchorman](http://github.com/infews/anchorman)_ jasmine-4.5.0/release_notes/2.3.4.md000066400000000000000000000014001432731766000167700ustar00rootroot00000000000000# Jasmine 2.3.4 Release Notes ## Summary This is a hotfix release to fix a regression with execution ordering ## Pull Requests & Issues * Fix ordering for suites with more than 11 direct children. - Fixes [#850](https://github.com/jasmine/jasmine/issues/850) * Update standalone installation instructions to reference the releases page - Fixes [#603](https://github.com/jasmine/jasmine/issues/603) * Remove dead CSS class styles - Merges [#849](https://github.com/jasmine/jasmine/issues/849) from @prather-mcs - Merges [#848](https://github.com/jasmine/jasmine/issues/848) from @prather-mcs - Fixes [#847](https://github.com/jasmine/jasmine/issues/847) ------ _Release Notes generated with _[Anchorman](http://github.com/infews/anchorman)_ jasmine-4.5.0/release_notes/2.4.0.md000066400000000000000000000074371432731766000170050ustar00rootroot00000000000000# Jasmine Core 2.4.0 Release Notes ## Summary This release contains a number of fixes and pull requests. The most notable is probably that Jasmine now supports randomization of spec order ## Changes * Run jasmine's specs in random order * Add support for returning run details for reporting randomness * Use className instead of class when creating DOM elements ## Pull Requests & Issues * Syntax highlighting in README.md - Merges [#973](https://github.com/jasmine/jasmine/issues/973) from @brunoqc * Added a throw error block in describe incase a function with arguments is passed in describe - Fixes [#896](https://github.com/jasmine/jasmine/issues/896) - Merges [#955](https://github.com/jasmine/jasmine/issues/955) from @himajasuman * Remove unused `queueableFn` arg from `onException` - Fixes [#958](https://github.com/jasmine/jasmine/issues/958) * Remove unused parameter from toThrowError - Merges [#957](https://github.com/jasmine/jasmine/issues/957) from @FuzzySockets * Abort spying when the target cannot be spied upon - Fixes [#948](https://github.com/jasmine/jasmine/issues/948) - Merges [#949](https://github.com/jasmine/jasmine/issues/949) from @StephanBijzitter * Removed GOALS_2.0.md, doesn't seem to be needed anymore - Merges [#954](https://github.com/jasmine/jasmine/issues/954) from @matthewhuff89 * Change #xit so that it will output a more BDD-style pending message - Merges [#942](https://github.com/jasmine/jasmine/issues/942) from @lalunamel - Fixes [#930](https://github.com/jasmine/jasmine/issues/930) - Fixes [#912](https://github.com/jasmine/jasmine/issues/912) * Allow tests to run in random order - Merges [#927](https://github.com/jasmine/jasmine/issues/927) from @marcioj * Use toString for objects if it has been overriden - Merges [#929](https://github.com/jasmine/jasmine/issues/929) from @myitcv - Fixes [#928](https://github.com/jasmine/jasmine/issues/928) * Fix circles/x from getting cut off on Mac/chrome - Merges [#932](https://github.com/jasmine/jasmine/issues/932) from @James-Dunn * Postpone find() until it is needed - Merges [#924](https://github.com/jasmine/jasmine/issues/924) from @danielalexiuc - Fixes [#917](https://github.com/jasmine/jasmine/issues/917) * check for global before assigning * Reverse suite afterEach behavior to match semantics? - Merges [#908](https://github.com/jasmine/jasmine/issues/908) from @mcamac * Use badges from shields.io - Merges [#902](https://github.com/jasmine/jasmine/issues/902) from @SimenB * xdescribe marks pending, plus associated tests. - Merges [#869](https://github.com/jasmine/jasmine/issues/869) from @ljwall - Fixes [#855](https://github.com/jasmine/jasmine/issues/855) * Update glob to latest - Merge [#892](https://github.com/jasmine/jasmine/issues/892) from @obastemur - Fixes [#891](https://github.com/jasmine/jasmine/issues/891) * Remove moot `version` property from bower.json - Merges [#874](https://github.com/jasmine/jasmine/issues/874) from @kkirsche * add toHaveBeenCalledTimes matcher - Merges [#871](https://github.com/jasmine/jasmine/issues/871) from @logankd - Fixes [#853](https://github.com/jasmine/jasmine/issues/853) * Update CONTRIBUTING.md - Merges [#856](https://github.com/jasmine/jasmine/issues/856) from @lpww * Make the HtmlReport CSS classes "unique enough" - Merges [#851](https://github.com/jasmine/jasmine/issues/851) from @prather-mcs - Fixes [#844](https://github.com/jasmine/jasmine/issues/844) * Raise an error when jasmine.any() isn't passed a constructor - Merges [#854](https://github.com/jasmine/jasmine/issues/854) from @danfinnie - Fixes [#852](https://github.com/jasmine/jasmine/issues/852) ------ _Release Notes generated with _[Anchorman](http://github.com/infews/anchorman)_ jasmine-4.5.0/release_notes/2.4.1.md000066400000000000000000000004041432731766000167710ustar00rootroot00000000000000# Jasmine Core 2.4.1 Release Notes ## Changes * Run `afterEach` in reverse order declared as before - Reverts [#908](https://github.com/jasmine/jasmine/issues/908) ------ _Release Notes generated with _[Anchorman](http://github.com/infews/anchorman)_ jasmine-4.5.0/release_notes/2.5.0.md000066400000000000000000000117421432731766000170000ustar00rootroot00000000000000# Jasmine 2.5.0 Release Notes ## Summary This release contains a number of fixes and pull requests. ## Changes * Rename `j$` to `jasmineUnderTest` for specs - Please update any pull requests to simplify merging, thanks. ## Pull Requests & Issues * Prettyprint objects whose constructors have custom toString method - Fixes [#1019](https://github.com/jasmine/jasmine/issues/1019) - Merges [#1099](https://github.com/jasmine/jasmine/issues/1099) from @mbildner * Add gulp-jasmine-browser link to readme - Fixes [#1089](https://github.com/jasmine/jasmine/issues/1089) * Exclude lib directory from codeclimate - Fixes [#1171](https://github.com/jasmine/jasmine/issues/1171) * Add instructions for testing in IE - Merges [#1170](https://github.com/jasmine/jasmine/issues/1170) from @benchristel * Update devDependencies and fix issues from this - Merges [#1162](https://github.com/jasmine/jasmine/issues/1162) from @amavisca * Remove runnableLookupTable which is no longer used - Merges [#1129](https://github.com/jasmine/jasmine/issues/1129) from @gregeninfrank * Make `toEqual` pass for arrays with equivalent properties - Merges [#1155](https://github.com/jasmine/jasmine/issues/1155) from @benchristel * Update ruby version on travis to let rack install - Merges [#1152](https://github.com/jasmine/jasmine/issues/1152) from @amavisca * Fix jasmine setup in Electron environment - Merges [#1079](https://github.com/jasmine/jasmine/issues/1079) from @skupr - Fixes [#964](https://github.com/jasmine/jasmine/issues/964) * Improve errors with the domain and how to use the API - Merges [#1026](https://github.com/jasmine/jasmine/issues/1026) from @dhoko - Fixes [#1025](https://github.com/jasmine/jasmine/issues/1025) * The done function now returns null - Merges [#1062](https://github.com/jasmine/jasmine/issues/1062) from @marneborn - Fixes [#992](https://github.com/jasmine/jasmine/issues/992) * Add .editorconfig file - Merges [#1058](https://github.com/jasmine/jasmine/issues/1058) from @kapke - Fixes [#1057](https://github.com/jasmine/jasmine/issues/1057) * Improve error message when passing a non-function to callFake - Merges [#1059](https://github.com/jasmine/jasmine/issues/1059) from @kapke - Fixes [#1016](https://github.com/jasmine/jasmine/issues/1016) * Allow expectations in a global beforeAll or afterAll - Fixes [#811](https://github.com/jasmine/jasmine/issues/811) * Correctly tear down spies on inherited methods - Merges [#1036](https://github.com/jasmine/jasmine/issues/1036) from @benchristel - Fixes [#737](https://github.com/jasmine/jasmine/issues/737) * Array equality treats undefined elements as equal however they got in there - Fixes [#786](https://github.com/jasmine/jasmine/issues/786) * Add support for a fallback reporter - Merges [#1009](https://github.com/jasmine/jasmine/issues/1009) from @mauricioborges * Grunt task for compass should prefix command with 'bundle exec' - Merges [#1047](https://github.com/jasmine/jasmine/issues/1047) from @d-reinhold * Fix `toEqual` for Microsoft Edge - Merges [#1041](https://github.com/jasmine/jasmine/issues/1041) from @everedifice * Update describe error message to no longer assume errant args are `done` - Fixes [#896](https://github.com/jasmine/jasmine/issues/896) * Add toBeGreatThanOrEqual and toBeLessThanOrEqual matchers - Merges [#1049](https://github.com/jasmine/jasmine/issues/1049) from @rullopat - Fixes [#1013](https://github.com/jasmine/jasmine/issues/1013) * Support call count of 0 with toHaveBeenCalledTimes matcher - Merges [#1048](https://github.com/jasmine/jasmine/issues/1048) from @logankd - Fixes [#994](https://github.com/jasmine/jasmine/issues/994) * Correctly clean up spies after a spy is replaced and re-spied upon - Merges [#1011](https://github.com/jasmine/jasmine/issues/1011) from @bodawei - Fixes [#1010](https://github.com/jasmine/jasmine/issues/1010) * remove extra topSuite `queueRunner` construction parameter - Merges [#1006](https://github.com/jasmine/jasmine/issues/1006) from @jurko-gospodnetic * add option to shallow clone args in call tracker - Merges [#1000](https://github.com/jasmine/jasmine/issues/1000) from @a-r-d - Fixes [#872](https://github.com/jasmine/jasmine/issues/872) * Update license year range to 2016 - Merges [#1021](https://github.com/jasmine/jasmine/issues/1021) from pra85 * Persist randomize param in 'run all' links - Merges [#990](https://github.com/jasmine/jasmine/issues/990) from @basawyer * make DelayedFunctionScheduler update the mockDate - Fixes [#915](https://github.com/jasmine/jasmine/issues/915) - Merges [#980](https://github.com/jasmine/jasmine/issues/980) from @andrewiggings * Allow `spyOn` to allow a respy for functions that have already been spied upon - Merges [#953](https://github.com/jasmine/jasmine/issues/953) from @guy-mograbi-at-gigaspaces - Fixes [#931](https://github.com/jasmine/jasmine/issues/931) ------ _Release Notes generated with _[Anchorman](http://github.com/infews/anchorman)_ jasmine-4.5.0/release_notes/2.5.1.md000066400000000000000000000012171432731766000167750ustar00rootroot00000000000000# Jasmine 2.5.1 Release Notes ## Pull Requests & Issues * fallback on assignment when a spy cannot be deleted - Merges [#1193](https://github.com/jasmine/jasmine/issues/1193) from @seanparmlee - Fixes [#1189](https://github.com/jasmine/jasmine/issues/1189) * Fix issue with equality of Arrays in PhantomJS - Merges [#1192](https://github.com/jasmine/jasmine/issues/1192) from @logankd - Fixes [#1188](https://github.com/jasmine/jasmine/issues/1188) * Properly tick date along with clock - Fixes [#1190](https://github.com/jasmine/jasmine/issues/1190) ------ _Release Notes generated with _[Anchorman](http://github.com/infews/anchorman)_ jasmine-4.5.0/release_notes/2.5.2.md000066400000000000000000000006261432731766000170010ustar00rootroot00000000000000# Jasmine 2.5.2 Release Notes ## Pull Requests & Issues * Allow currently registered reporters to be cleared - [jasmine/jasmine-npm#88](https://github.com/jasmine/jasmine-npm/issues/88) * Use `isFunction` to check for functionness in `callFake` - Fixes [#1191](https://github.com/jasmine/jasmine/issues/1191) ------ _Release Notes generated with _[Anchorman](http://github.com/infews/anchorman)_ jasmine-4.5.0/release_notes/2.6.0.md000066400000000000000000000115371432731766000170030ustar00rootroot00000000000000# Jasmine 2.6.0 Release Notes ## Summary This release contains a number of fixes and pull requests. ## Pull Requests & Issues Updating introduction url to last version - Merges [#1316](https://github.com/jasmine/jasmine/issues/1316) from @rachelcarmena * Throw a recognizable Error message when `fail` outside of a spec. - Fixes [#1017](https://github.com/jasmine/jasmine/issues/1017) * Allow the matcher provide a custom error message - Merges [#1298](https://github.com/jasmine/jasmine/issues/1298) from @deckar01 - Fixes [#1123](https://github.com/jasmine/jasmine/issues/1123) * Fix the order in which afterAll hooks are run to match afterEach - Merges [#1312](https://github.com/jasmine/jasmine/issues/1312) from @gdborton - Fixes [#1311](https://github.com/jasmine/jasmine/issues/1311) * Add matchers for positive and negative infinity - Merges [#1300](https://github.com/jasmine/jasmine/issues/1300) from @toubou91 - Fixes [#1294](https://github.com/jasmine/jasmine/issues/1294) * Add a first pass at JSDocs for the intended public API - Fixes [#596](https://github.com/jasmine/jasmine/issues/596) * Pretty print objects passed to fail method - Merges [#1283](https://github.com/jasmine/jasmine/issues/1283) from @mmmichl - Fixes [#1090](https://github.com/jasmine/jasmine/issues/1090) * Properly check for Error constructor from a different frame - Merges [#1275](https://github.com/jasmine/jasmine/issues/1275) from @anseki - Fixes [#1252](https://github.com/jasmine/jasmine/issues/1252) * Add toHaveBeenCalledBefore matcher - Merges [#1242](https://github.com/jasmine/jasmine/issues/1242) from @DamienCassou * Collect unhandled exceptions and pass them to the current runnable - Fixes [#529](https://github.com/jasmine/jasmine/issues/529) - Fixes [#937](https://github.com/jasmine/jasmine/issues/937) * Nicer error messages for `spyOn` when `null` is provided - Fixes [#1258](https://github.com/jasmine/jasmine/issues/1258) * Require arguments to beforeEach, it, etc, to be actual functions - Merges [#1222](https://github.com/jasmine/jasmine/issues/1222) from @voithos - Fixes [#1004](https://github.com/jasmine/jasmine/issues/1004) * Update MIT.LICENSE for new year - Merges [#1249](https://github.com/jasmine/jasmine/issues/1249) from @Scottkao85 * Update README.md for new year - Merges [#1248](https://github.com/jasmine/jasmine/issues/1248) from @Nebojsaa * Remove unused `message` param from Suite#pend - See [#1132](https://github.com/jasmine/jasmine/issues/1132) * Fix bug where before/afterAll were being executed in disabled suites. - Merges [#1225](https://github.com/jasmine/jasmine/issues/1225) from @voithos - Fixes [#1175](https://github.com/jasmine/jasmine/issues/1175) * Make toEqual matcher report the difference between objects - Merges [#1163](https://github.com/jasmine/jasmine/issues/1163) from @benchristel - Fixes [#675](https://github.com/jasmine/jasmine/issues/675) - Merges [#1236](https://github.com/jasmine/jasmine/issues/1236) from @benchristel * Implement spies for get/set functions on accessor properties - Merges [#1203](https://github.com/jasmine/jasmine/issues/1203) from @celluj34 - Merges [#1008](https://github.com/jasmine/jasmine/issues/1008) from @smacker - Fixes [#943](https://github.com/jasmine/jasmine/issues/943) * When the HtmlReporter has a 'spec' query param, the spec list only shows matching specs/suites - Merges [#1046](https://github.com/jasmine/jasmine/issues/1046) from @d-reinhold - Fixes [#510](https://github.com/jasmine/jasmine/issues/510) * createSpyObj may use object for method/response shorthand - Merges [#1101](https://github.com/jasmine/jasmine/issues/1101) from @mbildner * Separate clear stack and run it after each spec - Fixes [#985](https://github.com/jasmine/jasmine/issues/985) - Fixes [#945](https://github.com/jasmine/jasmine/issues/945) - Fixes [#366](https://github.com/jasmine/jasmine/issues/366) * Now spies preserve original function arity - Merges [#1055](https://github.com/jasmine/jasmine/issues/1055) from @kapke - Fixes [#991](https://github.com/jasmine/jasmine/issues/991) * Added support for ES6 sets to toContain and toEqual. - Merges [#1067](https://github.com/jasmine/jasmine/issues/1067) from @alur * Correctly pretty print objects from other contexts (e.g. iframes) and which do not override toString - Merges [#1091](https://github.com/jasmine/jasmine/issues/1091) from @thatfulvioguy - Fixes [#1087](https://github.com/jasmine/jasmine/issues/1087) * Pass custom testers to asymmetric testers - Merges [#1139](https://github.com/jasmine/jasmine/issues/1139) from @joeyparrish - Fixes [#1138](https://github.com/jasmine/jasmine/issues/1138) * Fix bad url in README - Merges [#1215](https://github.com/jasmine/jasmine/issues/1215) from @mattc41190 ------ _Release Notes generated with _[Anchorman](http://github.com/infews/anchorman)_ jasmine-4.5.0/release_notes/2.6.1.md000066400000000000000000000013141432731766000167740ustar00rootroot00000000000000# Jasmine 2.6.1 Release Notes ## Summary This is a patch release to fix some regressions in the 2.6.0 release ## Pull Requests & Issues * Update README.md to make installation instructions more version-agnostic - Merges #1319 from @reinrl * Check for `process.listeners` as well, for GlobalErrors - Fixes #1333 * allow explicit undefined as function for `it` and `xit` - Merges #1329 from @UziTech - Fixes #1328 * remove eval to create spy wrapper - Merges #1330 from @UziTech - Fixes #1325 * iterate through keys with a regular for loop - Merges #1326 from @seanparmelee - Fixes #1321 - Fixes #1324 ------ _Release Notes generated with _[Anchorman](http://github.com/infews/anchorman)_ jasmine-4.5.0/release_notes/2.6.2.md000066400000000000000000000011461432731766000170000ustar00rootroot00000000000000# Jasmine 2.6.2 Release Notes ## Summary This is a patch release to fix some regressions and performance problems in the 2.6.0 release ## Changes * Clear the stack if onmessage is called before the previous invocation finishes - Fixes #1327 - Fixes jasmine/gulp-jasmine-browser#48 * Correctly route errors that occur while a QueueRunner is clearing stack - Merges #1352 from @sgravrock - Fixes #1344 - Fixes #1349 * Don't mask errors that occur when no handlers are installed - Merges #1347 from @sgravrock ------ _Release Notes generated with _[Anchorman](http://github.com/infews/anchorman)_ jasmine-4.5.0/release_notes/2.6.3.md000066400000000000000000000010231432731766000167730ustar00rootroot00000000000000# Jasmine 2.6.3 Release Notes ## Summary This is a patch release to fix some regressions and performance problems in the 2.6.0 release ## Changes * Make sure the queue runner goes async for async specs - Fixes [#1327](https://github.com/jasmine/jasmine/issues/1327) - Fixes [#1334](https://github.com/jasmine/jasmine/issues/1334) - Fixes [jasmine/gulp-jasmine-browser#48](https://github.com/jasmine/gulp-jasmine-browser/issues/48) ------ _Release Notes generated with _[Anchorman](http://github.com/infews/anchorman)_ jasmine-4.5.0/release_notes/2.6.4.md000066400000000000000000000011201432731766000167720ustar00rootroot00000000000000# Jasmine 2.6.4 Release Notes ## Summary This is a patch release to fix some regressions and performance problems in the 2.6.0 release ## Changes * Break into a `setTimeout` every once in a while allowing the CPU to run other things that used the real `setTimeout` - Fixes [#1327](https://github.com/jasmine/jasmine/issues/1327) - See [#1334](https://github.com/jasmine/jasmine/issues/1334) - Fixes [jasmine/gulp-jasmine-browser#48](https://github.com/jasmine/gulp-jasmine-browser/issues/48) ------ _Release Notes generated with _[Anchorman](http://github.com/infews/anchorman)_ jasmine-4.5.0/release_notes/2.7.0.md000066400000000000000000000053431432731766000170020ustar00rootroot00000000000000# Jasmine 2.7.0 Release Notes ## Summary This release contains a number of fixes and pull requests. ## Pull Requests & Issues * Add class UserContext - Merges [#1400](https://github.com/jasmine/jasmine/issues/1400) from @darthjee * Send unfocused tests through the same queue as focused tests - Merges [#1399](https://github.com/jasmine/jasmine/issues/1399) from @jberney * PrettyPrinter allows an object to have a `toString` that isn't a function - Fixes [#1389](https://github.com/jasmine/jasmine/issues/1389) * Fix rounding in toBeCloseTo - Fixes [#1382](https://github.com/jasmine/jasmine/issues/1382) * When stop on failure is enabled, skip subsequent it() and beforeEach(). Note: afterEach() functions are still run, because skipping them is highly likely to pollute specs that run after the failure. - Fixes [#577](https://github.com/jasmine/jasmine/issues/577) - Fixes [#807](https://github.com/jasmine/jasmine/issues/807) * Only clear out the `spec` param for Run all link - Fixes [#1369](https://github.com/jasmine/jasmine/issues/1369) * Pretty printer will now use MAX_PRETTY_PRINT_ARRAY_LENGTH for objects - Fixes [#1291](https://github.com/jasmine/jasmine/issues/1291) - Fixes [#1360](https://github.com/jasmine/jasmine/issues/1360) * updated package glob from 7.0.5 to 7.1.2 - Merges [#1368](https://github.com/jasmine/jasmine/issues/1368) from @EsrefDurna * Fix bower.json url to be https instead of http - Merges [#1365](https://github.com/jasmine/jasmine/issues/1365) from @kant * Fail when one of the arguments passed into toBeCloseTo matcher is null - Merges [#1362](https://github.com/jasmine/jasmine/issues/1362) from @beatrichartz * Fixed HTML snippet in README - Closes [#1366](https://github.com/jasmine/jasmine/issues/1366). * Report the random seed at the beginning and end of execution. This allows reporters to provide the seed to the user even in cases where Jasmine crashes before completing. - Merges [#1348](https://github.com/jasmine/jasmine/issues/1348) from @sgravrock * Add ES6 map support to Jasmine - Merges [#1340](https://github.com/jasmine/jasmine/issues/1340) from @rmehlinger - Fixes [#1257](https://github.com/jasmine/jasmine/issues/1257) * Added support for async before/it/after functions that return promises and added support for ES2017 async functions - Merges [#1356](https://github.com/jasmine/jasmine/issues/1356) from @sgravrock - Fixes [#1336](https://github.com/jasmine/jasmine/issues/1336) - Fixes [#1270](https://github.com/jasmine/jasmine/issues/1270) - Fixes [#1350](https://github.com/jasmine/jasmine/issues/1350) - Fixes [#1320](https://github.com/jasmine/jasmine/issues/1320) ------ _Release Notes generated with _[Anchorman](http://github.com/infews/anchorman)_ jasmine-4.5.0/release_notes/2.8.0.md000066400000000000000000000034321432731766000170000ustar00rootroot00000000000000# Jasmine 2.8.0 Release Notes ## Summary This release contains a number of fixes and pull requests. 2.8 should be the last 2.x release of Jasmine, as we aim for 3.0. ## Changes * Create CODE_OF_CONDUCT.md * Add jsdocs for reporter events * Update jsApiReporter docs to link to new suite and spec results * Add explicit docs for the callback function passed to `it` etc. * Update afterAll documentation ## Pull Requests & Issues * Add 'nothing' matcher and tests - Merges [#1412](https://github.com/jasmine/jasmine/issues/1412) from @ksvitkovsky - Fixes [#1221](https://github.com/jasmine/jasmine/issues/1221) * Make toEqual matcher report the difference between array elements when arrays have different length - Closes [#1375](https://github.com/jasmine/jasmine/issues/1375) from @kiewic * Add arrayWithExactContents asymmetric matcher - Fixes [#817](https://github.com/jasmine/jasmine/issues/817) * Ensure no message added on asym. match success - Closes [#1408](https://github.com/jasmine/jasmine/issues/1408) from @ksvitkovsky - Fixes [#1388](https://github.com/jasmine/jasmine/issues/1388) * Better primitives detection for saveArgsByValue - Closes [#1407](https://github.com/jasmine/jasmine/issues/1407) from @ksvitkovsky - Fixes [#1403](https://github.com/jasmine/jasmine/issues/1403) * Better pretty printing for typed arrays - Closes [#1404](https://github.com/jasmine/jasmine/issues/1404) from @ksvitkovsky - Fixes [#1180](https://github.com/jasmine/jasmine/issues/1180) * Rewrite ES6 Set and Map comparison to ignore insertion order - Merges [#1406](https://github.com/jasmine/jasmine/issues/1406) from @theefer - Fixes [#1402](https://github.com/jasmine/jasmine/issues/1402) ------ _Release Notes generated with _[Anchorman](http://github.com/infews/anchorman)_ jasmine-4.5.0/release_notes/2.9.0.md000066400000000000000000000070121432731766000167770ustar00rootroot00000000000000# Jasmine 2.9 Release Notes ## Summary This release contains a number of fixes and pull requests. ## Changes * Fixed DelayedFunctionScheduler IE 8 compatibility issue * Fixed SPEC HAS NO EXPECTATIONS warning in HTML reporter * Correctly remove spies of window.onerror on IE * Truncate pretty printer output that is more than j$.MAX_PRETTY_PRINT_CHARS long * Reduced pretty printer limits to much smaller values * Update contributing for new naming of `jasmineUnderTest` * Allowed async functions to be passed into spy#callFake ## Pull Requests & Issues * Added complete support for Set also for IE11. - Merges [#1478](https://github.com/jasmine/jasmine/issues/1478) from @Volox - Fixes [#1355](https://github.com/jasmine/jasmine/issues/1355) * Added complete support for Map also for IE11. - Merges [#1477](https://github.com/jasmine/jasmine/issues/1477) from @Volox - Fixes [#1472](https://github.com/jasmine/jasmine/issues/1472) * Use timeout objects when in node - Merges [#1470](https://github.com/jasmine/jasmine/issues/1470) from @chris--young - Fixes [#1469](https://github.com/jasmine/jasmine/issues/1469) * Fixed `pending()` for `async`/promise-returning specs - Fixes [#1449](https://github.com/jasmine/jasmine/issues/1449) - Fixes [#1450](https://github.com/jasmine/jasmine/issues/1450) * Added test steps for other major node versions - Merges [#1448](https://github.com/jasmine/jasmine/issues/1448) from @mrlannigan * Fix equality computation for ES6 Sets. - Merges [#1445](https://github.com/jasmine/jasmine/issues/1445) from @b-3-n - Fixes [#1444](https://github.com/jasmine/jasmine/issues/1444) * Add instruction to sync local master with upstream - Merges [#1440](https://github.com/jasmine/jasmine/issues/1440) from aaronang * Add some unit tests that exercise jasmine.anything() and Map matching. - Merges [#1437](https://github.com/jasmine/jasmine/issues/1437) from @voithos * Add special handling of asymmetric matcher objects as keys in Maps. - Merges [#1436](https://github.com/jasmine/jasmine/issues/1436) from @voithos - Fixes [#1432](https://github.com/jasmine/jasmine/issues/1432) * Add support for jasmine.any(Symbol). - Merge [#1435](https://github.com/jasmine/jasmine/issues/1435) from @voithos - Fixes [#1431](https://github.com/jasmine/jasmine/issues/1431) * Throw an error for invalid nesting of a suite functions - Merges [#1411](https://github.com/jasmine/jasmine/issues/1411) from @ksvitkovsky - Fixes [#1295](https://github.com/jasmine/jasmine/issues/1295) * Deep clone args before passing them to reporters - Merges [#1424](https://github.com/jasmine/jasmine/issues/1424) from @aj-dev * Fix "Before Committing" section of CONTRIBUTING.md. - Merges [#1429](https://github.com/jasmine/jasmine/issues/1429) from @voithos * Fix lint warning in CallTracker. - Merges [#1428](https://github.com/jasmine/jasmine/issues/1428) from @voithos * clearTimeout should now correctly clear a timeout that is also scheduled to run at the same tick. - Merges [#1427](https://github.com/jasmine/jasmine/issues/1427) from @leahciMic - Fixes [#1426](https://github.com/jasmine/jasmine/issues/1426) * Add a note about `defineProperty` for `spyOnProperty` - Fixes [#1415](https://github.com/jasmine/jasmine/issues/1415) * Add Promise checking to eq - Merges [#1386](https://github.com/jasmine/jasmine/issues/1386) from @sderickson - Fixes [#1314](https://github.com/jasmine/jasmine/issues/1314) ------ _Release Notes generated with _[Anchorman](http://github.com/infews/anchorman)_ jasmine-4.5.0/release_notes/2.9.1.md000066400000000000000000000004701432731766000170010ustar00rootroot00000000000000# Jasmine Core 2.9.1 Release Notes ## Summary This is a hotfix release to fix a breaking change from the 2.9.0 release ## Changes * Clear timeouts when starting to process a milli instead of at the end - Fixes #1482 ------ _Release Notes generated with _[Anchorman](http://github.com/infews/anchorman)_ jasmine-4.5.0/release_notes/2.99.md000066400000000000000000000015231432731766000167330ustar00rootroot00000000000000# Jasmine-Core 2.99 Release Notes ## Summary This release is part of the upgrade path to Jasmine 3.0. It deprecates some functionality that will change. ## Changes * Add ability to report deprecation warnings from within the suite and display them in the HTML reporter * Add deprecation messages for things that will change/break in 3.0 * * done for async functionality will now add a failure if it is invoked with an Error * * Env.catchExceptions and the query param are going away, in favor of a more fully functional fail fast handler * * jasmine.Any(Object) will no longer match null * * Unhandled errors during suite load will be caught and reported as failures by Jasmine * * Calling execute more than once on the same spec will definitely fail in 3.0 ------ _Release Notes generated with _[Anchorman](http://github.com/infews/anchorman)_ jasmine-4.5.0/release_notes/20.md000066400000000000000000000267371432731766000165710ustar00rootroot00000000000000# Jasmine Core 2.0 Release Notes ## Summary These notes are for Jasmine Core 2.0. ## Breaking Changes The [`introduction.js`][intro] page covers the current syntax, highlighting the changes. Here are the known interface changes that are not backwards compatible with 1.x. * New syntax for asynchronous specs * New syntax for spies * New interface for reporters * Better Equality testing * Replaced custom matchers for ease of use * Change to `toThrow` matcher * Clock now remains installed when a spec finishes * More Jasmine internal variables/functions have been moved into closures ### New syntax for asynchronous specs Similar to [Mocha][mocha], Jasmine `before`s, `spec`s, and `after`s can take an optional `done` callback in order to force asynchronous tests. The next function, whether it's a `before`, `spec` or `after`, will wait until this function is called or until a timeout is reached. ### New syntax for spies Spies have a slightly modified syntax. The idea came from a desire to preserve any of the properties on a spied-upon function and some better testing patterns. ### New interface for reporters The reporter interface has been **replaced**. The callbacks are different and more consistent. The objects passed in should only provide what is needed to report results. This enforces an interface to result data so custom reporters will be less coupled to the Jasmine implementation. The Jasmine reporter API now includes a slot for a `timer` object. ### Better Equality testing We removed the previous equality code and are now using new code for testing equality. We started with [Underscore.js][underscore]'s `isEqual`, refactored a bit and added some additional tests. ### Replaced custom matchers for ease of use The interface for adding custom matchers has been **replaced**. It has always been possible to add custom matchers, but the API was barely documented and difficult to test. We've changed how matchers are added and tested. Jasmine adds its own matchers by the same mechanism that custom matchers use. Dogfooding FTW. ### Change to `toThrow` matcher We've changed the behavior of the `toThrow` matcher, moving some functionality to the `toThrowError` matcher. This should allow more of the requested use cases. ### Clock now remains installed when a spec finishes After installing the Jasmine Clock, it will stay installed until `uninstall` is called -- clearing up any ambiguity for when those timing functions will revert to using the global clock object. ## More Jasmine internal variables/functions have been moved into closures Certain variables/functions like a function to get the next spec id have been moved into closures, making the Jasmine interface cleaner. ## Other Changes * Massive refactoring and better testing * Environment setup now in `boot.js` * Development and Build moved to Grunt * Changes to how Jasmine is loaded * Changes to how Jasmine is tested * Better node.js support * Better Continuous Integration Environment at Travis * Support matrix updated * Removed JsDoc Pages * Adding Code Climate for JavaScript ## Massive refactoring and better testing This is the biggest set of changes. We've touched nearly every file and every object. We've merged objects together and factored out code. We styled the code more consistently. We've improved nearly every test. In general, Jasmine is made of smaller, more-loosely-coupled objects, unit-tested with explicit dependencies injected. This made tests easier to read, write, and maintain. We know this has made Jasmine development easier for the core team. We expect (and hope) this makes it easier for the community to extend Jasmine and provide pull requests that make more sense the first time out. ## Environment setup now in `boot.js` Instantiation and setup of the Jasmine environment, including building reporters, exposing the "global" functions, and executing tests has moved into its own file: `boot.js`. This should make it easier to add custom reporters, configure some objects, or just in general change how you use Jasmine from the outside. For example, during development, Jasmine uses its own `devboot.js` to load itself twice - once from `jasmine.js` and once from the source directories. ## Development and Build moved to Grunt We've moved away from Ruby and embraced [Node.js][node] and [Grunt.js][grunt] for the various command line tasks during development. Yes, it's a just a different set of dependencies. But it's less code for the team to maintain - it turns out that JavaScript tools are pretty good at building JavaScript projects. This will make it easier for the community to make sure contributions work in browsers and in Node.js before submitting Pull Requests. There is more detail in the [Contributor's Guide][contrib]. ## Changes to how Jasmine is loaded We did not want to add new run-time dependencies, yet we needed to be cleaner when loading Jasmine. So we wrote a custom "require" scheme that works in Node.js and in browsers. This only affects pull requests which add files - please be careful in these cases. Again, the [Contributor's Guide][contrib] should help. ## Changes to how Jasmine is tested with Jasmine Writing a custom require system helped enforce self-testing - the built `jasmine.js` testing Jasmine from the source directories. Overall this has improved the stability of the code. When you look at Jasmine's tests, you'll see both `jasmine` and `j$` used. The former, `jasmine`, will always be used to test the code from source, which is loaded into the reference `j$`. Please adhere to this pattern when writing tests for contributions. ## Better node.js support `Node.js` is now officially a first-class citizen. For a long time we've made sure tests were green in Node before releasing. But it is now officially part of Jasmine's CI build at [Travis][travis]. For the curious, the [`node_suite.js`][node_suite], is essentially a `boot.js` for Node. An official `npm` is coming. ## Better Continuous Integration Environment at Travis The [CI build at Travis][travis_jasmine] now runs the core specs in a build matrix across browsers. It's far from complete on the operating system matrix, but you will see that Jasmine runs against: Firefox, Chrome, Safari 5, Safari 6, [Phantom.js][phantom], [Node.js][node], and IE versions 8, 9, and 10. Big thanks to [SauceLabs][sauce] for their support of open source projects. We will happily take pull requests for additional OS/Browser combos within the matrix. ## Support Matrix Updated We're dropping support for IE < 8. [Jasmine 1.x][jasmine_downloads] remains for projects that need to support older browsers. ## Removed JsDoc Pages Comments in code are lies waiting to happen. Jasmine's JsDoc comments were no exception. The comments were out of date, the generated pages were even more out of date, and frankly they were not helpful. So they're gone. Last year saw the posting of the [`introduction.js`][intro] page to document the real, practical interface for projects to use. This page has received a lot of positive feedback so expect more pages like this one. ## Adding Code Climate for JavaScript We are running Code Climate for Jasmine. We have some work to do here but it's helping us easily find code hotspots. ## Pull Requests and Issues The following Pull Requests were merged: * ObjectContaining wrong filed value error message #[394](https://github.com/pivotal/jasmine/issues/394) from albertandrejev * Removed unnecessary parameter from `suiteFactory()` call #[397](https://github.com/pivotal/jasmine/issues/397) from valera-rozuvan * `jasmine.Any` supports `Boolean` #[392](https://github.com/pivotal/jasmine/issues/392) from albertandrejev * Reporters get execution time #[30](https://github.com/pivotal/jasmine/issues/30) * `toThrow` matchers handle falsy exceptions #[317](https://github.com/pivotal/jasmine/issues/371) * Removed deprecated `jasmine.Matchers.pp` #[363](https://github.com/pivotal/jasmine/issues/363) from robinboehm * Fix for Clock ticking to default to 0 #[340](https://github.com/pivotal/jasmine/issues/340) from Caio Cunha * Whitespace failures should be easier to understand #[332](https://github.com/pivotal/jasmine/issues/332) from bjornblomqvist * Fix for more markdown-y image for Build status #[329](https://github.com/pivotal/jasmine/issues/329) from sunliwen * UTF-8 encoding fixes #[333](https://github.com/pivotal/jasmine/issues/333) from bjornblomqvist * Replaced deprecated octal literal with hexadecimal from kris7t * Make getGlobal() work in strict mode from metaweta * Clears timeout timer even when async spec throws an exception from tidoust * Timeouts scheduled within a delayed function are correctly scheduled and executed from maciej-filip-sz ### Bug Fixes * Improved the performance of the HTML output with a CSS change #[428](https://github.com/pivotal/jasmine/issues/428) - Thanks @tjgrathwell * Removed an accidental global pollution of `j$` as a reference to Jasmine. Thanks to Morten Maxild from the mailing list * There is now a consistent `this` between `beforeEach`, `it` and `afterEach` for a spec * A spy's strategy now has properties `returnValue` and `throwError` because they are better names * Make it easy to copy the title of failing specs from the HTML output * Don't add periods to the full name of a spec fix #[427](https://github.com/pivotal/jasmine/issues/427) * Allow Env to take optional spec/suite ids when asked to `execute` * [Mock clock now less intrusive, replacing global timer functions only when clock is installed](http://www.pivotaltracker.com/story/54168708) * Restore custom failure messages for `toHaveBeenCalledWith` * Jasmine global object has a addCustomEqualityTester and addMatchers (no longer directly on global) * Fixed a global leak of `timer` * Remove currentRunner from Env (users can use topSuite from Env instead) * [Specs without expectations are now considered passing](http://www.pivotaltracker.com/story/59422744) * Improve error message when a spec does not call the async callback within the default time interval * Allow passing a negativeCompare in a custom matcher for more custom implementations when `.not` is called * Update favicon to be higher resolution * Make all async functions be subject to the timeout There were several other pull requests that either had already been fixed, or were good starting points for the various changes above. Thank you for all of the hard work to keep Jasmine awesome. ## Other Bugs and Features There were a few small changes and fixes that didn't fit into any of the above categories: * HTML Reporter refactored for simplicity and performance * Default character encoding on the HTML runner page is UTF-8 * [Escape special regex characters from the spec param](http://www.pivotaltracker.com/story/52731407) * Favicon returns * [Clock supports `eval`'d strings as functions](http://www.pivotaltracker.com/story/40853563) * There should always be stack traces on failures * Removed references to unused `jasmine.VERBOSE` * Removed references to unused `jasmine.XmlHttpRequest` [mocha]: http://visionmedia.github.io/mocha/ [underscore]: http://underscorejs.org/ [grunt]: http://gruntjs.com [node]: http://nodejs.org [phantom]: http://phantomjs.org [jasmine_downloads]: https://github.com/pivotal/jasmine/downloads [contrib]: https://github.com/pivotal/jasmine/blob/master/CONTRIBUTING.md [travis]: http://travis-ci.org [travis_jasmine]: http://travis-ci.org/jasmine [sauce]: http://saucelabs.com [node_suite]: https://github.com/pivotal/jasmine/blob/master/spec/node_suite.js [intro]: http://jasmine.github.io/2.0/introduction.html ------ _Release Notes generated with [Anchorman](http://github.com/infews/anchorman)_ jasmine-4.5.0/release_notes/3.0.md000066400000000000000000000060311432731766000166310ustar00rootroot00000000000000# Jasmine-Core 3.0 Release Notes ## Summary Jasmine 3.0 is a major release of Jasmine, and as such includes some breaking changes in addition to various new features. There is also a 2.99 release of Jasmine that will present deprecation warnings for suites that will encounter different behavior in 3.0. ## Breaking Changes * Replace old "catch exceptions" logic with proper fail fast with error reporting - Fixes [#414](https://github.com/jasmine/jasmine/issues/414) - Fixes [jasmine/jasmine-npm#16](https://github.com/jasmine/jasmine-npm/issues/16) * Detect an Error passed to `done` and add an expectation failure - Fixes [#567](https://github.com/jasmine/jasmine/issues/567) * Unify status for xdescribe and xit - Ensure *All's only execute if at least one child will run - Specs will report a status of `excluded` instead of disabled - Fixes [#1418](https://github.com/jasmine/jasmine/issues/1418) * Suite level errors all report the same way (on suiteDone) * Refactor QueueRunner and remove references to functions that Jasmine is done with * expect(null).toEqual(jasmine.any(Object)) no longer passes - Fixes [#1255](https://github.com/jasmine/jasmine/issues/1255) * Default to running tests in random order * The `identity` of a Jasmnine Spy is now a property and no longer a method * Additionally, Jasmine 3.0 drops support for older browsers and environments. Notably: - Internet Explorer 8 and 9 - Ruby 1.x (for the Ruby gem) - Rails 3.x (for the Ruby gem) - Python 2.x (for the Python wheel) - Nodejs 0.x (for the NPM package) ## Changes * Remove node modules from python wheel, and update languages * Allow reporter callbacks to be asynchronous - Fixes [#842](https://github.com/jasmine/jasmine/issues/842) * Allow adding custom spy strategies * Add the ability to specify the strategy to use for a spy based on which parameters are passed * Added links to re-run the suites containing a failing spec * Added a toHaveClass matcher * More informative pretty-printing of DOM elements * Allow jasmine-npm to handle its own load errors * Treat random= as a no-op rather than disabling randomization * Use prototype for spy strategy for better memory management * Remove console.js altogether * Add safari 10 and update readme to include edge * Determine overall status in core, not reporters * Filter Jasmine frames from stack traces * Treat afterAll errors at any level as failures * Improved reporting of load errors and afterAll errors - Pass file and line number to reporters when present - Show file and line number in the HTML reporter when present - Visually separate adjacent errors in the HTML reporter * Report loading errors as loading errors, not afterAll errors * HTML reporter reports overall failure if there are any global errors * Fail if error events (e.g. syntax errors) occur during loading * Allow use of a predicate function to validate thrown exceptions * Check truthiness of toThrowError args, not arg count ------ _Release Notes generated with _[Anchorman](http://github.com/infews/anchorman)_ jasmine-4.5.0/release_notes/3.1.0.md000066400000000000000000000036051432731766000167740ustar00rootroot00000000000000# Jasmine-Core 3.1 Release Notes ## Summary This release contains a number of fixes and pull requests ## Pull Requests and Issues * Display error properties for failed specs - Merges [#1516](https://github.com/jasmine/jasmine/issues/1516) from @jbunton-atlassian * Allow node to report load time errors - Fixes [#1519](https://github.com/jasmine/jasmine/issues/1519) * Fixing missing semi-colons - Merges [#1512](https://github.com/jasmine/jasmine/issues/1512) from @Sylhare * Fixed release notes link * Added matchers: truthy, falsy, empty and notEmpty - Merges [#1460](https://github.com/jasmine/jasmine/issues/1460) from @sjolicoeur * Add API docs for async reporters * Return for functions that have no actual words between keyword and ( - Also fixes a potential catastrophic backtracking if someone has severely damaged their own `toString` during test execution. * Moved toHaveClass matcher into core so that it can be used in Karma - Fixes [#1503](https://github.com/jasmine/jasmine/issues/1503) * allow adding a deprecation object - Merges [#1498](https://github.com/jasmine/jasmine/issues/1498) from @UziTech * Add CodeTriage badge to jasmine/jasmine - Merges [#1505](https://github.com/jasmine/jasmine/issues/1505) from @codetriage-readme-bot * Resolve merge conflict - Merges [#1501](https://github.com/jasmine/jasmine/issues/1501) from @aptx4869 - Fixes [#1500](https://github.com/jasmine/jasmine/issues/1500) * Fix release note typo - Merges [#1499](https://github.com/jasmine/jasmine/issues/1499) @bcaudan * Only show deprecation for catch exceptions if you tell Jasmine not to catch - Fixes [#1497](https://github.com/jasmine/jasmine/issues/1497) * Add notes for environments that have lost support - Fixes [#1495](https://github.com/jasmine/jasmine/issues/1495) ------ _Release Notes generated with _[Anchorman](http://github.com/infews/anchorman)_ jasmine-4.5.0/release_notes/3.10.0.md000066400000000000000000000032321432731766000170500ustar00rootroot00000000000000# Jasmine Core 3.10 Release Notes ## New features and bug fixes * Added support for running Jasmine multiple times * If the env is configured with `autoCleanClosures: false`, then it can be executed repeatedly. * Merges #1934 from @nicojs * Fixes #1925 * Improved error message when an async expectation occurs after the spec finishes * Merges #1937 from @AndreWillomitzer * Fixes #1854 * Reject timeout values that are too large for setTimeout * See #1930 * Don't immediately move to the next queueable fn on async error This allows assertion failures and other errors that occur after the async error to be routed to the correct spec/suite. * Added a stringContaining asymmetric equality tester * Fixes #1923. * The jasmine-core Ruby gem now prints a deprecation message when loaded unless the SUPPRESS_JASMINE_DEPRECATION environment variable is set. ## Documentation updates * Added discussion of max timeout value to jsdocs * Merges #1931 from @trusktr * Added missing @since annotations * Improved jsdocs for asymmetric equality testers * Added a deprecation notice to the jasmine-core Ruby gem's description ## Supported environments jasmine-core 3.10.0 has been tested in the following environments. | Environment | Supported versions | |-------------------|--------------------| | Node | 10, 12, 14, 16 | | Safari | 8-14 | | Chrome | 94 | | Firefox | 93, 78, 68 | | Edge | 94 | | Internet Explorer | 10, 11 | ------ _Release Notes generated with _[Anchorman](http://github.com/infews/anchorman)_ jasmine-4.5.0/release_notes/3.10.1.md000066400000000000000000000005251432731766000170530ustar00rootroot00000000000000# Jasmine Core 3.10.1 Release Notes ## Bugfixes * Fixed missing pendingReason in pending spec results * Fixes [#1939](https://github.com/jasmine/jasmine/issues/1939) * Merges [#1940](https://github.com/jasmine/jasmine/pull/1940) from @jan-molak ------ _Release Notes generated with _[Anchorman](http://github.com/infews/anchorman)_ jasmine-4.5.0/release_notes/3.2.0.md000066400000000000000000000044741432731766000170020ustar00rootroot00000000000000# Jasmine-Core 3.2 Release Notes ## Summary This release contains a number of fixes and pull requests ## Changes * Add spyOnAllFunctions function - Merges [#1581](https://github.com/jasmine/jasmine/issues/1581) from @aeisenberg - Fixes [#1421](https://github.com/jasmine/jasmine/issues/1421) * Improve timeout error message - Merges [#1567](https://github.com/jasmine/jasmine/issues/1567) from @ikonst * Fix JSDoc naming for Env functions - See [#1565](https://github.com/jasmine/jasmine/issues/1565) * Add documentation for more public functions on Env - Fixes [#1565](https://github.com/jasmine/jasmine/issues/1565) * Added a basic set of async matchers - Fixes [#1447](https://github.com/jasmine/jasmine/issues/1447) - Fixes [#1547](https://github.com/jasmine/jasmine/issues/1547) * Properly cascade StopExecutionError's up the tree - Fixes [#1563](https://github.com/jasmine/jasmine/issues/1563) * Implemented hiding of disabled specs - Merges [#1561](https://github.com/jasmine/jasmine/issues/1561) from @SamFare * Line-break long expectation failure messages - See [#296](https://github.com/jasmine/jasmine/issues/296) * Better detection of DOM Nodes for equality - Fixes [#1172](https://github.com/jasmine/jasmine/issues/1172) * Fix typo from `incimplete` to `incomplete` - Merges [#1555](https://github.com/jasmine/jasmine/issues/1555) from @yinm * Allow omitting the name argument: `createSpy(func)` - Merges [#1551](https://github.com/jasmine/jasmine/issues/1551) from @riophae * name new global status stuff correctly in API docs * Check for accidental global variable creation * Fixed global variable leak - Fixes [#1534](https://github.com/jasmine/jasmine/issues/1534) * Correctly format stack traces for errors with multiline messages - Fixes [#1526](https://github.com/jasmine/jasmine/issues/1526) * Change message for extra elements at end of actual array - Merges [#1527](https://github.com/jasmine/jasmine/issues/1527) from @majidmade - Fixes [#1485](https://github.com/jasmine/jasmine/issues/1485) * Report unhandled rejections as globalErrors. - Merges [#1521](https://github.com/jasmine/jasmine/issues/1521) from @johnjbarton * add some links to more tutorials from the api docs ------ _Release Notes generated with _[Anchorman](http://github.com/infews/anchorman)_ jasmine-4.5.0/release_notes/3.2.1.md000066400000000000000000000003611432731766000167720ustar00rootroot00000000000000# Jasmine-Core 3.2.1 Release Notes ## Changes * Correctly expose `spyOnAllFunctions` - See [#1581](https://github.com/jasmine/jasmine/issues/1581) ------ _Release Notes generated with _[Anchorman](http://github.com/infews/anchorman)_ jasmine-4.5.0/release_notes/3.3.0.md000066400000000000000000000036631432731766000170020ustar00rootroot00000000000000# Jasmine Core 3.3 Release Notes ## Summary This release includes a new way to configure Jasmine, the ability to provide additional context with your expectations, and other things ## Changes * Added expect().withContext() to provide additional information in failure messages * Implement `withContext` for async expectations too - Fixes [#641](https://github.com/jasmine/jasmine/issues/641) * New asynchronous matcher `toBeRejectedWith` - Merges [#1615](https://github.com/jasmine/jasmine/issues/1615) from @codymikol - Closes [#1600](https://github.com/jasmine/jasmine/issues/1600) - Fixes [#1595](https://github.com/jasmine/jasmine/issues/1595) * Show a tip for `toBe` failures for how to get deep equality - Merges [#1616](https://github.com/jasmine/jasmine/issues/1616) from @tdurtshi - Fixes [#1614](https://github.com/jasmine/jasmine/issues/1614) * `expectAsync` now works with non-native promises - Merges [#1613](https://github.com/jasmine/jasmine/issues/1613) from @codymikol - Fixes [#1612](https://github.com/jasmine/jasmine/issues/1612) * Show status marks next to spec description in HTML reporter - Merges [#1610](https://github.com/jasmine/jasmine/issues/1610) from @m1010j - Fixes [#1596](https://github.com/jasmine/jasmine/issues/1596) * Show error messages for `Error`s without a name - Merges [#1601](https://github.com/jasmine/jasmine/issues/1601) from @nitobuendia - Fixes [#1594](https://github.com/jasmine/jasmine/issues/1594) * Optimized clearTimeout cpu usage - Merges [#1599](https://github.com/jasmine/jasmine/issues/1599) from @Havunen * Introduce a configuration object to `Env` deprecating old single use functions - [finishes #159158038](http://www.pivotaltracker.com/story/159158038) * Specify https for github urls in package.json - Merges [#1597](https://github.com/jasmine/jasmine/issues/1597) @limonte ------ _Release Notes generated with _[Anchorman](http://github.com/infews/anchorman)_ jasmine-4.5.0/release_notes/3.4.0.md000066400000000000000000000041021432731766000167700ustar00rootroot00000000000000# Jasmine Core 3.4 Release Notes ## Summary This is a maintenance release of Jasmine with a number of new features and fixes ## Changes * Handle WebSocket events in IE when detecting Errors - Fixes [#1623](https://github.com/jasmine/jasmine/issues/1623) * bump dependencies for security fixes - Merges [#1672](https://github.com/jasmine/jasmine/issues/1672) from @wood1986 * Make node execution default and override for browsers - Merges [#1658](https://github.com/jasmine/jasmine/issues/1658) from @wood1986 - Fixes [#883](https://github.com/jasmine/jasmine/issues/883) * feat(result.duration): report test duration in ms - Merges [#1660](https://github.com/jasmine/jasmine/issues/1660) from @johnjbarton - Fixes [#1646](https://github.com/jasmine/jasmine/issues/1646) * refactor(Timer): share htmlReporter noopTimer via Timer.js - Merges [#1669](https://github.com/jasmine/jasmine/issues/1669) from @johnjbarton * Fix various typos - Merges [#1666](https://github.com/jasmine/jasmine/issues/1666) from @FelixRilling - Merges [#1667](https://github.com/jasmine/jasmine/issues/1667) from @FelixRilling - Merges [#1665](https://github.com/jasmine/jasmine/issues/1665) from @FelixRilling - Merges [#1664](https://github.com/jasmine/jasmine/issues/1664) from @FelixRilling - Fixes [#1663](https://github.com/jasmine/jasmine/issues/1663) * When catching a global error in Node.js, print the type of error - Merges [#1632](https://github.com/jasmine/jasmine/issues/1632) from @jbunton-atlassian * Support Error.stack in globalErrors. - Merges [#1644](https://github.com/jasmine/jasmine/issues/1644) from @johnjbarton * Stop treating objects with a `nodeType` as if they are DOM Nodes - Fixes [#1638](https://github.com/jasmine/jasmine/issues/1638) * Fixes issue where PhantomJS 2 and IE 10 - 11 crashed when reporting SVG element equality - Merges [#1621](https://github.com/jasmine/jasmine/issues/1621) from @Havunen - Fixes [#1618](https://github.com/jasmine/jasmine/issues/1618) ------ _Release Notes generated with _[Anchorman](http://github.com/infews/anchorman)_ jasmine-4.5.0/release_notes/3.5.0.md000066400000000000000000000163321432731766000170010ustar00rootroot00000000000000# Jasmine Core 3.5 Release Notes ## Summary This is a maintenance release of Jasmine with a number of new features and fixes ### Highlights * The output of toHaveBeenCalledWith should now be more readable This breaks each call out onto its own line, so that it's much easier to see where each call starts and how they differ. E.g. previously the output would be: Expected spy foo to have been called with [ 'bar', 'baz', 'qux' ] but actual calls were [ [ 42, 'wibble' ], [ 'bar' 'qux' ], [ 'grault '] ] Now it's: Expected spy foo to have been called with: [ 'bar', 'baz', 'qux' ] but actual calls were: [ 42, 'wibble' ], [ 'bar' 'qux' ], [ 'grault ']. * Add new spy strategies to resolve and reject Promises `resolveTo` and `rejectWith` * Add the ability to have custom async matchers ### Internal notes * Stop testing against PhantomJS * PhantomJS is at end of life, and the last version of Selenium that supported it was 3.6.0, released almost three years ago. We can't test Jasmine against PhantomJS without pinning key pieces of the project to increasingly outdated versions of key libraries. * Fail Jasmine's CI build if the promise returned from `jasmineBrowser.runSpecs` is rejected * A bunch of other rejiggering of the Travis-CI builds to make them easier to work with * Also released a new browser runner that is being used by Jasmine * See [jasmine-browser-runner](https://github.com/jasmine/jasmine-browser) * This is a first pass at getting this to work for other projects as well. Please try it out and let us know what isn't working for you. * add prettier and eslint ## All Changes * Adds new configuration option to failSpecWithNoExpectations that will report specs without expectations as failures if enabled * Merges [#1743](https://github.com/jasmine/jasmine/issues/1743) from @dtychshenko * Fixes [#1740](https://github.com/jasmine/jasmine/issues/1740) * Correctly propagate the `Error` object caught by the global error handler to reporters, etc. - Merges [#1738](https://github.com/jasmine/jasmine/issues/1738) from @prantlf - Fixes [#1728](https://github.com/jasmine/jasmine/issues/1728) * Show argument diffs in toHaveBeenCalledWith failure messages * Fixes [#1641](https://github.com/jasmine/jasmine/issues/1641) * Updated arrayContaining to require actual values to be arrays * Merges [#1746](https://github.com/jasmine/jasmine/issues/1746) from @divido * Fixes [#1745](https://github.com/jasmine/jasmine/issues/1745) * Add the ability to have custom async matchers * Merges [#1732](https://github.com/jasmine/jasmine/issues/1732) from @UziTech * Allow users to set a default spy strategy - Merges [#1716](https://github.com/jasmine/jasmine/issues/1716) from @elliot-nelson * Accept configurations with `Promise: undefined`. * PrettyPrinter survives if objects throw in toString - Merges [#1718](https://github.com/jasmine/jasmine/issues/1718) from @johnjbarton - Fixes [#1726](https://github.com/jasmine/jasmine/issues/1726) * Add `mapContaining` and `setContaining` asymmetric matchers * Merges [#1741](https://github.com/jasmine/jasmine/issues/1741) from @eventlistener * Use the same spec file pattern for both node and browser * Gemspec: Drop EOL'd property rubyforge_project * Merges [#1736](https://github.com/jasmine/jasmine/issues/1736) from @olleolleolle * Updated async timeout message to include all of the ways that async code can be run in Jasmine * Allow users to pass property names to createSpyObj - Merges [#1722](https://github.com/jasmine/jasmine/issues/1722) from @elliot-nelson - Closes [#1569](https://github.com/jasmine/jasmine/issues/1569) - Fixes [#1442](https://github.com/jasmine/jasmine/issues/1442) * don't attempt to normalize PNGs (gitattributes) - Merges [#1721](https://github.com/jasmine/jasmine/issues/1721) from @elliot-nelson * Add `@since` to most JSDoc comments - See [jasmine/jasmine.github.io#117](https://github.com/jasmine/jasmine.github.io/issues/117) * Make no expectations in HTML Reporter message a console warning - Fixes [#1704](https://github.com/jasmine/jasmine/issues/1704) * Prevent page overflow in HTML reporter under some situations by setting an explicit width - Merges [#1713](https://github.com/jasmine/jasmine/issues/1713) from @pixelpax * Cleanup: minor dead code removal and style fixes - Merges [#1708](https://github.com/jasmine/jasmine/issues/1708) from @elliot-nelson * Pretty Printer can now handle printing an object whose `toString` function has been spied upon - Merges [#1712](https://github.com/jasmine/jasmine/issues/1712) from @johnjbarton * PrettyPrinter handles objects with invalid toString implementations - Merges [#1711](https://github.com/jasmine/jasmine/issues/1711) from @elliot-nelson - Closes [#1700](https://github.com/jasmine/jasmine/issues/1700) - Closes [#1575](https://github.com/jasmine/jasmine/issues/1575) * Fix toBeCloseTo matcher for Node.js 12 and Chrome 74 - Merges [#1710](https://github.com/jasmine/jasmine/issues/1710) from @paulvanbrenk - Fixes [#1695](https://github.com/jasmine/jasmine/issues/1695) * spyOnProperty jasmine-style error messages with usage note - Merges [#1706](https://github.com/jasmine/jasmine/issues/1706) from @elliot-nelson * spyOnProperty respects the allowRespy flag - Merges [#1705](https://github.com/jasmine/jasmine/issues/1705) from @elliot-nelson * Introduce matchers#toBeInstanceOf - Merges [#1697](https://github.com/jasmine/jasmine/issues/1697) from @elliot-nelson * Print global errors encountered during CI runs - Merges [#1701](https://github.com/jasmine/jasmine/issues/1701) from @elliot-nelson * Update contributing doc based on some of the newer tooling - Fixes [#1702](https://github.com/jasmine/jasmine/issues/1702) * Extend spyOnAllFunctions to include prototype and parent methods - Merges [#1699](https://github.com/jasmine/jasmine/issues/1699) from @elliot-nelson - Fixes [#1677](https://github.com/jasmine/jasmine/issues/1677) * Improve error handling in CI test launcher - Merges [#1598](https://github.com/jasmine/jasmine/issues/1598) from @elliot-nelson * Add new spy strategies to resolve and reject Promises `resolveTo` and `rejectWith` - Merges [#1688](https://github.com/jasmine/jasmine/issues/1688) from @enelson - See [#1590](https://github.com/jasmine/jasmine/issues/1590) - Fixes [#1715](https://github.com/jasmine/jasmine/issues/1715) * Update deprecation messages to indicate _future_ removal - Fixes [#1628](https://github.com/jasmine/jasmine/issues/1628) * Add toBeRejectedWithError matcher - Merges [#1686](https://github.com/jasmine/jasmine/issues/1686) from @megahertz - Fixes [#1625](https://github.com/jasmine/jasmine/issues/1625) * Ignore internal ci.js from npm package - See [#1684](https://github.com/jasmine/jasmine/issues/1684) * Fix failure messages for positive/negative infinity matchers - Fixes [#1674](https://github.com/jasmine/jasmine/issues/1674) * nit: fix typo - Merges [#1680](https://github.com/jasmine/jasmine/issues/1680) from @acinader * added #toBeTrue and #toBeFalse matchers - Merges [#1679](https://github.com/jasmine/jasmine/issues/1679) from @FelixRilling ------ _Release Notes generated with _[Anchorman](http://github.com/infews/anchorman)_ jasmine-4.5.0/release_notes/3.6.0.md000066400000000000000000000132021432731766000167730ustar00rootroot00000000000000# Jasmine Core 3.6 Release Notes ## Summary This is a maintenance release of Jasmine with a number of new features and fixes. ## Highlights * Added support for custom object formatters * Allows customizing how an object is stringified in matcher failure messages * [Tutorial](https://jasmine.github.io/tutorials/custom_object_formatter) * [API reference](https://jasmine.github.io/api/3.6/jasmine.html#.addCustomObjectFormatter) * Don't require matchers and asymmetric equality testers to pass custom object formatters back to Jasmine - Supports custom object formatters. - Makes it easier to write high quality matchers and asymmetric equality testers. - The old API will still work until 4.0. * Properly import jasmineRequire object before using - Improves compatibility with Webpack - Merges [#1766](https://github.com/jasmine/jasmine/pull/1766) from @amilligan * Added a toHaveBeenCalledOnceWith matcher - Merges [#1801](https://github.com/jasmine/jasmine/pull/1801) from @Maximaximum - Fixes [#1717](https://github.com/jasmine/jasmine/issues/1717) * Added a toHaveSize matcher - Merges [#1796](https://github.com/jasmine/jasmine/pull/1796) from @wokier * Added a toBePending async matcher - Merges [#1808](https://github.com/jasmine/jasmine/pull/1808) from @DCtheTall - Fixes [#1803](https://github.com/jasmine/jasmine/issues/1803) * Added support for user-defined spec/suite properties - Allows specs/suites to pass data to custom reporters - Merges [#1763](https://github.com/jasmine/jasmine/pull/1763) from @johnjbarton * Route unhandled promise rejections to onerror - Merges [#1778](https://github.com/jasmine/jasmine/pull/1778) from @johnjbarton - Fixes [#1777](https://github.com/jasmine/jasmine/issues/1777) ## Internal notes * Use a version of eslint that works on Node 8 * Check for syntax and standard library objects that don't work in IE * Run eslint against all files * Add Additional Test for equals Matcher - Merges [#1829](https://github.com/jasmine/jasmine/pull/1829) from @tobiasschweizer - Fixes [#1821](https://github.com/jasmine/jasmine/issues/1821) * Depend on head of jasmine-browser to fix IE failures in CI * Fixed test failure in Firefox 74 * Added test for resolveTo/rejectWith with empty parameters - Merges [#1802](https://github.com/jasmine/jasmine/pull/1802) from @chivesrs * Removed unnecessary uses of new in tests * Realigned the browser testing matrix to match current reality - Use Windows instead of Linux so we can get current browsers from Sauce. - Test against the version of Firefox that corresponds to ESR as well as latest. - Test the latest Edge rather than a specific older version. - Test Safari 8 and 13 instead of 8, 9 and 10. What works in those versions is likely to work in the ones in between. * Don't leak global error handlers between Jasmine's own tests * Added basic property tests for matchersUtil.equals * Added integration tests for existing matcher interfaces * Added integration tests for asymmetric equality testers * Test IE before other browsers on Travis ## Other Changes * Show diffs involving root-level asymmetric equality testers - Fixes [#1831](https://github.com/jasmine/jasmine/issues/1831) * Fixed references to master in docs * Allow spy throwError to throw an Object - Merges [#1822](https://github.com/jasmine/jasmine/pull/1822) from @terencehonles * Added missing periods to README - Merges [#1828](https://github.com/jasmine/jasmine/pull/1828) from @dirkpuge * Expose setSpec/SuiteProperty on interface - Merges [#1820](https://github.com/jasmine/jasmine/pull/1820) from @johnjbarton * Prevent undesired reloads when karma-jasmine-html-reporter is used - Merges [#1807](https://github.com/jasmine/jasmine/pull/1807) from @parloti - Fixes [#1775](https://github.com/jasmine/jasmine/issues/1775) * Correctly report spec and suite duration - Fixes [#1676](https://github.com/jasmine/jasmine/issues/1676). * Added jsdocs for MatchersUtil * Allow the .callThrough spy strategy to call constructor functions without errors - Merges [#1782](https://github.com/jasmine/jasmine/pull/1782) from @enelson - Fixes [#1760](https://github.com/jasmine/jasmine/issues/1760) * Inject a per-runable pretty printer into MatchersUtil - Supports custom object formatters * Include stack traces in unhandled promise rejection messages * Describe the naming for the function it - Merges [#1772](https://github.com/jasmine/jasmine/pull/1772) from @johnlinp * Correctly extract error messages from stack traces that don't start with `Error` - Merges [#1776](https://github.com/jasmine/jasmine/pull/1776) from @vhermannitk - Fixes [#1771](https://github.com/jasmine/jasmine/issues/1771) * Fixed objectContaining to not match when the expected is the empty object and the actual is a non-object * Fixed toEqual(0, Number.MIN_VALUE) to fail instead of passing - Merges [#1764](https://github.com/jasmine/jasmine/pull/1764) from @dubzzz * Fixed comparison between ObjectContaining and non-objects on IE * Provide better diffs for object graphs that include `objectContaining` * Indent multiline failure messages in the output of `withContext` * This makes it easier to see where each failure message begins and ends. * Report async expectations that complete after the runable completes - See [#1752](https://github.com/jasmine/jasmine/issues/1752). * Treat NodeJS assertion failures as expectation failures - Merges [#1678](https://github.com/jasmine/jasmine/pull/1678) from @apla ------ _Release Notes generated with _[Anchorman](http://github.com/infews/anchorman)_ jasmine-4.5.0/release_notes/3.7.0.md000066400000000000000000000051701432731766000170010ustar00rootroot00000000000000# Jasmine Core 3.7 Release Notes ## Summary This is a maintenance release of Jasmine with a number of new features and fixes. ## New features and bug fixes * Allow custom object formatters to be added in beforeAll - Fixes [#1876](http://github.com/jasmine/jasmine/issues/1876) * Allow specs to disable Jasmine's global error handling by overwriting `onerror`. - Merges [#1860](https://github.com/jasmine/jasmine/pull/1860) from @greghuc * Fixed comparison between URL objects - Fixes [#1866](http://github.com/jasmine/jasmine/issues/1866) * Added support for stack traces created by `node --enable-source-maps` with tools like the Typescript compiler. - Merges [#1862](https://github.com/jasmine/jasmine/pull/1862) from @JannesMeyer * Made properties added by createSpyObj() enumerable. - Merges [#1859](https://github.com/jasmine/jasmine/pull/1859) from DCtheTall - Fixes [#1837](http://github.com/jasmine/jasmine/issues/1837) * Show the name of the spec/suite that caused a deprecation * Warn if a spec or before/after function both takes a callback and returns a promise * Don't overwrite MatchersUtil methods with ones that were added to `Array.prototype`, esp. `contains` - Fixes [#1849](http://github.com/jasmine/jasmine/issues/1849) * Allow generator functions to be passed to `.and.callFake` - Fixes [#1848](http://github.com/jasmine/jasmine/issues/1848) ## Documentation updates * Fixed instructions for contributors to run Jasmine's ci script * Updated supported Node versions in README * Fixed script and CSS URLs in standalone example in README - Merges [#1839](https://github.com/jasmine/jasmine/pull/1839) from @snowman * Fixed typo in asyncMatcher toBePending comment - Merges [#1847](https://github.com/jasmine/jasmine/pull/1847) from @SnailCoil * Fixed link to custom object formatter tutorial * Added jasmine.isSpy to the public interface - Fixes [#1880](http://github.com/jasmine/jasmine/issues/1880) ## Internal notes * Fixed intermittent test failures * Added additional assertions to tests for toBeTruthy and toBeFalsy - Merges [#1875](https://github.com/jasmine/jasmine/pull/1875) from @yasinkocak * Pointed Travis badge at travis-ci.com, not .org * Fixed file globs so that Prettier runs on all files * Check for forgotten console and debugger statements * Fixed code in Jasmine that will trigger deprecations in 3.99 * Use jasmine-browser from npm rather than from the main branch - The current released version now works with IE, so we no longer need to depend on main. ------ _Release Notes generated with _[Anchorman](http://github.com/infews/anchorman)_ jasmine-4.5.0/release_notes/3.7.1.md000066400000000000000000000005011432731766000167730ustar00rootroot00000000000000# Jasmine Core 3.7.1 Release Notes ## Summary This is a bug fix release of Jasmine. It's identical to 3.7.0 except that it correctly reports its version number. Please see the [3.7.0 release notes](https://github.com/jasmine/jasmine/blob/main/release_notes/3.7.0.md) if you're upgrading directly from 3.6.0 or earlier. jasmine-4.5.0/release_notes/3.8.0.md000066400000000000000000000112541432731766000170020ustar00rootroot00000000000000# Jasmine Core 3.8 Release Notes ## Summary This is a maintenance release of Jasmine with a number of new features and fixes. ## Python deprecation The Jasmine packages for Python are deprecated. We intend to continue releasing them through the end of the 3.x series, but after that they will be discontinued. We recommend migrating to the following alternatives: * The [jasmine-browser-runner](https://github.com/jasmine/jasmine-browser) npm package to run specs in browsers, including headless Chrome and Saucelabs. This is the most direct replacement for the `jasmine server` and `jasmine ci` commands provided by the `jasmine` Python package. * The [jasmine](https://github.com/jasmine/jasmine-npm) npm package ( `npm install jasmine`) to run specs under Node.js. * The standalone distribution from the [latest Jasmine release](https://github.com/jasmine/jasmine/releases) to run specs in browsers with no additional tools. * The [jasmine-core](https://github.com/jasmine/jasmine) npm package if all you need is the Jasmine assets. This is the direct equivalent of the jasmine-core Python package. ## New features and bug fixes * Fixed spec filtering in Karma * Merges [#1920](https://github.com/jasmine/jasmine/pull/1920) from @jlpstolwijk * Fixes [#1906](https://github.com/jasmine/jasmine/issues/1906) * Added expectAsync(...).already * Causes async matchers to immediately fail if the promise is pending * See https://jasmine.github.io/api/3.8/async-matchers.html#already * Fixes [#1845](https://github.com/jasmine/jasmine/issues/1845) * Include rejection details in failure messages for toBeResolved and toBeResolvedWith * Fixed "stop spec on expectation failure" checkbox in standalone * Added option for spyOnAllFunctions to include non-enumerable props * Makes spyOnAllFunctions work on instance methods of ES6 classes * Merges [#1909](https://github.com/jasmine/jasmine/pull/1909) from @Dari-k * Fixes [#1897](https://github.com/jasmine/jasmine/issues/1897) * Added Spy#calls#thisFor * Provides the `this` value for a given spy call * Merges [#1903](https://github.com/jasmine/jasmine/pull/1903) from @ajvincent * Improved handling of unhandled promise rejections with no error in Node * Fixes [#1759](https://github.com/jasmine/jasmine/issues/1759) ## Documentation updates * Updated package description * Updated contributing guide * Added TypeScript typings and jasmine-browser-runner to issue template * Removed constructors from jsdocs of classes that aren't user-constructable * Fixed config.seed type in jsdocs * Merges [#1892](https://github.com/jasmine/jasmine/pull/1892) from @UziTech * Added jsdocs for the following: * asymmetric equality testers * Env#execute * Env#allowRespy * The public portion of Spec * Spy.callData.returnValue * Env#topSuite and Suite * Added a jsdoc cross-reference from Configuration to its usage * Added a note about correct usage of async matchers * Added support for ArrayBuffers to matchersUtil.equals * Merges [#1891](https://github.com/jasmine/jasmine/pull/1892) from @Finesse * Merges [#1689](https://github.com/jasmine/jasmine/pull/1892) from @dankurka * Fixes [#1687](https://github.com/jasmine/jasmine/issues/1687) ## Internal notes * Fixed typo in spec name * Merges [#1918](https://github.com/jasmine/jasmine/pull/1918) from @eltociear * Specify files to include in the NPM package rather than files to exclude * Added test coverage for MatchersUtil#equals with typed arrays * Removed checks for typed array support in the test suite * All supported browsers have all typed arrays except for Uint8ClampedArray, BigInt64Array, and BigUint64Array. * Fixed test failures on IE 10 * Test matrix updates * Added Node 16 * Added Safari 14 * Added Firefox 78 (closest match to current ESR) * Removed Safari 10-12 to speed up CI. The newer and older versions we test provide a good measure of safety. * Replaced node-sass dev dependency that isn't compatible with Node 16 * Removed unused dev dependencies * Migrated CI from Travis to Circle * Compensate for clock jitter in specs ## Supported environments jasmine-core 3.8.0 has been tested in the following environments. | Environment | Supported versions | |-------------------|--------------------| | Node | 10, 12, 14, 16 | | Safari | 8-14 | | Chrome | 91 | | Firefox | 89, 68, 78 | | Edge | 91 | | Internet Explorer | 10, 11 | ------ _Release Notes generated with _[Anchorman](http://github.com/infews/anchorman)_ jasmine-4.5.0/release_notes/3.9.0.md000066400000000000000000000050161432731766000170020ustar00rootroot00000000000000# Jasmine Core 3.9 Release Notes ## New features and bug fixes * Fixed Trusted Types error in `j$.isError_` in Chromium-based browsers * Merges [#1921](https://github.com/jasmine/jasmine/pull/1921) from @bjarkler * Fixes [#1910](https://github.com/jasmine/jasmine/issues/1910) * Fixes [#1653](https://github.com/jasmine/jasmine/issues/1653) * Better reporting of unhandled promise rejections with truthy but non-`Error` reasons on Node * `Env#execute` returns a promise in environments that support promises * Renamed `failFast` and `oneFailurePerSpec` config props to `stopOnSpecFailure` and `stopSpecOnExpectationFailure` The new names are more self-explanatory and consistent with jasmine-npm. The old names are deprecated but will still work until the next major release. * Split `boot.js` into two files to allow the env to be configured in between This is mainly intended to support jasmine-browser-runner, which will load a script that configures the env in between the two boot files (`boot0.js` and `boot1.js`). The single-file `boot.js` will still be included until the next major release. ## Ruby deprecation The Jasmine Ruby gems are deprecated. There will be no further releases after the end of the Jasmine 3.x series. We recommend that most users migrate to the [jasmine-browser-runner](https://github.com/jasmine/jasmine-browser) npm package, which is the direct replacement for the `jasmine` gem. If `jasmine-browser-runner` doesn't meet your needs, one of these might: * The [jasmine](https://github.com/jasmine/jasmine-npm) npm package to run specs in Node.js. * The [standalone distribution](https://github.com/jasmine/jasmine#installation) to run specs in browsers with no additional tools. * The [jasmine-core](https://github.com/jasmine/jasmine) npm package if all you need is the Jasmine assets. This is the direct equivalent of the `jasmine-core` Ruby gem. ## Documentation updates * Added API docs for `Suite#id` and `Spec#id` * Marked `Env#hideDisabled` deprecated in jsdocs ------ ## Supported environments jasmine-core 3.9.0 has been tested in the following environments. | Environment | Supported versions | |-------------------|--------------------| | Node | 10, 12, 14, 16 | | Safari | 8-14 | | Chrome | 92 | | Firefox | 91, 78, 68 | | Edge | 92 | | Internet Explorer | 10, 11 | _Release Notes generated with _[Anchorman](http://github.com/infews/anchorman)_ jasmine-4.5.0/release_notes/3.99.0.md000066400000000000000000000007771432731766000171040ustar00rootroot00000000000000# Jasmine Core 3.99.0 Release Notes ## Summary This release adds deprecation warnings for breaking changes that will be introduced in Jasmine 4.0. Please see the [migration guide](https://jasmine.github.io/tutorials/upgrading_to_Jasmine_4.0) for more information. This is the last planned release of jasmine-core for Ruby or Python. Versions 4.0 and later will be offered only via NPM and the standalone distribution. ------ _Release Notes generated with _[Anchorman](http://github.com/infews/anchorman)_ jasmine-4.5.0/release_notes/3.99.1.md000066400000000000000000000013561432731766000170770ustar00rootroot00000000000000# Jasmine Core 3.99.1 Release Notes This release fixes a bug in 3.99.0, which incorrectly reported a deprecation warning when a promise returned from a function passed to `it`, `beforeEach`, etc was resolved to a value. ## Supported environments jasmine-core 3.99.1 has been tested in the following environments. | Environment | Supported versions | |-------------------|--------------------| | Node | 10, 12, 14, 16 | | Safari | 10-14 | | Chrome | 98 | | Firefox | 97, 78, 68 | | Edge | 98 | | Internet Explorer | 10, 11 | ------ _Release Notes generated with _[Anchorman](http://github.com/infews/anchorman)_ jasmine-4.5.0/release_notes/4.0.0.md000066400000000000000000000174671432731766000170070ustar00rootroot00000000000000# Jasmine Core 4.0.0 Release Notes ## Summary This is a major release. In addition to new features and bug fixes it contains a number of breaking changes that are intended to diagnose common errors, improve awkward or outdated APIs, and make Jasmine easier to maintain and contribute to. If you're upgrading from Jasmine 3.x, we recommend installing 3.99 and fixing any deprecation warnings that it emits before updating to 4.0. Please see the [migration guide](https://jasmine.github.io/tutorials/upgrading_to_Jasmine_4.0) for more information. If you use the `jasmine` or `jasmine-browser-runner` NPM packages, please read the release notes for those packages as well. ## Highlights * Obsolete environments, most notably Internet Explorer, are no longer supported. Jasmine now expects to run in an environment that provides most of ES2017 and, in the case of Node, good interoperability between CommonJS modules and ES modules. * The Jasmine packages for Ruby and Python have been discontinued. * Errors in `beforeAll` and `beforeEach` functions are handled better. * Jasmine can optionally be used without creating globals in Node. * Certain common async testing bugs are now reported as errors. * A new debug logging feature makes it easier to debug failing specs, particularly intermittent failures. See details below. ## Breaking changes ### Changes to supported environments The following previously supported environments are no longer supported: * Internet Explorer * PhantomJS * Safari 8-13 * Firefox 68 and 78 * Node 10 and 12.0-12.16 * Python * Ruby * Bower Although Jasmine 4.0 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 4.x releases. The [`jasmine-browser-runner`](https://jasmine.github.io/setup/browser.html) NPM package is the direct replacement for the `jasmine` Ruby and Python packages. ### Changes that affect how specs are written * When a `beforeAll` function fails in any way other than a failed expectation, Jasmine will not run the contents of the suite or any child suites except for any `afterAll` functions defined in the same suite as the failed `beforeAll` function. All affected specs will still be reported as failed. See [#1533](https://github.com/jasmine/jasmine/issues/1533). * When a `beforeEach` function fails in any way other than a failed expectation, Jasmine will skip any subsequent `beforeEach` functions, the corresponding spec, and any `afterEach` functions defined in child suites. `afterEach` functions defined at the same or higher levels will still run. The spec will still be reported as failed. See [#1533](https://github.com/jasmine/jasmine/issues/1533). * `MatchersUtil#contains` and the `toContain` matcher use deep equality rather than `===` to compare set members. This matches how arrays are handled but may cause some previously passing `.not.toContain()` expectations to fail. * `jasmine.clock().mockDate()` throws if its argument is not a `Date`. Previous versions ignored non-`Date` arguments. * Multiple calls to an asynchronous function's `done` callback are treated as errors. * Any argument passed to a `done` callback (other than `undefined`) is treated as an error. Previous versions ignored any argument that wasn't an `Error` instance. * Jasmine will report an error rather than a warning when a function tries to combine two different forms of async (e.g. taking a callback and also returning a promise). * `this` in `describe` functions is no longer a `Suite` object. * Empty suites are treated as errors. * Merges [#1742](https://github.com/jasmine/jasmine/pull/1742) from @johnjbarton * The current time value does not decrease when `jasmine.clock().tick()` is called from a `setTimeout` or `setInterval` callback. * Merges [#1948](https://github.com/jasmine/jasmine/pull/1948) from @thw0rted * Fixes [#1929](https://github.com/jasmine/jasmine/issues/1929). ### Changes to how Jasmine is configured * Individual configuration property getters and setters such as `Env#randomTests` and `Env#randomizeTests` have been removed. Use `Env#configuration` and `Env#configure` instead. * The `failFast` and `oneFailurePerSpec` configuration properties have been removed. Use `stopOnSpecFailure` and `stopSpecOnExpectationFailure` instead. * The `Promise` configuration property has been removed. Jasmine can still consume non-native promises but will always use the global `Promise` to create promises. ### Changes that affect custom matchers * The old style of using custom equality testers, where matchers received them from Jasmine and had to pass them back to `matchersUtil` methods, is no longer supported. * `matchersUtil` and `pp` are no longer available globally. Instead, use the instances that are passed to custom matcher factories and to `jasmineToString`. See the [migration guide](https://jasmine.github.io/tutorials/upgrading_to_Jasmine_4.0) for more information about these changes and how to update custom matchers that use the old APIs. ### Changes that affect custom reporters * The [`Suite`](https://jasmine.github.io/api/4.0/Suite.html) and [`Spec`](https://jasmine.github.io/api/4.0/Spec.html) objects returned from `describe`, `it`, and `Env#topSuite` no longer expose private APIs. ### Other breaking changes * `boot.js` is no longer included. Use `boot0.js` and `boot1.js` instead. * Boot files in `lib/jasmine-core/boot` are no longer included in the NPM package. Use the boot files in `lib/jasmine-core` instead. * `json2.js` is no longer included, since all supported environments provide a JSON parser. ## Other new features and bug fixes * Jasmine can optionally be used without creating globals in Node.js. * See https://jasmine.github.io/api/4.0/module-jasmine-core.html#.noGlobals * If you're using the `jasmine` package, see [its documentation](https://jasmine.github.io/api/npm/4.0/JasmineOptions.html#globals). * Fixes [#1235](https://github.com/jasmine/jasmine/issues/1235) * Custom spy strategies are inherited from parent suites like other runnable resources. * `pending()` can now be called from `beforeEach` functions. * Fixes [#1579](https://github.com/jasmine/jasmine/issues/1579) * Removed duplicate message from errors (including. matcher failures) in V8-based environments. * `Spy#withArgs` supports custom equality testers. * Fixes [#1836](https://github.com/jasmine/jasmine/issues/1836) * The promise returned by `Env#execute` is resolved to the [jasmineDoneInfo](https://jasmine.github.io/api/4.0/global.html#JasmineDoneInfo). * Fixed stack trace filtering on Safari 15. * The HTML reporter includes top suite failures in the reported failure count. * `afterAll` functions are run after a failure even if the `stopOnSpecFailure` config property is set. * Added a debug logging feature to make it easier to debug failing specs. * Call `jasmine#debugLog` during spec execution to add a log entry. * If the spec fails, log entries are reported as part of the [specDone](https://jasmine.github.io/api/4.0/Reporter.html#specDone) reporter event. * The HTML reporter no longer says that expectations occurring after the spec finishes are AfterAll errors. ## Documentation updates * Added a [4.0 migration guide](https://jasmine.github.io/tutorials/upgrading_to_Jasmine_4.0) * Updated the README and contributing guide for 4.0 ## Supported environments jasmine-core 4.0.0 has been tested in the following environments. | Environment | Supported versions | |-------------------|--------------------| | Node | 12.17+, 14, 16 | | Safari | 14-15 | | Chrome | 96 | | Firefox | 91, 95 | | Edge | 96 | ------ _Release Notes generated with _[Anchorman](http://github.com/infews/anchorman)_ jasmine-4.5.0/release_notes/4.0.1.md000066400000000000000000000012641432731766000167740ustar00rootroot00000000000000# Jasmine Core 4.0.1 Release Notes This release fixes a bug in 4.0.0, which incorrectly reported a failure when a promise returned from a function passed to `it`, `beforeEach`, etc was resolved to a value. ## Supported environments jasmine-core 4.0.1 has been tested in the following environments. | Environment | Supported versions | |-------------------|--------------------| | Node | 12.17+, 14, 16 | | Safari | 14-15 | | Chrome | 98 | | Firefox | 91, 97 | | Edge | 98 | ------ _Release Notes generated with _[Anchorman](http://github.com/infews/anchorman)_ jasmine-4.5.0/release_notes/4.1.0.md000066400000000000000000000034201432731766000167700ustar00rootroot00000000000000# Jasmine 4.1.0 Release Notes ## New Features and Bug Fixes * toBeCloseTo treats Infinity and -Infinity as close to themselves * Fixes [#1957](https://github.com/jasmine/jasmine/issues/1957) * Replaced uses of deprecated String.prototype.substr() * Merges [#1962](https://github.com/jasmine/jasmine/pull/1962) from @CommanderRoot * Removed obsolete vendor-specific background-size CSS rules * Fixes [#1961](https://github.com/jasmine/jasmine/issues/1961) * Added toHaveSpyInteractions matcher * Merges [#1959](https://github.com/jasmine/jasmine/pull/1959) from @nitobuenida * Fixes [#1568](https://github.com/jasmine/jasmine/issues/1568) * Pretty-print [new String("")] as "[ '' ]", not "[]" * Fixed cloning of Date objects in Spy#calls#saveArgumentsByValue * Merges [#1955](https://github.com/jasmine/jasmine/pull/1955) from @coyoteecd * Fixes [#1885](https://github.com/jasmine/jasmine/issues/1885) * Include the name of the suite in the empty suite error message * toEqual checks keys that are Symbols * Merges [#1879](https://github.com/jasmine/jasmine/pull/1879) from @laeleoni * Fixes [#1811](https://github.com/jasmine/jasmine/issues/1811) ## Documentation Updates * Replaced redundant installation instructions in README with link to docs * Added links to usage instructions to README ## Supported environments jasmine-core 4.1.0 has been tested in the following environments. | Environment | Supported versions | |-------------------|--------------------| | Node | 12.17+, 14, 16 | | Safari | 14-15 | | Chrome | 100 | | Firefox | 91, 99 | | Edge | 100 | ------ _Release Notes generated with _[Anchorman](http://github.com/infews/anchorman)_ jasmine-4.5.0/release_notes/4.1.1.md000066400000000000000000000021621432731766000167730ustar00rootroot00000000000000# Jasmine 4.1.1 Release Notes ## Summary This release fixes several bugs involving equality comparison of properties with Symbol keys. ## Bug fixes * Fixes for crash bugs and output problems when comparing objects with Symbol keys * Include symbol properties in matcher diffs * Fixed exception when comparing arrays with Symbol keys * Include symbol properties in matcher diffs * Include symbol keys when pretty-printing objects * Fixes [#1966](https://github.com/jasmine/jasmine/issues/1966) * Exclude non-enumerable symbol properties from equality comparison * Merges [#1963](https://github.com/jasmine/jasmine/pull/1963) from @suke ## Supported environments jasmine-core 4.1.1 has been tested in the following environments. | Environment | Supported versions | |-------------------|--------------------| | Node | 12.17+, 14, 16, 18 | | Safari | 14, 15 | | Chrome | 101 | | Firefox | 91, 100 | | Edge | 101 | ------ _Release Notes generated with _[Anchorman](http://github.com/infews/anchorman)_ jasmine-4.5.0/release_notes/4.2.0.md000066400000000000000000000025431432731766000167760ustar00rootroot00000000000000# Jasmine 4.2.0 Release Notes ## New Features * Added a jasmine.is asymmetric equality tester * Allows the use of === comparisons for specific fields of an object that should otherwise be compared with the default deep value equality logic. ## Bug Fixes * More reliably report errors that occur late in the suite/spec lifecycle * Previously, an error that occurred after Jasmine started to report the suiteDone or specDone event for the current runable would not be reliably reported. Now such an error is reported on the nearest ancestor suite whose suiteDone event has not yet been reported. * Don't report a deprecation when a runnable uses two forms of async * This was made into an error in 4.0, so the deprecation is redundant. * Include property getter values in pretty-printed objects ## Documentation Updates * Removed duplicate Suite and Spec jsdocs ## Supported environments jasmine-core 4.2.0 has been tested in the following environments. | Environment | Supported versions | |-------------------|--------------------| | Node | 12.17+, 14, 16, 18 | | Safari | 14-15 | | Chrome | 102 | | Firefox | 91, 101 | | Edge | 101 | ------ _Release Notes generated with _[Anchorman](http://github.com/infews/anchorman)_ jasmine-4.5.0/release_notes/4.3.0.md000066400000000000000000000025371432731766000170020ustar00rootroot00000000000000# Jasmine 4.3.0 Release Notes ## New Features * Added [`jasmine.spyOnGlobalErrorsAsync`](https://jasmine.github.io/api/4.3/jasmine.html#.spyOnGlobalErrorsAsync), to better support testing code that's expected to produce unhandled exceptions or unhandled promise rejections * Fixes [#1843](https://github.com/jasmine/jasmine/issues/1843) * Fixes [#1453](https://github.com/jasmine/jasmine/issues/1453) ## Documentation updates * Updated the README to reduce redundancy and update support links ## Internal improvements * Split `Env` into several smaller classes * Replaced uses of `var` with `const`/`let` * Replaced most uses of `self = this` with arrow fns * Removed obsolete and unused utility fns * Separated reporter- and runable-specific queue runner configuration * Added more test coverage for default spy strategies * Converted integration specs to `async`/`await` ## Supported environments jasmine-core 4.3.0 has been tested in the following environments. | Environment | Supported versions | |-------------------|--------------------| | Node | 12.17+, 14, 16, 18 | | Safari | 14-15 | | Chrome | 103 | | Firefox | 91, 102 | | Edge | 103 | ------ _Release Notes generated with _[Anchorman](http://github.com/infews/anchorman)_ jasmine-4.5.0/release_notes/4.4.0.md000066400000000000000000000016721432731766000170020ustar00rootroot00000000000000# Jasmine 4.4.0 Release Notes ## Changes * Optimized the process of transitioning between specs in Node, Safari, and Edge. This change reduces the run time of jasmine-core's own test suite by 50-70% in Node, about 20% in Edge, and 75-90% in Safari. Your results may vary. In general, suites with many fast specs will see the greatest performance improvement. * Removed old code to support browsers that don't provide addEventListener/removeEventListener. ## Supported environments jasmine-core 4.4.0 has been tested in the following environments. | Environment | Supported versions | |-------------------|--------------------| | Node | 12.17+, 14, 16, 18 | | Safari | 14-15 | | Chrome | 105 | | Firefox | 91, 102, 104 | | Edge | 104 | ------ _Release Notes generated with _[Anchorman](http://github.com/infews/anchorman)_ jasmine-4.5.0/release_notes/4.5.0.md000066400000000000000000000022661432731766000170030ustar00rootroot00000000000000# Jasmine 4.5.0 Release Notes ## New Features * Added Safari 16 to supported browsers * Include inner exceptions in stack traces ## Bug Fixes * Report exceptions thrown by a describe before any it calls * Coerce the random string to a seed before sending it to reporters * This fixes an error in HTMLReporter when the configured seed is a number rather than a string, which has been allowed since 3.8.0 ## Documentation updates * Fixed the jsdoc types of SuiteResult and SpecResult ids * Replaced var with const in API doc examples * Updated the style of the examples that are included in jasmine-core ## Internal improvements * Converted TreeProcessor to async/await * Converted ReportDispatcher to promises ## Supported environments jasmine-core 4.5.0 has been tested in the following environments. | Environment | Supported versions | |-------------------|--------------------| | Node | 12.17+, 14, 16, 18 | | Safari | 14-16 | | Chrome | 107 | | Firefox | 91, 102, 106 | | Edge | 106 | ------ _Release Notes generated with _[Anchorman](http://github.com/infews/anchorman)_ jasmine-4.5.0/release_notes/older_versions.md000066400000000000000000000072461432731766000213770ustar00rootroot00000000000000# Release 1.0.1.1 — November 9, 2010 ## Jasmine Gem ## Bugs fixed * Rails 3.0 and RSpec 2.0 are now supported. ## Known issues * Rails 3 generators are not yet implemented -- coming soon! ----- # Release 1.0.1 — October 5, 2010 ----- ## Jasmine Core ## Bugs fixed * Bug fixes for Internet Explorer (thanks fschwiet, fabiomcosta, and jfirebaugh). ## Jasmine Gem ## Bugs fixed * Bug fix for Windows (thanks jfirebaugh). ----- # Release 1.0 — September 14, 2010 ----- ## Jasmine Core ## Features * `waitsFor()` arguments can now be specified in any order. Timeout and message are optional. * The default `waitsFor()` timeout period is now specified in `env.defaultTimeoutInterval`; the default value is 5 seconds. * Added link to jasmine site from html runner. * Added license file to standalone distribution. * New friendly version number. ## Bugs fixed * `waitsFor()` hanged forever if latch function never returned true. * The `not.toThrow()` matcher threw an exception when used with no args. * The `toThrow()` matcher, when inverted, gave misleading failure messages. * Spy matchers, when inverted, gave misleading failure messages. ## Deprecations * Deprecated `waits()` block in favor of `waitsFor()`; `waits()` will be removed in a future release. * Deprecated `toNotBe()`, `toNotEqual()`, `toNotMatch()`, and `toNotContain()` matchers; they will be removed in a future release. * Console X was removed from the distribution as it was no longer used. * To give us some flexibility for future features, wrapped matcher functions now return `undefined` (they previously returned `true` or `false`, but this was undocumented). ## Jasmine Gem ## Features * Jasmine now supports JRuby. * Jasmine now supports Ruby 1.9. ## Bugs fixed * Various generator issues fixed. ## Known issues * Rails 3 and RSpec 2 are not yet fully supported. ----- # Release 0.11.1 — June 25, 2010 ----- ## Jasmine Core ### Features * Jasmine no longer logs "Jasmine Running…" messages to the log by default. This can be enabled in runner.html by adding 'trivialReporter.logRunningSpecs = true;'. * The `wasCalled()`, `wasCalledWith()`, `wasNotCalled()` and `wasNotCalledWith()` matchers have been deprecated. The new matchers `toHaveBeenCalled()` and `toHaveBeenCalledWith()` have been added. You can use the `not` prefix to achieve equivalent of the `wasNot…()` expectation (e.g. `not.toHaveBeenCalled()`). ## Notables * A barebones version of Jasmine is now available on http://pivotal.github.com/jasmine/. ----- # Release 0.11.0 — June 23, 2010 ----- ## Jasmine Core ## Features * The version number has been removed from the generated single-file /lib/jasmine.js. We're also now uploading this file, with the version number in the filename, to github's Downloads page. * Old-style matchers (those using this.report(), from before 0.10.x) are no longer supported. See the README for instructions on writing new-style matchers. * jasmine.log pretty-prints its parameters to the spec's output. * Jasmine no longer depends on 'window'. * HTML runner should show number of passes/fails by spec, not expectation. * Small modification to JsApiReporter data format. ## Bugs fixed: * If multiple beforeEach blocks were declared, they were executed in reverse order. * Specs with duplicate names confused TrivialReporter output. * Errors in describe functions caused later tests to be weirdly nested. * Nested specs weren't reported properly by the JsApiReporter. ## Known issues: * If you turn on the mock clock, you'll get a spurious log message at the end of your spec. jasmine-4.5.0/scripts/000077500000000000000000000000001432731766000146465ustar00rootroot00000000000000jasmine-4.5.0/scripts/run-all-browsers000077500000000000000000000014021432731766000200070ustar00rootroot00000000000000#!/bin/sh run_browser() { browser=$1 version=$2 description="$browser $version" if [ $version = "latest" ]; then version="" fi echo echo echo "Running $description" echo USE_SAUCE=true JASMINE_BROWSER=$browser SAUCE_BROWSER_VERSION=$version npm run ci if [ $? -eq 0 ]; then echo "PASS: $description" >> "$passfile" else echo "FAIL: $description" >> "$failfile" fi } passfile=`mktemp -t jasmine-results.XXXXXX` || exit 1 failfile=`mktemp -t jasmine-results.XXXXXX` || exit 1 run_browser chrome latest run_browser firefox latest run_browser firefox 102 run_browser firefox 91 run_browser safari 16 run_browser safari 15 run_browser safari 14 run_browser MicrosoftEdge latest echo cat "$passfile" "$failfile" if [ -s "$failfile" ]; then exit 1 fi jasmine-4.5.0/scripts/start-sauce-connect000077500000000000000000000015021432731766000204540ustar00rootroot00000000000000#!/usr/bin/env bash set -o errexit set -o pipefail if [ $# -gt 1 -o "$1" = "--help" ]; then echo "Usage: $0 [pidfile]" 1>&2 exit fi if [ -z "$1" ]; then pidfile=`mktemp` else pidfile="$1" fi outfile=`mktemp` echo "Starting Sauce Connect" if [ -z "$SAUCE_TUNNEL_IDENTIFIER" ]; then sc -u "$SAUCE_USERNAME" -k "$SAUCE_ACCESS_KEY" -X 4445 --pidfile "$pidfile" 2>&1 | tee "$outfile" & else sc -u "$SAUCE_USERNAME" -k "$SAUCE_ACCESS_KEY" -X 4445 --pidfile "$pidfile" -i "$SAUCE_TUNNEL_IDENTIFIER" 2>&1 | tee "$outfile" & fi 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 echo "Can't connect to Sauce tunnel" killall sc exit 1 fi jasmine-4.5.0/scripts/stop-sauce-connect000077500000000000000000000014361432731766000203120ustar00rootroot00000000000000#!/usr/bin/env bash set -o errexit set -o pipefail if [ -z "$1" ]; then echo "Usage: $0 sauce-connect-pid" 1>&2 exit fi pid="$1" echo "PID: $pid" echo "Stopping Sauce Connect" # Sauce Connect docs say that we can just kill -9 it if we don't care about # failing any ongoing sessions. In practice, that sometimes works but usually # leaks a tunnel so badly that you can't even stop it from the web UI. # Instead of doing that, we give Sauce Connect some time to shut down # gracefully and then give up. kill -INT $pid # Wait up to 2 minutes, then give up if it's still running n=0 while [ $n -lt 120 ] && ps -p $pid > /dev/null; do sleep 1 kill -INT $pid 2> /dev/null || true n=$(($n + 1)) done if ps -p $pid > /dev/null; then echo "Could not shut down Sauce Connect" fi exit $exitcode jasmine-4.5.0/spec/000077500000000000000000000000001432731766000141115ustar00rootroot00000000000000jasmine-4.5.0/spec/.eslintrc.js000066400000000000000000000014561432731766000163560ustar00rootroot00000000000000module.exports = { ignorePatterns: ['support/ci.js', 'support/jasmine-browser.js'], rules: { // Relax rules for now to allow for the quirks of the test suite // TODO: We should probably remove these & fix the resulting errors quotes: 'off', semi: 'off', 'key-spacing': 'off', 'space-before-blocks': 'off', 'no-trailing-spaces': 'off', 'block-spacing': 'off', // Additionally, check for unused fn args // TODO: consider doing this in src files as well as specs 'no-unused-vars': ['error', { args: 'after-used' }], // Since linting is done at the end of the process and doesn't stop us // from running tests, it makes sense to fail if debugger statements // or console references are present. 'no-debugger': 'error', 'no-console': 'error' } }; jasmine-4.5.0/spec/core/000077500000000000000000000000001432731766000150415ustar00rootroot00000000000000jasmine-4.5.0/spec/core/AsyncExpectationSpec.js000066400000000000000000000600051432731766000214740ustar00rootroot00000000000000describe('AsyncExpectation', function() { beforeEach(function() { jasmineUnderTest.Expectation.addAsyncCoreMatchers( jasmineUnderTest.asyncMatchers ); }); describe('#not', function() { it('converts a pass to a fail', function() { const addExpectationResult = jasmine.createSpy('addExpectationResult'), actual = Promise.resolve(), pp = jasmineUnderTest.makePrettyPrinter(), expectation = jasmineUnderTest.Expectation.asyncFactory({ matchersUtil: new jasmineUnderTest.MatchersUtil({ pp: pp }), actual: actual, addExpectationResult: addExpectationResult }); return expectation.not.toBeResolved().then(function() { expect(addExpectationResult).toHaveBeenCalledWith( false, jasmine.objectContaining({ passed: false, message: 'Expected [object Promise] not to be resolved.' }) ); }); }); it('converts a fail to a pass', function() { const addExpectationResult = jasmine.createSpy('addExpectationResult'), actual = Promise.reject(), expectation = jasmineUnderTest.Expectation.asyncFactory({ matchersUtil: new jasmineUnderTest.MatchersUtil({ pp: function() {} }), actual: actual, addExpectationResult: addExpectationResult }); return expectation.not.toBeResolved().then(function() { expect(addExpectationResult).toHaveBeenCalledWith( true, jasmine.objectContaining({ passed: true, message: '' }) ); }); }); }); it('propagates rejections from the comparison function', function() { const error = new Error('ExpectationSpec failure'); const addExpectationResult = jasmine.createSpy('addExpectationResult'), actual = dummyPromise(), expectation = jasmineUnderTest.Expectation.asyncFactory({ actual: actual, addExpectationResult: addExpectationResult }); spyOn(expectation, 'toBeResolved').and.returnValue(Promise.reject(error)); return expectation.toBeResolved().then( function() { fail('Expected a rejection'); }, function(e) { expect(e).toBe(error); } ); }); describe('#withContext', function() { it('prepends the context to the generated failure message', function() { const matchersUtil = { pp: function(val) { return val.toString(); } }, addExpectationResult = jasmine.createSpy('addExpectationResult'), expectation = jasmineUnderTest.Expectation.asyncFactory({ actual: Promise.reject('rejected'), addExpectationResult: addExpectationResult, matchersUtil: matchersUtil }); return expectation .withContext('Some context') .toBeResolved() .then(function() { expect(addExpectationResult).toHaveBeenCalledWith( false, jasmine.objectContaining({ message: 'Some context: Expected a promise to be resolved but it was rejected with rejected.' }) ); }); }); it('prepends the context to a custom failure message', function() { const matchersUtil = { buildFailureMessage: function() { return 'failure message'; }, pp: jasmineUnderTest.makePrettyPrinter() }, addExpectationResult = jasmine.createSpy('addExpectationResult'), expectation = jasmineUnderTest.Expectation.asyncFactory({ actual: Promise.reject('b'), addExpectationResult: addExpectationResult, matchersUtil: matchersUtil }); return expectation .withContext('Some context') .toBeResolvedTo('a') .then(function() { expect(addExpectationResult).toHaveBeenCalledWith( false, jasmine.objectContaining({ message: "Some context: Expected a promise to be resolved to 'a' " + "but it was rejected with 'b'." }) ); }); }); it('prepends the context to a custom failure message from a function', function() { pending('should actually work, but no custom matchers for async yet'); const matchersUtil = { buildFailureMessage: function() { return 'failure message'; } }, addExpectationResult = jasmine.createSpy('addExpectationResult'), actual = Promise.reject(), expectation = jasmineUnderTest.Expectation.asyncFactory({ actual: actual, addExpectationResult: addExpectationResult, matchersUtil: matchersUtil }); return expectation .withContext('Some context') .toBeResolved() .then(function() { expect(addExpectationResult).toHaveBeenCalledWith( false, jasmine.objectContaining({ message: 'Some context: msg' }) ); }); }); it('works with #not', function() { const addExpectationResult = jasmine.createSpy('addExpectationResult'), actual = Promise.resolve(), pp = jasmineUnderTest.makePrettyPrinter(), expectation = jasmineUnderTest.Expectation.asyncFactory({ actual: actual, addExpectationResult: addExpectationResult, matchersUtil: new jasmineUnderTest.MatchersUtil({ pp: pp }) }); return expectation .withContext('Some context') .not.toBeResolved() .then(function() { expect(addExpectationResult).toHaveBeenCalledWith( false, jasmine.objectContaining({ message: 'Some context: Expected [object Promise] not to be resolved.' }) ); }); }); it('works with #not and a custom message', function() { const addExpectationResult = jasmine.createSpy('addExpectationResult'), actual = Promise.resolve('a'), expectation = jasmineUnderTest.Expectation.asyncFactory({ actual: actual, addExpectationResult: addExpectationResult, matchersUtil: new jasmineUnderTest.MatchersUtil({ pp: jasmineUnderTest.makePrettyPrinter() }) }); return expectation .withContext('Some context') .not.toBeResolvedTo('a') .then(function() { expect(addExpectationResult).toHaveBeenCalledWith( false, jasmine.objectContaining({ message: "Some context: Expected a promise not to be resolved to 'a'." }) ); }); }); }); describe('async matchers', function() { it('makes custom matchers available to this expectation', function() { const asyncMatchers = { toFoo: function() {}, toBar: function() {} }, expectation = jasmineUnderTest.Expectation.asyncFactory({ customAsyncMatchers: asyncMatchers }); expect(expectation.toFoo).toBeDefined(); expect(expectation.toBar).toBeDefined(); }); it("wraps matchers's compare functions, passing in matcher dependencies", function() { const fakeCompare = function() { return Promise.resolve({ pass: true }); }, matcherFactory = jasmine .createSpy('matcher') .and.returnValue({ compare: fakeCompare }), matchers = { toFoo: matcherFactory }, matchersUtil = { buildFailureMessage: jasmine.createSpy('buildFailureMessage') }, addExpectationResult = jasmine.createSpy('addExpectationResult'), expectation = jasmineUnderTest.Expectation.asyncFactory({ matchersUtil: matchersUtil, customAsyncMatchers: matchers, actual: 'an actual', addExpectationResult: addExpectationResult }); return expectation.toFoo('hello').then(function() { expect(matcherFactory).toHaveBeenCalledWith(matchersUtil); }); }); it("wraps matchers's compare functions, passing the actual and expected", function() { const fakeCompare = jasmine .createSpy('fake-compare') .and.returnValue(Promise.resolve({ pass: true })), matchers = { toFoo: function() { return { compare: fakeCompare }; } }, matchersUtil = { buildFailureMessage: jasmine.createSpy('buildFailureMessage') }, addExpectationResult = jasmine.createSpy('addExpectationResult'), expectation = jasmineUnderTest.Expectation.asyncFactory({ matchersUtil: matchersUtil, customAsyncMatchers: matchers, actual: 'an actual', addExpectationResult: addExpectationResult }); return expectation.toFoo('hello').then(function() { expect(fakeCompare).toHaveBeenCalledWith('an actual', 'hello'); }); }); 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 }); } }; } }, 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, matchersUtil: matchersUtil, actual: 'an actual', addExpectationResult: addExpectationResult }); return expectation.toFoo('hello').then(function() { expect(addExpectationResult).toHaveBeenCalledWith(true, { matcherName: 'toFoo', passed: true, message: '', error: undefined, expected: 'hello', actual: 'an actual', errorForStack: errorWithStack }); }); }); it('reports a failing result to the spec when the comparison fails', function() { const matchers = { toFoo: function() { return { compare: function() { return Promise.resolve({ pass: false }); } }; } }, matchersUtil = { buildFailureMessage: function() { return ''; } }, addExpectationResult = jasmine.createSpy('addExpectationResult'), errorWithStack = new Error('errorWithStack'); spyOn(jasmineUnderTest.util, 'errorWithStack').and.returnValue( errorWithStack ); const expectation = jasmineUnderTest.Expectation.asyncFactory({ customAsyncMatchers: matchers, matchersUtil: matchersUtil, actual: 'an actual', addExpectationResult: addExpectationResult }); return expectation.toFoo('hello').then(function() { expect(addExpectationResult).toHaveBeenCalledWith(false, { matcherName: 'toFoo', passed: false, expected: 'hello', actual: 'an actual', message: '', error: undefined, 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' }); } }; } }, addExpectationResult = jasmine.createSpy('addExpectationResult'), errorWithStack = new Error('errorWithStack'); spyOn(jasmineUnderTest.util, 'errorWithStack').and.returnValue( errorWithStack ); const expectation = jasmineUnderTest.Expectation.asyncFactory({ actual: 'an actual', customAsyncMatchers: matchers, addExpectationResult: addExpectationResult }); return expectation.toFoo('hello').then(function() { expect(addExpectationResult).toHaveBeenCalledWith(false, { matcherName: 'toFoo', passed: false, expected: 'hello', actual: 'an actual', message: 'I am a custom message', error: undefined, 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'; } }); } }; } }, addExpectationResult = jasmine.createSpy('addExpectationResult'), errorWithStack = new Error('errorWithStack'); spyOn(jasmineUnderTest.util, 'errorWithStack').and.returnValue( errorWithStack ); const expectation = jasmineUnderTest.Expectation.asyncFactory({ customAsyncMatchers: matchers, actual: 'an actual', addExpectationResult: addExpectationResult }); return expectation.toFoo('hello').then(function() { expect(addExpectationResult).toHaveBeenCalledWith(false, { matcherName: 'toFoo', passed: false, expected: 'hello', actual: 'an actual', message: 'I am a custom message', error: undefined, 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 }); } }; } }, 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, actual: 'an actual', addExpectationResult: addExpectationResult }).not; return expectation.toFoo('hello').then(function() { expect(addExpectationResult).toHaveBeenCalledWith(true, { matcherName: 'toFoo', passed: true, message: '', error: undefined, expected: 'hello', actual: actual, errorForStack: errorWithStack }); }); }); it('reports a failing result to the spec when the comparison passes for a negative expectation', function() { const matchers = { toFoo: function() { return { compare: function() { return Promise.resolve({ pass: true }); } }; } }, matchersUtil = { buildFailureMessage: function() { return 'default 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, actual: 'an actual', matchersUtil: matchersUtil, addExpectationResult: addExpectationResult }).not; return expectation.toFoo('hello').then(function() { expect(addExpectationResult).toHaveBeenCalledWith(false, { matcherName: 'toFoo', passed: false, expected: 'hello', actual: actual, message: 'default message', error: undefined, 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' }); } }; } }, 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, actual: 'an actual', addExpectationResult: addExpectationResult }).not; return expectation.toFoo('hello').then(function() { expect(addExpectationResult).toHaveBeenCalledWith(false, { matcherName: 'toFoo', passed: false, expected: 'hello', actual: actual, message: 'I am a custom message', error: undefined, 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 }); } }; } }, 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, actual: 'an actual', addExpectationResult: addExpectationResult }).not; return expectation.toFoo('hello').then(function() { expect(addExpectationResult).toHaveBeenCalledWith(true, { matcherName: 'toFoo', passed: true, expected: 'hello', actual: actual, message: '', error: undefined, 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" }); } }; } }, 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, actual: 'an actual', addExpectationResult: addExpectationResult }).not; return expectation.toFoo('hello').then(function() { expect(addExpectationResult).toHaveBeenCalledWith(false, { matcherName: 'toFoo', passed: false, expected: 'hello', actual: actual, message: "I'm a custom message", error: undefined, errorForStack: errorWithStack }); }); }); 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 }); } }; } }, addExpectationResult = jasmine.createSpy('addExpectationResult'), errorWithStack = new Error('errorWithStack'); spyOn(jasmineUnderTest.util, 'errorWithStack').and.returnValue( errorWithStack ); const expectation = jasmineUnderTest.Expectation.asyncFactory({ actual: 'an actual', customAsyncMatchers: matchers, addExpectationResult: addExpectationResult }); return expectation.toFoo('hello').then(function() { expect(addExpectationResult).toHaveBeenCalledWith(false, { matcherName: 'toFoo', passed: false, expected: 'hello', actual: 'an actual', message: 'I am a custom message', error: undefined, 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' }); } }; } }, addExpectationResult = jasmine.createSpy('addExpectationResult'), errorWithStack = new Error('errorWithStack'); spyOn(jasmineUnderTest.util, 'errorWithStack').and.returnValue( errorWithStack ); const expectation = jasmineUnderTest.Expectation.asyncFactory({ actual: 'an actual', customAsyncMatchers: matchers, addExpectationResult: addExpectationResult }).not; return expectation.toFoo('hello').then(function() { expect(addExpectationResult).toHaveBeenCalledWith(false, { matcherName: 'toFoo', passed: false, expected: 'hello', actual: 'an actual', message: 'I am a custom message', error: undefined, 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'; } }); } }; } }, addExpectationResult = jasmine.createSpy('addExpectationResult'), errorWithStack = new Error('errorWithStack'); spyOn(jasmineUnderTest.util, 'errorWithStack').and.returnValue( errorWithStack ); let expectation = jasmineUnderTest.Expectation.asyncFactory({ actual: 'an actual', customAsyncMatchers: matchers, addExpectationResult: addExpectationResult }).not; return expectation.toFoo('hello').then(function() { expect(addExpectationResult).toHaveBeenCalledWith(false, { matcherName: 'toFoo', passed: false, expected: 'hello', actual: 'an actual', message: 'I am a custom message', error: undefined, errorForStack: errorWithStack }); }); }); }); function dummyPromise() { return new Promise(function() {}); } }); jasmine-4.5.0/spec/core/CallTrackerSpec.js000066400000000000000000000111001432731766000203720ustar00rootroot00000000000000describe('CallTracker', function() { it('tracks that it was called when executed', function() { const callTracker = new jasmineUnderTest.CallTracker(); expect(callTracker.any()).toBe(false); callTracker.track(); expect(callTracker.any()).toBe(true); }); it('tracks that number of times that it is executed', function() { const callTracker = new jasmineUnderTest.CallTracker(); expect(callTracker.count()).toEqual(0); callTracker.track(); expect(callTracker.count()).toEqual(1); }); it('tracks the params from each execution', function() { const callTracker = new jasmineUnderTest.CallTracker(); callTracker.track({ object: void 0, args: [] }); callTracker.track({ object: {}, args: [0, 'foo'] }); expect(callTracker.argsFor(0)).toEqual([]); expect(callTracker.argsFor(1)).toEqual([0, 'foo']); }); it("tracks the 'this' object from each execution", function() { const callTracker = new jasmineUnderTest.CallTracker(); const this0 = {}, this1 = {}; callTracker.track({ object: this0, args: [] }); callTracker.track({ object: this1, args: [] }); callTracker.track({ args: [] }); expect(callTracker.thisFor(0)).toBe(this0); expect(callTracker.thisFor(1)).toBe(this1); expect(callTracker.thisFor(2)).toBe(undefined); }); it('returns any empty array when there was no call', function() { const callTracker = new jasmineUnderTest.CallTracker(); expect(callTracker.argsFor(0)).toEqual([]); }); it('allows access for the arguments for all calls', function() { const callTracker = new jasmineUnderTest.CallTracker(); callTracker.track({ object: {}, args: [] }); callTracker.track({ object: {}, args: [0, 'foo'] }); expect(callTracker.allArgs()).toEqual([[], [0, 'foo']]); }); it('tracks the context and arguments for each call', function() { const callTracker = new jasmineUnderTest.CallTracker(); callTracker.track({ object: {}, args: [] }); callTracker.track({ object: {}, args: [0, 'foo'] }); expect(callTracker.all()[0]).toEqual({ object: {}, args: [] }); expect(callTracker.all()[1]).toEqual({ object: {}, args: [0, 'foo'] }); }); it('simplifies access to the arguments for the last (most recent) call', function() { const callTracker = new jasmineUnderTest.CallTracker(); callTracker.track(); callTracker.track({ object: {}, args: [0, 'foo'] }); expect(callTracker.mostRecent()).toEqual({ object: {}, args: [0, 'foo'] }); }); it("returns a useful falsy value when there isn't a last (most recent) call", function() { const callTracker = new jasmineUnderTest.CallTracker(); expect(callTracker.mostRecent()).toBeFalsy(); }); it('simplifies access to the arguments for the first (oldest) call', function() { const callTracker = new jasmineUnderTest.CallTracker(); callTracker.track({ object: {}, args: [0, 'foo'] }); expect(callTracker.first()).toEqual({ object: {}, args: [0, 'foo'] }); }); it("returns a useful falsy value when there isn't a first (oldest) call", function() { const callTracker = new jasmineUnderTest.CallTracker(); expect(callTracker.first()).toBeFalsy(); }); it('allows the tracking to be reset', function() { const callTracker = new jasmineUnderTest.CallTracker(); callTracker.track(); callTracker.track({ object: {}, args: [0, 'foo'] }); callTracker.reset(); expect(callTracker.any()).toBe(false); expect(callTracker.count()).toEqual(0); expect(callTracker.argsFor(0)).toEqual([]); expect(callTracker.all()).toEqual([]); expect(callTracker.mostRecent()).toBeFalsy(); }); it('allows object arguments to be shallow cloned', function() { const callTracker = new jasmineUnderTest.CallTracker(); callTracker.saveArgumentsByValue(); const objectArg = { foo: 'bar' }, arrayArg = ['foo', 'bar']; callTracker.track({ object: {}, args: [objectArg, arrayArg, false, undefined, null, NaN, '', 0, 1.0] }); expect(callTracker.mostRecent().args[0]).not.toBe(objectArg); expect(callTracker.mostRecent().args[0]).toEqual(objectArg); expect(callTracker.mostRecent().args[1]).not.toBe(arrayArg); expect(callTracker.mostRecent().args[1]).toEqual(arrayArg); }); it('saves primitive arguments by value', function() { const callTracker = new jasmineUnderTest.CallTracker(), args = [undefined, null, false, '', /\s/, 0, 1.2, NaN]; callTracker.saveArgumentsByValue(); callTracker.track({ object: {}, args: args }); expect(callTracker.mostRecent().args).toEqual(args); }); }); jasmine-4.5.0/spec/core/ClearStackSpec.js000066400000000000000000000141521432731766000202310ustar00rootroot00000000000000describe('ClearStack', function() { it('works in an integrationy way', function(done) { const clearStack = jasmineUnderTest.getClearStack( jasmineUnderTest.getGlobal() ); clearStack(function() { done(); }); }); describe('in Safari', function() { usesQueueMicrotaskWithSetTimeout(function() { return { navigator: { userAgent: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit/605.0.8 (KHTML, like Gecko) Version/15.1 Safari/605.0.8' }, // queueMicrotask should be used even though MessageChannel is present MessageChannel: fakeMessageChannel }; }); }); describe('in browsers other than Safari', function() { usesMessageChannel(function() { return { navigator: { // Chrome's user agent string contains "Safari" so it's a good test case userAgent: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.0.0 Safari/537.36' } }; }); describe('when MessageChannel is unavailable', function() { usesQueueMicrotaskWithSetTimeout(function() { return { navigator: { userAgent: 'CERN-LineMode/2.15 libwww/2.17b3', MessageChannel: undefined } }; }); }); }); describe('in Node', function() { usesQueueMicrotaskWithoutSetTimeout(function() { return { process: { versions: { node: '3.1415927' } } }; }); }); function usesMessageChannel(makeGlobal) { it('uses MessageChannel', function() { const global = { ...makeGlobal(), MessageChannel: fakeMessageChannel }; const clearStack = jasmineUnderTest.getClearStack(global); let called = false; clearStack(function() { called = true; }); expect(called).toBe(true); }); it('uses setTimeout instead of MessageChannel every 10 calls to make sure we release the CPU', function() { const fakeChannel = fakeMessageChannel(); spyOn(fakeChannel.port2, 'postMessage'); const setTimeout = jasmine.createSpy('setTimeout'); const global = { ...makeGlobal(), setTimeout, MessageChannel: function() { return fakeChannel; } }; const clearStack = jasmineUnderTest.getClearStack(global); for (let i = 0; i < 9; i++) { clearStack(function() {}); } expect(fakeChannel.port2.postMessage).toHaveBeenCalled(); expect(setTimeout).not.toHaveBeenCalled(); clearStack(function() {}); expect(fakeChannel.port2.postMessage).toHaveBeenCalledTimes(9); expect(setTimeout).toHaveBeenCalledTimes(1); clearStack(function() {}); expect(fakeChannel.port2.postMessage).toHaveBeenCalledTimes(10); expect(setTimeout).toHaveBeenCalledTimes(1); }); it('calls setTimeout when onmessage is called recursively', function() { const setTimeout = jasmine.createSpy('setTimeout'); const global = { ...makeGlobal(), setTimeout, MessageChannel: fakeMessageChannel }; const clearStack = jasmineUnderTest.getClearStack(global); const fn = jasmine.createSpy('second clearStack function'); clearStack(function() { clearStack(fn); }); expect(fn).not.toHaveBeenCalled(); expect(setTimeout).toHaveBeenCalledWith(fn, 0); }); } function usesQueueMicrotaskWithSetTimeout(makeGlobal) { it('uses queueMicrotask', function() { const global = { ...makeGlobal(), queueMicrotask: function(fn) { fn(); } }; const clearStack = jasmineUnderTest.getClearStack(global); let called = false; clearStack(function() { called = true; }); expect(called).toBe(true); }); it('uses setTimeout instead of queueMicrotask every 10 calls to make sure we release the CPU', function() { const queueMicrotask = jasmine.createSpy('queueMicrotask'); const setTimeout = jasmine.createSpy('setTimeout'); const global = { ...makeGlobal(), queueMicrotask, setTimeout }; const clearStack = jasmineUnderTest.getClearStack(global); for (let i = 0; i < 9; i++) { clearStack(function() {}); } expect(queueMicrotask).toHaveBeenCalled(); expect(setTimeout).not.toHaveBeenCalled(); clearStack(function() {}); expect(queueMicrotask).toHaveBeenCalledTimes(9); expect(setTimeout).toHaveBeenCalledTimes(1); clearStack(function() {}); expect(queueMicrotask).toHaveBeenCalledTimes(10); expect(setTimeout).toHaveBeenCalledTimes(1); }); } function usesQueueMicrotaskWithoutSetTimeout(makeGlobal) { it('uses queueMicrotask', function() { const global = { ...makeGlobal(), queueMicrotask: function(fn) { fn(); } }; const clearStack = jasmineUnderTest.getClearStack(global); let called = false; clearStack(function() { called = true; }); expect(called).toBe(true); }); it('does not use setTimeout', function() { const queueMicrotask = jasmine.createSpy('queueMicrotask'); const setTimeout = jasmine.createSpy('setTimeout'); const global = { ...makeGlobal(), queueMicrotask, setTimeout }; const clearStack = jasmineUnderTest.getClearStack(global); clearStack(function() {}); clearStack(function() {}); clearStack(function() {}); clearStack(function() {}); clearStack(function() {}); clearStack(function() {}); clearStack(function() {}); clearStack(function() {}); clearStack(function() {}); clearStack(function() {}); expect(queueMicrotask).toHaveBeenCalledTimes(10); expect(setTimeout).not.toHaveBeenCalled(); }); } function fakeMessageChannel() { const channel = { port1: {}, port2: { postMessage: function() { channel.port1.onmessage(); } } }; return channel; } }); jasmine-4.5.0/spec/core/ClockSpec.js000066400000000000000000000763761432731766000172700ustar00rootroot00000000000000describe('Clock', function() { const NODE_JS = typeof process !== 'undefined' && process.versions && typeof process.versions.node === 'string'; it('does not replace setTimeout until it is installed', function() { const fakeSetTimeout = jasmine.createSpy('global setTimeout'), fakeGlobal = { setTimeout: fakeSetTimeout }, delayedFunctionScheduler = jasmine.createSpyObj( 'delayedFunctionScheduler', ['scheduleFunction'] ), delayedFn = jasmine.createSpy('delayedFn'), mockDate = { install: function() {}, tick: function() {}, uninstall: function() {} }, clock = new jasmineUnderTest.Clock( fakeGlobal, function() { return delayedFunctionScheduler; }, mockDate ); fakeGlobal.setTimeout(delayedFn, 0); expect(fakeSetTimeout).toHaveBeenCalledWith(delayedFn, 0); expect(delayedFunctionScheduler.scheduleFunction).not.toHaveBeenCalled(); fakeSetTimeout.calls.reset(); clock.install(); fakeGlobal.setTimeout(delayedFn, 0); expect(delayedFunctionScheduler.scheduleFunction).toHaveBeenCalled(); expect(fakeSetTimeout).not.toHaveBeenCalled(); }); it('does not replace clearTimeout until it is installed', function() { const fakeClearTimeout = jasmine.createSpy('global cleartimeout'), fakeGlobal = { clearTimeout: fakeClearTimeout }, delayedFunctionScheduler = jasmine.createSpyObj( 'delayedFunctionScheduler', ['removeFunctionWithId'] ), mockDate = { install: function() {}, tick: function() {}, uninstall: function() {} }, clock = new jasmineUnderTest.Clock( fakeGlobal, function() { return delayedFunctionScheduler; }, mockDate ); fakeGlobal.clearTimeout('foo'); expect(fakeClearTimeout).toHaveBeenCalledWith('foo'); expect( delayedFunctionScheduler.removeFunctionWithId ).not.toHaveBeenCalled(); fakeClearTimeout.calls.reset(); clock.install(); fakeGlobal.clearTimeout('foo'); expect(delayedFunctionScheduler.removeFunctionWithId).toHaveBeenCalled(); expect(fakeClearTimeout).not.toHaveBeenCalled(); }); it('does not replace setInterval until it is installed', function() { const fakeSetInterval = jasmine.createSpy('global setInterval'), fakeGlobal = { setInterval: fakeSetInterval }, delayedFunctionScheduler = jasmine.createSpyObj( 'delayedFunctionScheduler', ['scheduleFunction'] ), delayedFn = jasmine.createSpy('delayedFn'), mockDate = { install: function() {}, tick: function() {}, uninstall: function() {} }, clock = new jasmineUnderTest.Clock( fakeGlobal, function() { return delayedFunctionScheduler; }, mockDate ); fakeGlobal.setInterval(delayedFn, 0); expect(fakeSetInterval).toHaveBeenCalledWith(delayedFn, 0); expect(delayedFunctionScheduler.scheduleFunction).not.toHaveBeenCalled(); fakeSetInterval.calls.reset(); clock.install(); fakeGlobal.setInterval(delayedFn, 0); expect(delayedFunctionScheduler.scheduleFunction).toHaveBeenCalled(); expect(fakeSetInterval).not.toHaveBeenCalled(); }); it('does not replace clearInterval until it is installed', function() { const fakeClearInterval = jasmine.createSpy('global clearinterval'), fakeGlobal = { clearInterval: fakeClearInterval }, delayedFunctionScheduler = jasmine.createSpyObj( 'delayedFunctionScheduler', ['removeFunctionWithId'] ), mockDate = { install: function() {}, tick: function() {}, uninstall: function() {} }, clock = new jasmineUnderTest.Clock( fakeGlobal, function() { return delayedFunctionScheduler; }, mockDate ); fakeGlobal.clearInterval('foo'); expect(fakeClearInterval).toHaveBeenCalledWith('foo'); expect( delayedFunctionScheduler.removeFunctionWithId ).not.toHaveBeenCalled(); fakeClearInterval.calls.reset(); clock.install(); fakeGlobal.clearInterval('foo'); expect(delayedFunctionScheduler.removeFunctionWithId).toHaveBeenCalled(); expect(fakeClearInterval).not.toHaveBeenCalled(); }); it('does not install if the current setTimeout is not the original function on the global', function() { const originalFakeSetTimeout = function() {}, replacedSetTimeout = function() {}, fakeGlobal = { setTimeout: originalFakeSetTimeout }, delayedFunctionSchedulerFactory = jasmine.createSpy( 'delayedFunctionSchedulerFactory' ), mockDate = {}, clock = new jasmineUnderTest.Clock( fakeGlobal, delayedFunctionSchedulerFactory, mockDate ); fakeGlobal.setTimeout = replacedSetTimeout; expect(function() { clock.install(); }).toThrowError(/unable to install/); expect(delayedFunctionSchedulerFactory).not.toHaveBeenCalled(); expect(fakeGlobal.setTimeout).toBe(replacedSetTimeout); }); it('does not install if the current clearTimeout is not the original function on the global', function() { const originalFakeClearTimeout = function() {}, replacedClearTimeout = function() {}, fakeGlobal = { clearTimeout: originalFakeClearTimeout }, delayedFunctionSchedulerFactory = jasmine.createSpy( 'delayedFunctionSchedulerFactory' ), mockDate = {}, clock = new jasmineUnderTest.Clock( fakeGlobal, delayedFunctionSchedulerFactory, mockDate ); fakeGlobal.clearTimeout = replacedClearTimeout; expect(function() { clock.install(); }).toThrowError(/unable to install/); expect(delayedFunctionSchedulerFactory).not.toHaveBeenCalled(); expect(fakeGlobal.clearTimeout).toBe(replacedClearTimeout); }); it('does not install if the current setInterval is not the original function on the global', function() { const originalFakeSetInterval = function() {}, replacedSetInterval = function() {}, fakeGlobal = { setInterval: originalFakeSetInterval }, delayedFunctionSchedulerFactory = jasmine.createSpy( 'delayedFunctionSchedulerFactory' ), mockDate = {}, clock = new jasmineUnderTest.Clock( fakeGlobal, delayedFunctionSchedulerFactory, mockDate ); fakeGlobal.setInterval = replacedSetInterval; expect(function() { clock.install(); }).toThrowError(/unable to install/); expect(delayedFunctionSchedulerFactory).not.toHaveBeenCalled(); expect(fakeGlobal.setInterval).toBe(replacedSetInterval); }); it('does not install if the current clearInterval is not the original function on the global', function() { const originalFakeClearInterval = function() {}, replacedClearInterval = function() {}, fakeGlobal = { clearInterval: originalFakeClearInterval }, delayedFunctionSchedulerFactory = jasmine.createSpy( 'delayedFunctionSchedulerFactory' ), mockDate = {}, clock = new jasmineUnderTest.Clock( fakeGlobal, delayedFunctionSchedulerFactory, mockDate ); fakeGlobal.clearInterval = replacedClearInterval; expect(function() { clock.install(); }).toThrowError(/unable to install/); expect(delayedFunctionSchedulerFactory).not.toHaveBeenCalled(); expect(fakeGlobal.clearInterval).toBe(replacedClearInterval); }); it('replaces the global timer functions on uninstall', function() { const fakeSetTimeout = jasmine.createSpy('global setTimeout'), fakeClearTimeout = jasmine.createSpy('global clearTimeout'), fakeSetInterval = jasmine.createSpy('global setInterval'), fakeClearInterval = jasmine.createSpy('global clearInterval'), fakeGlobal = { setTimeout: fakeSetTimeout, clearTimeout: fakeClearTimeout, setInterval: fakeSetInterval, clearInterval: fakeClearInterval }, delayedFunctionScheduler = jasmine.createSpyObj( 'delayedFunctionScheduler', ['scheduleFunction', 'reset'] ), delayedFn = jasmine.createSpy('delayedFn'), mockDate = { install: function() {}, tick: function() {}, uninstall: function() {} }, clock = new jasmineUnderTest.Clock( fakeGlobal, function() { return delayedFunctionScheduler; }, mockDate ); clock.install(); clock.uninstall(); fakeGlobal.setTimeout(delayedFn, 0); fakeGlobal.clearTimeout('foo'); fakeGlobal.setInterval(delayedFn, 10); fakeGlobal.clearInterval('bar'); expect(fakeSetTimeout).toHaveBeenCalledWith(delayedFn, 0); expect(fakeClearTimeout).toHaveBeenCalledWith('foo'); expect(fakeSetInterval).toHaveBeenCalledWith(delayedFn, 10); expect(fakeClearInterval).toHaveBeenCalledWith('bar'); expect(delayedFunctionScheduler.scheduleFunction).not.toHaveBeenCalled(); }); it('can be installed for the duration of a passed in function and uninstalled when done', function() { const fakeSetTimeout = jasmine.createSpy('global setTimeout'), fakeClearTimeout = jasmine.createSpy('global clearTimeout'), fakeSetInterval = jasmine.createSpy('global setInterval'), fakeClearInterval = jasmine.createSpy('global clearInterval'), fakeGlobal = { setTimeout: fakeSetTimeout, clearTimeout: fakeClearTimeout, setInterval: fakeSetInterval, clearInterval: fakeClearInterval }, delayedFunctionScheduler = jasmine.createSpyObj( 'delayedFunctionScheduler', ['scheduleFunction', 'reset', 'removeFunctionWithId'] ), delayedFn = jasmine.createSpy('delayedFn'), mockDate = { install: function() {}, tick: function() {}, uninstall: function() {} }, clock = new jasmineUnderTest.Clock( fakeGlobal, function() { return delayedFunctionScheduler; }, mockDate ); let passedFunctionCalled = false; clock.withMock(function() { fakeGlobal.setTimeout(delayedFn, 0); fakeGlobal.clearTimeout('foo'); fakeGlobal.setInterval(delayedFn, 10); fakeGlobal.clearInterval('bar'); passedFunctionCalled = true; }); expect(passedFunctionCalled).toBe(true); expect(fakeSetTimeout).not.toHaveBeenCalled(); expect(fakeClearTimeout).not.toHaveBeenCalled(); expect(fakeSetInterval).not.toHaveBeenCalled(); expect(fakeClearInterval).not.toHaveBeenCalled(); expect(delayedFunctionScheduler.scheduleFunction).toHaveBeenCalled(); delayedFunctionScheduler.scheduleFunction.calls.reset(); fakeGlobal.setTimeout(delayedFn, 0); fakeGlobal.clearTimeout('foo'); fakeGlobal.setInterval(delayedFn, 10); fakeGlobal.clearInterval('bar'); expect(fakeSetTimeout).toHaveBeenCalledWith(delayedFn, 0); expect(fakeClearTimeout).toHaveBeenCalledWith('foo'); expect(fakeSetInterval).toHaveBeenCalledWith(delayedFn, 10); expect(fakeClearInterval).toHaveBeenCalledWith('bar'); expect(delayedFunctionScheduler.scheduleFunction).not.toHaveBeenCalled(); }); it('can be installed for the duration of a passed in function and uninstalled if an error is thrown', function() { const fakeSetTimeout = jasmine.createSpy('global setTimeout'), fakeClearTimeout = jasmine.createSpy('global clearTimeout'), fakeSetInterval = jasmine.createSpy('global setInterval'), fakeClearInterval = jasmine.createSpy('global clearInterval'), fakeGlobal = { setTimeout: fakeSetTimeout, clearTimeout: fakeClearTimeout, setInterval: fakeSetInterval, clearInterval: fakeClearInterval }, delayedFunctionScheduler = jasmine.createSpyObj( 'delayedFunctionScheduler', ['scheduleFunction', 'reset', 'removeFunctionWithId'] ), delayedFn = jasmine.createSpy('delayedFn'), mockDate = { install: function() {}, tick: function() {}, uninstall: function() {} }, clock = new jasmineUnderTest.Clock( fakeGlobal, function() { return delayedFunctionScheduler; }, mockDate ); let passedFunctionCalled = false; expect(function() { clock.withMock(function() { fakeGlobal.setTimeout(delayedFn, 0); fakeGlobal.clearTimeout('foo'); fakeGlobal.setInterval(delayedFn, 10); fakeGlobal.clearInterval('bar'); passedFunctionCalled = true; throw 'oops'; }); }).toThrow('oops'); expect(passedFunctionCalled).toBe(true); expect(fakeSetTimeout).not.toHaveBeenCalled(); expect(fakeClearTimeout).not.toHaveBeenCalled(); expect(fakeSetInterval).not.toHaveBeenCalled(); expect(fakeClearInterval).not.toHaveBeenCalled(); expect(delayedFunctionScheduler.scheduleFunction).toHaveBeenCalled(); delayedFunctionScheduler.scheduleFunction.calls.reset(); fakeGlobal.setTimeout(delayedFn, 0); fakeGlobal.clearTimeout('foo'); fakeGlobal.setInterval(delayedFn, 10); fakeGlobal.clearInterval('bar'); expect(fakeSetTimeout).toHaveBeenCalledWith(delayedFn, 0); expect(fakeClearTimeout).toHaveBeenCalledWith('foo'); expect(fakeSetInterval).toHaveBeenCalledWith(delayedFn, 10); expect(fakeClearInterval).toHaveBeenCalledWith('bar'); expect(delayedFunctionScheduler.scheduleFunction).not.toHaveBeenCalled(); }); it('schedules the delayed function (via setTimeout) with the fake timer', function() { const fakeSetTimeout = jasmine.createSpy('setTimeout'), scheduleFunction = jasmine.createSpy('scheduleFunction'), delayedFunctionScheduler = { scheduleFunction: scheduleFunction }, fakeGlobal = { setTimeout: fakeSetTimeout }, delayedFn = jasmine.createSpy('delayedFn'), mockDate = { install: function() {}, tick: function() {}, uninstall: function() {} }, clock = new jasmineUnderTest.Clock( fakeGlobal, function() { return delayedFunctionScheduler; }, mockDate ), timeout = new clock.FakeTimeout(); clock.install(); clock.setTimeout(delayedFn, 0, 'a', 'b'); expect(fakeSetTimeout).not.toHaveBeenCalled(); if (!NODE_JS) { expect(delayedFunctionScheduler.scheduleFunction).toHaveBeenCalledWith( delayedFn, 0, ['a', 'b'] ); } else { expect(delayedFunctionScheduler.scheduleFunction).toHaveBeenCalledWith( delayedFn, 0, ['a', 'b'], false, timeout ); } }); it('returns an id for the delayed function', function() { const fakeSetTimeout = jasmine.createSpy('setTimeout'), scheduleId = 123, scheduleFunction = jasmine .createSpy('scheduleFunction') .and.returnValue(scheduleId), delayedFunctionScheduler = { scheduleFunction: scheduleFunction }, fakeGlobal = { setTimeout: fakeSetTimeout }, delayedFn = jasmine.createSpy('delayedFn'), mockDate = { install: function() {}, tick: function() {}, uninstall: function() {} }, clock = new jasmineUnderTest.Clock( fakeGlobal, function() { return delayedFunctionScheduler; }, mockDate ); clock.install(); const timeout = clock.setTimeout(delayedFn, 0); if (!NODE_JS) { expect(timeout).toEqual(123); } else { expect(timeout.constructor.name).toEqual('FakeTimeout'); } }); it('clears the scheduled function with the scheduler', function() { const fakeClearTimeout = jasmine.createSpy('clearTimeout'), delayedFunctionScheduler = jasmine.createSpyObj( 'delayedFunctionScheduler', ['removeFunctionWithId'] ), fakeGlobal = { setTimeout: fakeClearTimeout }, mockDate = { install: function() {}, tick: function() {}, uninstall: function() {} }, clock = new jasmineUnderTest.Clock( fakeGlobal, function() { return delayedFunctionScheduler; }, mockDate ); clock.install(); clock.clearTimeout(123); expect(fakeClearTimeout).not.toHaveBeenCalled(); expect(delayedFunctionScheduler.removeFunctionWithId).toHaveBeenCalledWith( 123 ); }); it('schedules the delayed function with the fake timer', function() { const fakeSetInterval = jasmine.createSpy('setInterval'), scheduleFunction = jasmine.createSpy('scheduleFunction'), delayedFunctionScheduler = { scheduleFunction: scheduleFunction }, fakeGlobal = { setInterval: fakeSetInterval }, delayedFn = jasmine.createSpy('delayedFn'), mockDate = { install: function() {}, tick: function() {}, uninstall: function() {} }, clock = new jasmineUnderTest.Clock( fakeGlobal, function() { return delayedFunctionScheduler; }, mockDate ), timeout = new clock.FakeTimeout(); clock.install(); clock.setInterval(delayedFn, 0, 'a', 'b'); expect(fakeSetInterval).not.toHaveBeenCalled(); if (!NODE_JS) { expect(delayedFunctionScheduler.scheduleFunction).toHaveBeenCalledWith( delayedFn, 0, ['a', 'b'], true ); } else { expect(delayedFunctionScheduler.scheduleFunction).toHaveBeenCalledWith( delayedFn, 0, ['a', 'b'], true, timeout ); } }); it('returns an id for the delayed function', function() { const fakeSetInterval = jasmine.createSpy('setInterval'), scheduleId = 123, scheduleFunction = jasmine .createSpy('scheduleFunction') .and.returnValue(scheduleId), delayedFunctionScheduler = { scheduleFunction: scheduleFunction }, fakeGlobal = { setInterval: fakeSetInterval }, delayedFn = jasmine.createSpy('delayedFn'), mockDate = { install: function() {}, tick: function() {}, uninstall: function() {} }, clock = new jasmineUnderTest.Clock( fakeGlobal, function() { return delayedFunctionScheduler; }, mockDate ); clock.install(); const interval = clock.setInterval(delayedFn, 0); if (!NODE_JS) { expect(interval).toEqual(123); } else { expect(interval.constructor.name).toEqual('FakeTimeout'); } }); it('clears the scheduled function with the scheduler', function() { const clearInterval = jasmine.createSpy('clearInterval'), delayedFunctionScheduler = jasmine.createSpyObj( 'delayedFunctionScheduler', ['removeFunctionWithId'] ), fakeGlobal = { setInterval: clearInterval }, mockDate = { install: function() {}, tick: function() {}, uninstall: function() {} }, clock = new jasmineUnderTest.Clock( fakeGlobal, function() { return delayedFunctionScheduler; }, mockDate ); clock.install(); clock.clearInterval(123); expect(clearInterval).not.toHaveBeenCalled(); expect(delayedFunctionScheduler.removeFunctionWithId).toHaveBeenCalledWith( 123 ); }); it('gives you a friendly reminder if the Clock is not installed and you tick', function() { const clock = new jasmineUnderTest.Clock( {}, jasmine.createSpyObj('delayedFunctionScheduler', ['tick']) ); expect(function() { clock.tick(50); }).toThrow(); }); }); describe('Clock (acceptance)', function() { it('can run setTimeouts/setIntervals synchronously', function() { const delayedFn1 = jasmine.createSpy('delayedFn1'), delayedFn2 = jasmine.createSpy('delayedFn2'), delayedFn3 = jasmine.createSpy('delayedFn3'), recurring1 = jasmine.createSpy('recurring1'), delayedFunctionScheduler = new jasmineUnderTest.DelayedFunctionScheduler(), mockDate = { install: function() {}, tick: function() {}, uninstall: function() {} }, clock = new jasmineUnderTest.Clock( { setTimeout: setTimeout }, function() { return delayedFunctionScheduler; }, mockDate ); clock.install(); clock.setTimeout(delayedFn1, 0); const intervalId = clock.setInterval(recurring1, 50); clock.setTimeout(delayedFn2, 100); clock.setTimeout(delayedFn3, 200); expect(delayedFn1).not.toHaveBeenCalled(); expect(delayedFn2).not.toHaveBeenCalled(); expect(delayedFn3).not.toHaveBeenCalled(); clock.tick(0); expect(delayedFn1).toHaveBeenCalled(); expect(delayedFn2).not.toHaveBeenCalled(); expect(delayedFn3).not.toHaveBeenCalled(); clock.tick(50); expect(recurring1).toHaveBeenCalled(); expect(recurring1.calls.count()).toBe(1); expect(delayedFn2).not.toHaveBeenCalled(); expect(delayedFn3).not.toHaveBeenCalled(); clock.tick(50); expect(recurring1.calls.count()).toBe(2); expect(delayedFn2).toHaveBeenCalled(); expect(delayedFn3).not.toHaveBeenCalled(); clock.tick(100); expect(recurring1.calls.count()).toBe(4); expect(delayedFn3).toHaveBeenCalled(); clock.clearInterval(intervalId); clock.tick(50); expect(recurring1.calls.count()).toBe(4); }); it('can clear a previously set timeout', function() { const clearedFn = jasmine.createSpy('clearedFn'), delayedFunctionScheduler = new jasmineUnderTest.DelayedFunctionScheduler(), mockDate = { install: function() {}, tick: function() {}, uninstall: function() {} }, clock = new jasmineUnderTest.Clock( { setTimeout: function() {} }, function() { return delayedFunctionScheduler; }, mockDate ); clock.install(); const timeoutId = clock.setTimeout(clearedFn, 100); expect(clearedFn).not.toHaveBeenCalled(); clock.clearTimeout(timeoutId); clock.tick(100); expect(clearedFn).not.toHaveBeenCalled(); }); it("can clear a previously set interval using that interval's handler", function() { const spy = jasmine.createSpy('spy'), delayedFunctionScheduler = new jasmineUnderTest.DelayedFunctionScheduler(), mockDate = { install: function() {}, tick: function() {}, uninstall: function() {} }, clock = new jasmineUnderTest.Clock( { setInterval: function() {} }, function() { return delayedFunctionScheduler; }, mockDate ); clock.install(); const intervalId = clock.setInterval(function() { spy(); clock.clearInterval(intervalId); }, 100); clock.tick(200); expect(spy.calls.count()).toEqual(1); }); it('correctly schedules functions after the Clock has advanced', function() { const delayedFn1 = jasmine.createSpy('delayedFn1'), delayedFunctionScheduler = new jasmineUnderTest.DelayedFunctionScheduler(), mockDate = { install: function() {}, tick: function() {}, uninstall: function() {} }, clock = new jasmineUnderTest.Clock( { setTimeout: function() {} }, function() { return delayedFunctionScheduler; }, mockDate ); clock.install(); clock.tick(100); clock.setTimeout(delayedFn1, 10, ['some', 'arg']); clock.tick(5); expect(delayedFn1).not.toHaveBeenCalled(); clock.tick(5); expect(delayedFn1).toHaveBeenCalled(); }); it('correctly schedules functions while the Clock is advancing', function() { const delayedFn1 = jasmine.createSpy('delayedFn1'), delayedFn2 = jasmine.createSpy('delayedFn2'), delayedFunctionScheduler = new jasmineUnderTest.DelayedFunctionScheduler(), mockDate = { install: function() {}, tick: function() {}, uninstall: function() {} }, clock = new jasmineUnderTest.Clock( { setTimeout: function() {} }, function() { return delayedFunctionScheduler; }, mockDate ); delayedFn1.and.callFake(function() { clock.setTimeout(delayedFn2, 0); }); clock.install(); clock.setTimeout(delayedFn1, 5); clock.tick(5); expect(delayedFn1).toHaveBeenCalled(); expect(delayedFn2).not.toHaveBeenCalled(); clock.tick(); expect(delayedFn2).toHaveBeenCalled(); }); it('correctly calls functions scheduled while the Clock is advancing', function() { const delayedFn1 = jasmine.createSpy('delayedFn1'), delayedFn2 = jasmine.createSpy('delayedFn2'), delayedFunctionScheduler = new jasmineUnderTest.DelayedFunctionScheduler(), mockDate = { install: function() {}, tick: function() {}, uninstall: function() {} }, clock = new jasmineUnderTest.Clock( { setTimeout: function() {} }, function() { return delayedFunctionScheduler; }, mockDate ); delayedFn1.and.callFake(function() { clock.setTimeout(delayedFn2, 1); }); clock.install(); clock.setTimeout(delayedFn1, 5); clock.tick(6); expect(delayedFn1).toHaveBeenCalled(); expect(delayedFn2).toHaveBeenCalled(); }); it('correctly schedules functions scheduled while the Clock is advancing but after the Clock is uninstalled', function() { const delayedFn1 = jasmine.createSpy('delayedFn1'), delayedFn2 = jasmine.createSpy('delayedFn2'), delayedFunctionScheduler = new jasmineUnderTest.DelayedFunctionScheduler(), mockDate = { install: function() {}, tick: function() {}, uninstall: function() {} }, clock = new jasmineUnderTest.Clock( { setTimeout: function() {} }, function() { return delayedFunctionScheduler; }, mockDate ); delayedFn1.and.callFake(function() { clock.uninstall(); clock.install(); clock.setTimeout(delayedFn2, 0); }); clock.install(); clock.setTimeout(delayedFn1, 1); clock.tick(1); expect(delayedFn1).toHaveBeenCalled(); expect(delayedFn2).not.toHaveBeenCalled(); clock.tick(1); expect(delayedFn2).toHaveBeenCalled(); }); it('does not mock the Date object by default', function() { const delayedFunctionScheduler = new jasmineUnderTest.DelayedFunctionScheduler(), global = { Date: Date }, mockDate = new jasmineUnderTest.MockDate(global), clock = new jasmineUnderTest.Clock( { setTimeout: setTimeout }, function() { return delayedFunctionScheduler; }, mockDate ); clock.install(); expect(global.Date).toEqual(Date); const now = new global.Date().getTime(); clock.tick(50); expect(new global.Date().getTime() - now).not.toEqual(50); }); it('mocks the Date object and sets it to current time', function() { const delayedFunctionScheduler = new jasmineUnderTest.DelayedFunctionScheduler(), global = { Date: Date }, mockDate = new jasmineUnderTest.MockDate(global), clock = new jasmineUnderTest.Clock( { setTimeout: setTimeout }, function() { return delayedFunctionScheduler; }, mockDate ); clock.install().mockDate(); const now = new global.Date().getTime(); clock.tick(50); expect(new global.Date().getTime() - now).toEqual(50); let timeoutDate = 0; clock.setTimeout(function() { timeoutDate = new global.Date().getTime(); }, 100); clock.tick(100); expect(timeoutDate - now).toEqual(150); }); it('mocks the Date object and sets it to a given time', function() { const delayedFunctionScheduler = new jasmineUnderTest.DelayedFunctionScheduler(), global = { Date: Date }, mockDate = new jasmineUnderTest.MockDate(global), clock = new jasmineUnderTest.Clock( { setTimeout: setTimeout }, function() { return delayedFunctionScheduler; }, mockDate ), baseTime = new Date(2013, 9, 23); clock.install().mockDate(baseTime); const now = new global.Date().getTime(); expect(now).toEqual(baseTime.getTime()); clock.tick(50); expect(new global.Date().getTime()).toEqual(baseTime.getTime() + 50); let timeoutDate = 0; clock.setTimeout(function() { timeoutDate = new global.Date().getTime(); }, 100); clock.tick(100); expect(timeoutDate).toEqual(baseTime.getTime() + 150); }); it('throws mockDate is called with a non-Date', function() { const delayedFunctionScheduler = new jasmineUnderTest.DelayedFunctionScheduler(), global = { Date: Date }, mockDate = new jasmineUnderTest.MockDate(global), clock = new jasmineUnderTest.Clock( { setTimeout: setTimeout }, function() { return delayedFunctionScheduler; }, mockDate ); expect(() => clock.mockDate(12345)).toThrowError( 'The argument to jasmine.clock().mockDate(), if specified, should be ' + 'a Date instance.' ); }); it('mocks the Date object and updates the date per delayed function', function() { const delayedFunctionScheduler = new jasmineUnderTest.DelayedFunctionScheduler(), global = { Date: Date }, mockDate = new jasmineUnderTest.MockDate(global), clock = new jasmineUnderTest.Clock( { setTimeout: setTimeout }, function() { return delayedFunctionScheduler; }, mockDate ), baseTime = new Date(); clock.install().mockDate(baseTime); const actualTimes = []; const pushCurrentTime = function() { actualTimes.push(global.Date().getTime()); }; delayedFunctionScheduler.scheduleFunction(pushCurrentTime); delayedFunctionScheduler.scheduleFunction(pushCurrentTime, 1); delayedFunctionScheduler.scheduleFunction(pushCurrentTime, 3); clock.tick(1); expect(global.Date().getTime()).toEqual(baseTime.getTime() + 1); clock.tick(3); expect(global.Date().getTime()).toEqual(baseTime.getTime() + 4); clock.tick(1); expect(global.Date().getTime()).toEqual(baseTime.getTime() + 5); expect(actualTimes).toEqual([ baseTime.getTime(), baseTime.getTime() + 1, baseTime.getTime() + 3 ]); }); it('correctly clears a scheduled timeout while the Clock is advancing', function() { const delayedFunctionScheduler = new jasmineUnderTest.DelayedFunctionScheduler(), global = { Date: Date, setTimeout: undefined }, mockDate = new jasmineUnderTest.MockDate(global), clock = new jasmineUnderTest.Clock( global, function() { return delayedFunctionScheduler; }, mockDate ); clock.install(); let timerId2; global.setTimeout(function() { global.clearTimeout(timerId2); }, 100); timerId2 = global.setTimeout(fail, 100); clock.tick(100); }); it('correctly clears a scheduled interval while the Clock is advancing', function() { const delayedFunctionScheduler = new jasmineUnderTest.DelayedFunctionScheduler(), global = { Date: Date, setTimeout: undefined }, mockDate = new jasmineUnderTest.MockDate(global), clock = new jasmineUnderTest.Clock( global, function() { return delayedFunctionScheduler; }, mockDate ); clock.install(); let timerId2; global.setInterval(function() { global.clearInterval(timerId2); }, 100); timerId2 = global.setInterval(fail, 100); clock.tick(400); }); }); jasmine-4.5.0/spec/core/CompleteOnFirstErrorSkipPolicySpec.js000066400000000000000000000073051432731766000243150ustar00rootroot00000000000000describe('CompleteOnFirstErrorSkipPolicy', function() { describe('#skipTo', function() { describe('Before anything has errored', function() { it('returns the next index', function() { const policy = new jasmineUnderTest.CompleteOnFirstErrorSkipPolicy( arrayOfArbitraryFns(4), 4 ); expect(policy.skipTo(1)).toEqual(2); }); }); describe('After something has errored', function() { it('skips non cleanup fns', function() { const fns = arrayOfArbitraryFns(4); fns[2].type = arbitraryCleanupType(); fns[3].type = arbitraryCleanupType(); const policy = new jasmineUnderTest.CompleteOnFirstErrorSkipPolicy(fns); policy.fnErrored(0); expect(policy.skipTo(0)).toEqual(2); expect(policy.skipTo(2)).toEqual(3); expect(policy.skipTo(3)).toEqual(4); }); for (const type of ['afterEach', 'specCleanup', 'afterAll']) { it(`does not skip ${type} fns`, function() { const fns = arrayOfArbitraryFns(2); fns[1].type = type; const policy = new jasmineUnderTest.CompleteOnFirstErrorSkipPolicy( fns ); policy.fnErrored(0); expect(policy.skipTo(0)).toEqual(1); }); } describe('When the error was in a beforeEach fn', function() { it('runs cleanup fns defined by the current and containing suites', function() { const parentSuite = { description: 'parentSuite' }; const suite = { description: 'suite', parentSuite }; const fns = [ { suite: suite }, { fn: () => {} }, { fn: () => {}, suite: suite, type: arbitraryCleanupType() }, { fn: () => {}, suite: parentSuite, type: arbitraryCleanupType() } ]; const policy = new jasmineUnderTest.CompleteOnFirstErrorSkipPolicy( fns ); policy.fnErrored(0); expect(policy.skipTo(0)).toEqual(2); expect(policy.skipTo(2)).toEqual(3); }); it('skips cleanup fns defined by nested suites', function() { const parentSuite = { description: 'parentSuite' }; const suite = { description: 'suite', parentSuite }; const fns = [ { fn: () => {}, type: 'beforeEach', suite: parentSuite }, { fn: () => {} }, { fn: () => {}, suite: suite, type: arbitraryCleanupType() }, { fn: () => {}, suite: parentSuite, type: arbitraryCleanupType() } ]; const policy = new jasmineUnderTest.CompleteOnFirstErrorSkipPolicy( fns ); policy.fnErrored(0); expect(policy.skipTo(0)).toEqual(3); }); }); it('does not skip cleanup fns that have no suite, such as the spec complete fn', function() { const fns = [ { fn: () => {} }, { fn: () => {}, type: arbitraryCleanupType() } ]; const policy = new jasmineUnderTest.CompleteOnFirstErrorSkipPolicy(fns); policy.fnErrored(0); expect(policy.skipTo(0)).toEqual(1); }); }); }); function arrayOfArbitraryFns(n) { const result = []; for (let i = 0; i < n; i++) { result.push({ fn: () => {} }); } return result; } function arbitraryCleanupType() { return 'specCleanup'; } }); jasmine-4.5.0/spec/core/DelayedFunctionSchedulerSpec.js000066400000000000000000000230241432731766000231270ustar00rootroot00000000000000describe('DelayedFunctionScheduler', function() { it('schedules a function for later execution', function() { const scheduler = new jasmineUnderTest.DelayedFunctionScheduler(), fn = jasmine.createSpy('fn'); scheduler.scheduleFunction(fn, 0); expect(fn).not.toHaveBeenCalled(); scheduler.tick(0); expect(fn).toHaveBeenCalled(); }); it('schedules a string for later execution', function() { const scheduler = new jasmineUnderTest.DelayedFunctionScheduler(), strfn = 'horrible = true;'; scheduler.scheduleFunction(strfn, 0); scheduler.tick(0); expect(horrible).toEqual(true); }); it('#tick defaults to 0', function() { const scheduler = new jasmineUnderTest.DelayedFunctionScheduler(), fn = jasmine.createSpy('fn'); scheduler.scheduleFunction(fn, 0); expect(fn).not.toHaveBeenCalled(); scheduler.tick(); expect(fn).toHaveBeenCalled(); }); it('defaults delay to 0', function() { const scheduler = new jasmineUnderTest.DelayedFunctionScheduler(), fn = jasmine.createSpy('fn'); scheduler.scheduleFunction(fn); expect(fn).not.toHaveBeenCalled(); scheduler.tick(0); expect(fn).toHaveBeenCalled(); }); it('optionally passes params to scheduled functions', function() { const scheduler = new jasmineUnderTest.DelayedFunctionScheduler(), fn = jasmine.createSpy('fn'); scheduler.scheduleFunction(fn, 0, ['foo', 'bar']); expect(fn).not.toHaveBeenCalled(); scheduler.tick(0); expect(fn).toHaveBeenCalledWith('foo', 'bar'); }); it('scheduled fns can optionally reoccur', function() { const scheduler = new jasmineUnderTest.DelayedFunctionScheduler(), fn = jasmine.createSpy('fn'); scheduler.scheduleFunction(fn, 20, [], true); expect(fn).not.toHaveBeenCalled(); scheduler.tick(20); expect(fn.calls.count()).toBe(1); scheduler.tick(40); expect(fn.calls.count()).toBe(3); scheduler.tick(21); expect(fn.calls.count()).toBe(4); }); it('increments scheduled fns ids unless one is passed', function() { const scheduler = new jasmineUnderTest.DelayedFunctionScheduler(); 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(3); }); it('#removeFunctionWithId removes a previously scheduled function with a given id', function() { const scheduler = new jasmineUnderTest.DelayedFunctionScheduler(), fn = jasmine.createSpy('fn'), timeoutKey = scheduler.scheduleFunction(fn, 0); expect(fn).not.toHaveBeenCalled(); scheduler.removeFunctionWithId(timeoutKey); scheduler.tick(0); expect(fn).not.toHaveBeenCalled(); }); it('executes recurring functions interleaved with regular functions in the correct order', function() { const scheduler = new jasmineUnderTest.DelayedFunctionScheduler(); const fn = jasmine.createSpy('fn'); let recurringCallCount = 0; const recurring = jasmine.createSpy('recurring').and.callFake(function() { recurringCallCount++; if (recurringCallCount < 5) { expect(fn).not.toHaveBeenCalled(); } }); scheduler.scheduleFunction(recurring, 10, [], true); scheduler.scheduleFunction(fn, 50); scheduler.tick(60); expect(recurring).toHaveBeenCalled(); expect(recurring.calls.count()).toBe(6); expect(fn).toHaveBeenCalled(); }); it('schedules a function for later execution during a tick', function() { const scheduler = new jasmineUnderTest.DelayedFunctionScheduler(), fn = jasmine.createSpy('fn'), fnDelay = 10; scheduler.scheduleFunction(function() { scheduler.scheduleFunction(fn, fnDelay); }, 0); expect(fn).not.toHaveBeenCalled(); scheduler.tick(fnDelay); expect(fn).toHaveBeenCalled(); }); it('#removeFunctionWithId removes a previously scheduled function with a given id during a tick', function() { const scheduler = new jasmineUnderTest.DelayedFunctionScheduler(), fn = jasmine.createSpy('fn'), fnDelay = 10; let timeoutKey; scheduler.scheduleFunction(function() { scheduler.removeFunctionWithId(timeoutKey); }, 0); timeoutKey = scheduler.scheduleFunction(fn, fnDelay); expect(fn).not.toHaveBeenCalled(); scheduler.tick(fnDelay); expect(fn).not.toHaveBeenCalled(); }); it('executes recurring functions interleaved with regular functions and functions scheduled during a tick in the correct order', function() { const scheduler = new jasmineUnderTest.DelayedFunctionScheduler(); const fn = jasmine.createSpy('fn'); let recurringCallCount = 0; const recurring = jasmine.createSpy('recurring').and.callFake(function() { recurringCallCount++; if (recurringCallCount < 5) { expect(fn).not.toHaveBeenCalled(); } }); const innerFn = jasmine.createSpy('innerFn').and.callFake(function() { expect(recurring.calls.count()).toBe(4); expect(fn).not.toHaveBeenCalled(); }); const scheduling = jasmine.createSpy('scheduling').and.callFake(function() { expect(recurring.calls.count()).toBe(3); expect(fn).not.toHaveBeenCalled(); scheduler.scheduleFunction(innerFn, 10); // 41ms absolute }); scheduler.scheduleFunction(recurring, 10, [], true); scheduler.scheduleFunction(fn, 50); scheduler.scheduleFunction(scheduling, 31); scheduler.tick(60); expect(recurring).toHaveBeenCalled(); expect(recurring.calls.count()).toBe(6); expect(fn).toHaveBeenCalled(); expect(scheduling).toHaveBeenCalled(); expect(innerFn).toHaveBeenCalled(); }); it('executes recurring functions after rescheduling them', function() { const scheduler = new jasmineUnderTest.DelayedFunctionScheduler(), recurring = function() { expect(scheduler.scheduleFunction).toHaveBeenCalled(); }; scheduler.scheduleFunction(recurring, 10, [], true); spyOn(scheduler, 'scheduleFunction'); scheduler.tick(10); }); it('removes functions during a tick that runs the function', function() { const scheduler = new jasmineUnderTest.DelayedFunctionScheduler(), spy = jasmine.createSpy('fn'), spyAndRemove = jasmine.createSpy('fn'), fnDelay = 10; let timeoutKey; spyAndRemove.and.callFake(function() { scheduler.removeFunctionWithId(timeoutKey); }); scheduler.scheduleFunction(spyAndRemove, fnDelay); timeoutKey = scheduler.scheduleFunction(spy, fnDelay, [], true); scheduler.tick(2 * fnDelay); expect(spy).not.toHaveBeenCalled(); expect(spyAndRemove).toHaveBeenCalled(); }); it('removes functions during the first tick that runs the function', function() { const scheduler = new jasmineUnderTest.DelayedFunctionScheduler(), fn = jasmine.createSpy('fn'), fnDelay = 10; let timeoutKey; timeoutKey = scheduler.scheduleFunction(fn, fnDelay, [], true); scheduler.scheduleFunction(function() { scheduler.removeFunctionWithId(timeoutKey); }, fnDelay); expect(fn).not.toHaveBeenCalled(); scheduler.tick(3 * fnDelay); expect(fn).toHaveBeenCalled(); expect(fn.calls.count()).toBe(1); }); it("does not remove a function that hasn't been added yet", function() { const scheduler = new jasmineUnderTest.DelayedFunctionScheduler(), fn = jasmine.createSpy('fn'), fnDelay = 10; scheduler.removeFunctionWithId('foo'); scheduler.scheduleFunction(fn, fnDelay, [], false, 'foo'); expect(fn).not.toHaveBeenCalled(); scheduler.tick(fnDelay + 1); expect(fn).toHaveBeenCalled(); }); it('updates the mockDate per scheduled time', function() { const scheduler = new jasmineUnderTest.DelayedFunctionScheduler(), tickDate = jasmine.createSpy('tickDate'); scheduler.scheduleFunction(function() {}); scheduler.scheduleFunction(function() {}, 1); scheduler.tick(1, tickDate); expect(tickDate).toHaveBeenCalledWith(0); expect(tickDate).toHaveBeenCalledWith(1); }); describe('ticking inside a scheduled function', function() { let clock; // Runner function calls the callback until it returns false function runWork(workCallback) { while (workCallback()) {} } // Make a worker that takes a little time and tracks when it finished function mockWork(times) { return () => { clock.tick(1); const now = new Date().getTime(); expect(lastWork) .withContext('Previous function calls should always be in the past') .toBeLessThan(now); lastWork = now; times--; return times > 0; }; } let lastWork = 0; beforeEach(() => { clock = jasmineUnderTest.getEnv().clock; clock.install(); clock.mockDate(new Date(1)); }); afterEach(function() { jasmineUnderTest.getEnv().clock.uninstall(); }); it('preserves monotonically-increasing current time', () => { const work1 = mockWork(3); setTimeout(() => { runWork(work1); }, 1); clock.tick(1); expect(lastWork) .withContext('tick should advance past last-scheduled function') .toBeLessThanOrEqual(new Date().getTime()); const work2 = mockWork(3); setTimeout(() => { runWork(work2); }, 1); clock.tick(1); expect(lastWork) .withContext('tick should advance past last-scheduled function') .toBeLessThanOrEqual(new Date().getTime()); }); }); }); jasmine-4.5.0/spec/core/DeprecatorSpec.js000066400000000000000000000263111432731766000203050ustar00rootroot00000000000000/* eslint no-console: 0 */ describe('Deprecator', function() { describe('#deprecate', function() { beforeEach(function() { spyOn(console, 'error'); }); it('logs the mesage without context when the runnable is the top suite', function() { const runnable = { addDeprecationWarning: function() {} }; const deprecator = new jasmineUnderTest.Deprecator(runnable); deprecator.verboseDeprecations(true); deprecator.addDeprecationWarning(runnable, 'the message', { omitStackTrace: true }); expect(console.error).toHaveBeenCalledWith('DEPRECATION: the message'); }); it('logs the message in a descendant suite', function() { const runnable = { addDeprecationWarning: function() {}, getFullName: function() { return 'the suite'; }, children: [] }; const deprecator = new jasmineUnderTest.Deprecator({}); deprecator.verboseDeprecations(true); deprecator.addDeprecationWarning(runnable, 'the message', { omitStackTrace: true }); expect(console.error).toHaveBeenCalledWith( 'DEPRECATION: the message (in suite: the suite)' ); }); it('logs and reports the message in a spec', function() { const runnable = { addDeprecationWarning: function() {}, getFullName: function() { return 'the spec'; } }; const deprecator = new jasmineUnderTest.Deprecator({}); deprecator.verboseDeprecations(true); deprecator.addDeprecationWarning(runnable, 'the message', { omitStackTrace: true }); expect(console.error).toHaveBeenCalledWith( 'DEPRECATION: the message (in spec: the spec)' ); }); it('logs and reports the message without runnable info when ignoreRunnable is true', function() { const topSuite = jasmine.createSpyObj('topSuite', [ 'addDeprecationWarning', 'getFullName' ]); const deprecator = new jasmineUnderTest.Deprecator(topSuite); const runnable = jasmine.createSpyObj('spec', [ 'addDeprecationWarning', 'getFullName' ]); runnable.getFullName.and.returnValue('a spec'); deprecator.addDeprecationWarning(runnable, 'the message', { ignoreRunnable: true }); expect(topSuite.addDeprecationWarning).toHaveBeenCalledWith( jasmine.objectContaining({ message: jasmine.stringMatching(/^the message/) }) ); expect(runnable.addDeprecationWarning).not.toHaveBeenCalled(); expect(console.error).toHaveBeenCalledWith( jasmine.stringMatching(/the message/) ); expect(console.error).not.toHaveBeenCalledWith( jasmine.stringMatching(/a spec/) ); }); describe('with no options', function() { it('includes the stack trace', function() { testStackTrace(undefined); }); }); it('omits the stack trace when omitStackTrace is true', function() { testNoStackTrace({ omitStackTrace: true }); }); it('includes the stack trace when omitStackTrace is false', function() { testStackTrace({ omitStackTrace: false }); }); it('includes the stack trace when omitStackTrace is undefined', function() { testStackTrace({ includeStackTrace: undefined }); }); it('emits the deprecation only once when verboseDeprecations is not set', function() { const deprecator = new jasmineUnderTest.Deprecator({}); const runnable1 = jasmine.createSpyObj('runnable1', [ 'addDeprecationWarning', 'getFullName' ]); const runnable2 = jasmine.createSpyObj('runnable2', [ 'addDeprecationWarning', 'getFullName' ]); deprecator.addDeprecationWarning(runnable1, 'the message'); deprecator.addDeprecationWarning(runnable1, 'the message'); deprecator.addDeprecationWarning(runnable2, 'the message'); expect(runnable1.addDeprecationWarning).toHaveBeenCalledTimes(1); expect(runnable2.addDeprecationWarning).not.toHaveBeenCalled(); }); it('emits the deprecation only once when verboseDeprecations is false', function() { const deprecator = new jasmineUnderTest.Deprecator({}); const runnable1 = jasmine.createSpyObj('runnable1', [ 'addDeprecationWarning', 'getFullName' ]); const runnable2 = jasmine.createSpyObj('runnable2', [ 'addDeprecationWarning', 'getFullName' ]); deprecator.verboseDeprecations(false); deprecator.addDeprecationWarning(runnable1, 'the message'); deprecator.addDeprecationWarning(runnable1, 'the message'); deprecator.addDeprecationWarning(runnable2, 'the message'); expect(runnable1.addDeprecationWarning).toHaveBeenCalledTimes(1); expect(runnable2.addDeprecationWarning).not.toHaveBeenCalled(); }); it('emits the deprecation for each call when verboseDeprecations is true', function() { const deprecator = new jasmineUnderTest.Deprecator({}); const runnable1 = jasmine.createSpyObj('runnable1', [ 'addDeprecationWarning', 'getFullName' ]); const runnable2 = jasmine.createSpyObj('runnable2', [ 'addDeprecationWarning', 'getFullName' ]); deprecator.verboseDeprecations(true); deprecator.addDeprecationWarning(runnable1, 'the message'); deprecator.addDeprecationWarning(runnable1, 'the message'); deprecator.addDeprecationWarning(runnable2, 'the message'); expect(runnable1.addDeprecationWarning).toHaveBeenCalledTimes(2); expect(runnable2.addDeprecationWarning).toHaveBeenCalled(); }); it('includes a note about verboseDeprecations', function() { const deprecator = new jasmineUnderTest.Deprecator({}); const runnable = jasmine.createSpyObj('runnable', [ 'addDeprecationWarning', 'getFullName' ]); deprecator.addDeprecationWarning(runnable, 'the message'); expect(runnable.addDeprecationWarning).toHaveBeenCalledTimes(1); expect( runnable.addDeprecationWarning.calls.argsFor(0)[0].message ).toContain(verboseDeprecationsNote()); expect(console.error).toHaveBeenCalledTimes(1); expect(console.error.calls.argsFor(0)[0]).toContain( verboseDeprecationsNote() ); }); it('omits the note about verboseDeprecations when verboseDeprecations is true', function() { const deprecator = new jasmineUnderTest.Deprecator({}); const runnable = jasmine.createSpyObj('runnable', [ 'addDeprecationWarning', 'getFullName' ]); deprecator.verboseDeprecations(true); deprecator.addDeprecationWarning(runnable, 'the message'); expect(runnable.addDeprecationWarning).toHaveBeenCalledTimes(1); expect( runnable.addDeprecationWarning.calls.argsFor(0)[0].message ).not.toContain(verboseDeprecationsNote()); expect(console.error).toHaveBeenCalledTimes(1); expect(console.error.calls.argsFor(0)[0]).not.toContain( verboseDeprecationsNote() ); }); describe('When the deprecation is an Error', function() { // This form is used by external systems like atom-jasmine3-test-runner // to report their own deprecations through Jasmine. See // . it('passes the error through unchanged', function() { const deprecator = new jasmineUnderTest.Deprecator({}); const runnable = jasmine.createSpyObj('runnable', [ 'addDeprecationWarning', 'getFullName' ]); let deprecation, originalStack; try { throw new Error('the deprecation'); } catch (err) { deprecation = err; originalStack = err.stack; } deprecator.addDeprecationWarning(runnable, deprecation); expect(runnable.addDeprecationWarning).toHaveBeenCalledTimes(1); expect( runnable.addDeprecationWarning.calls.argsFor(0)[0].message ).toEqual('the deprecation'); expect(runnable.addDeprecationWarning.calls.argsFor(0)[0].stack).toBe( originalStack ); expect(console.error).toHaveBeenCalledTimes(1); expect(console.error.calls.argsFor(0)[0].message).toEqual( 'the deprecation' ); expect(console.error.calls.argsFor(0)[0].stack).toEqual(originalStack); }); it('reports the deprecation every time, regardless of config.verboseDeprecations', function() { const deprecator = new jasmineUnderTest.Deprecator({}); const runnable = jasmine.createSpyObj('runnable', [ 'addDeprecationWarning', 'getFullName' ]); let deprecation; try { throw new Error('the deprecation'); } catch (err) { deprecation = err; } deprecator.addDeprecationWarning(runnable, deprecation); deprecator.addDeprecationWarning(runnable, deprecation); expect(runnable.addDeprecationWarning).toHaveBeenCalledTimes(2); expect(console.error).toHaveBeenCalledTimes(2); }); it('omits the note about verboseDeprecations', function() { const deprecator = new jasmineUnderTest.Deprecator({}); const runnable = jasmine.createSpyObj('runnable', [ 'addDeprecationWarning', 'getFullName' ]); let deprecation; try { throw new Error('the deprecation'); } catch (err) { deprecation = err; } deprecator.addDeprecationWarning(runnable, deprecation); expect(runnable.addDeprecationWarning).toHaveBeenCalledTimes(1); expect( runnable.addDeprecationWarning.calls.argsFor(0)[0].message ).not.toContain(verboseDeprecationsNote()); expect(console.error).toHaveBeenCalledTimes(1); expect(console.error.calls.argsFor(0)[0]).not.toContain( verboseDeprecationsNote() ); }); }); function verboseDeprecationsNote() { return ( 'Note: This message will be shown only once. Set the ' + 'verboseDeprecations config property to true to see every occurrence.' ); } function testStackTrace(options) { const deprecator = new jasmineUnderTest.Deprecator({}); const runnable = jasmine.createSpyObj('runnable', [ 'addDeprecationWarning', 'getFullName' ]); deprecator.addDeprecationWarning(runnable, 'the message', options); expect(runnable.addDeprecationWarning).toHaveBeenCalledWith({ message: jasmine.stringMatching(/^the message/), omitStackTrace: false }); expect(console.error).toHaveBeenCalledTimes(1); expect(console.error.calls.argsFor(0)[0]).toContain('the message'); expect(console.error.calls.argsFor(0)[0]).toContain('DeprecatorSpec.js'); } function testNoStackTrace(options) { const deprecator = new jasmineUnderTest.Deprecator({}); const runnable = jasmine.createSpyObj('runnable', [ 'addDeprecationWarning', 'getFullName' ]); deprecator.addDeprecationWarning(runnable, 'the message', options); expect(runnable.addDeprecationWarning).toHaveBeenCalledWith({ message: jasmine.stringMatching(/^the message/), omitStackTrace: true }); } }); }); jasmine-4.5.0/spec/core/EnvSpec.js000066400000000000000000000472651432731766000167600ustar00rootroot00000000000000// TODO: Fix these unit tests! describe('Env', function() { let env; beforeEach(function() { env = new jasmineUnderTest.Env(); }); afterEach(function() { env.cleanup_(); }); describe('#pending', function() { it('throws the Pending Spec exception', function() { expect(function() { env.pending(); }).toThrow(jasmineUnderTest.Spec.pendingSpecExceptionMessage); }); it('throws the Pending Spec exception with a custom message', function() { expect(function() { env.pending('custom message'); }).toThrow( jasmineUnderTest.Spec.pendingSpecExceptionMessage + 'custom message' ); }); }); describe('#topSuite', function() { it('returns an object that describes the tree of suites and specs', function() { spyOn(env, 'deprecated'); env.it('a top level spec'); env.describe('a suite', function() { env.it('a spec'); env.describe('a nested suite', function() { env.it('a nested spec'); }); }); const suite = env.topSuite(); expect(suite).not.toBeInstanceOf(jasmineUnderTest.Suite); expect(suite.description).toEqual('Jasmine__TopLevel__Suite'); expect(suite.getFullName()).toEqual(''); expect(suite.children.length).toEqual(2); expect(suite.children[0]).not.toBeInstanceOf(jasmineUnderTest.Spec); expect(suite.children[0].description).toEqual('a top level spec'); expect(suite.children[0].getFullName()).toEqual('a top level spec'); expect(suite.children[0].children).toBeFalsy(); expect(suite.children[1]).not.toBeInstanceOf(jasmineUnderTest.Suite); expect(suite.children[1].description).toEqual('a suite'); expect(suite.children[1].getFullName()).toEqual('a suite'); expect(suite.children[1].parentSuite).toBe(suite); expect(suite.children[1].children.length).toEqual(2); expect(suite.children[1].children[0]).not.toBeInstanceOf( jasmineUnderTest.Spec ); expect(suite.children[1].children[0].description).toEqual('a spec'); expect(suite.children[1].children[0].getFullName()).toEqual( 'a suite a spec' ); expect(suite.children[1].children[0].children).toBeFalsy(); expect(suite.children[1].children[1].description).toEqual( 'a nested suite' ); expect(suite.children[1].children[1].getFullName()).toEqual( 'a suite a nested suite' ); expect(suite.children[1].children[1].parentSuite).toBe(suite.children[1]); expect(suite.children[1].children[1].children.length).toEqual(1); expect(suite.children[1].children[1].children[0].description).toEqual( 'a nested spec' ); expect(suite.children[1].children[1].children[0].getFullName()).toEqual( 'a suite a nested suite a nested spec' ); expect(suite.children[1].children[1].children[0].children).toBeFalsy(); }); }); it('accepts its own current configureation', function() { env.configure(env.configuration()); }); it('can configure specs to throw errors on expectation failures', function() { env.configure({ stopSpecOnExpectationFailure: true }); spyOn(jasmineUnderTest, 'Spec').and.callThrough(); env.it('foo', function() {}); expect(jasmineUnderTest.Spec).toHaveBeenCalledWith( jasmine.objectContaining({ throwOnExpectationFailure: true }) ); }); it('can configure suites to throw errors on expectation failures', function() { env.configure({ stopSpecOnExpectationFailure: true }); spyOn(jasmineUnderTest, 'Suite'); env.describe('foo', function() {}); expect(jasmineUnderTest.Suite).toHaveBeenCalledWith( jasmine.objectContaining({ throwOnExpectationFailure: true }) ); }); it('ignores configuration properties that are present but undefined', function() { spyOn(env, 'deprecated'); const initialConfig = { random: true, seed: '123', failSpecWithNoExpectations: true, stopSpecOnExpectationFailure: true, stopOnSpecFailure: true, hideDisabled: true }; env.configure(initialConfig); env.configure({ random: undefined, seed: undefined, failSpecWithNoExpectations: undefined, stopSpecOnExpectationFailure: undefined, stopOnSpecFailure: undefined, hideDisabled: undefined }); expect(env.configuration()).toEqual( jasmine.objectContaining(initialConfig) ); }); it('defaults to multiple failures for specs', function() { spyOn(jasmineUnderTest, 'Spec').and.callThrough(); env.it('bar', function() {}); expect(jasmineUnderTest.Spec).toHaveBeenCalledWith( jasmine.objectContaining({ throwOnExpectationFailure: false }) ); }); it('defaults to multiple failures for suites', function() { spyOn(jasmineUnderTest, 'Suite'); env.describe('foo', function() {}); expect(jasmineUnderTest.Suite).toHaveBeenCalledWith( jasmine.objectContaining({ throwOnExpectationFailure: false }) ); }); function behavesLikeDescribe(methodName) { it('returns a suite metadata object', function() { let innerSuite; let spec; const suite = env[methodName]('outer suite', function() { innerSuite = env[methodName]('inner suite', function() { spec = env.it('a spec'); }); }); expect(suite.parentSuite).toEqual( jasmine.objectContaining({ description: 'Jasmine__TopLevel__Suite' }) ); expect(suite.parentSuite.pend).toBeUndefined(); expect(suite.pend).toBeUndefined(); expect(suite.description).toEqual('outer suite'); expect(suite.getFullName()).toEqual('outer suite'); expect(suite.id).toBeInstanceOf(String); expect(suite.id).not.toEqual(''); expect(suite.children.length).toEqual(1); expect(suite.children[0]).toBe(innerSuite); expect(innerSuite.children.length).toEqual(1); expect(innerSuite.children[0]).toBe(spec); expect(innerSuite.getFullName()).toEqual('outer suite inner suite'); expect(innerSuite.parentSuite).toBe(suite); expect(spec.getFullName()).toEqual('outer suite inner suite a spec'); }); } describe('#describe', function() { behavesLikeDescribe('describe'); 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'); }); it('throws an error when it receives a non-fn argument', function() { // Some versions of PhantomJS return [object DOMWindow] when // Object.prototype.toString.apply is called with `undefined` or `null`. // In a similar fashion, IE8 gives [object Object] for both `undefined` // and `null`. We mostly just want these tests to check that using // anything other than a function throws an error. expect(function() { env.describe('undefined arg', undefined); }).toThrowError( /describe expects a function argument; received \[object (Undefined|DOMWindow|Object)\]/ ); expect(function() { env.describe('null arg', null); }).toThrowError( /describe expects a function argument; received \[object (Null|DOMWindow|Object)\]/ ); expect(function() { env.describe('array arg', []); }).toThrowError( 'describe expects a function argument; received [object Array]' ); expect(function() { env.describe('object arg', {}); }).toThrowError( 'describe expects a function argument; received [object Object]' ); expect(function() { env.describe('fn arg', function() { env.it('has a spec', function() {}); }); }).not.toThrowError( 'describe expects a function argument; received [object Function]' ); }); it('throws an error when it has no children', function() { let ran = false; env.describe('parent suite', function() { expect(function() { env.describe('child suite', function() {}); }).toThrowError( 'describe with no children (describe() or it()): parent suite child suite' ); ran = true; }); expect(ran).toBeTrue(); }); }); describe('#fdescribe', function() { behavesLikeDescribe('fdescribe'); }); describe('xdescribe', function() { behavesLikeDescribe('xdescribe'); }); function behavesLikeIt(methodName) { it('returns a spec metadata object', function() { let spec; env.describe('a suite', function() { spec = env[methodName]('a spec', function() {}); }); expect(spec.description) .withContext('description') .toEqual('a spec'); expect(spec.getFullName()) .withContext('getFullName') .toEqual('a suite a spec'); expect(spec.id) .withContext('id') .toBeInstanceOf(String); expect(spec.id) .withContext('id') .not.toEqual(''); expect(spec.pend).toBeFalsy(); }); } describe('#it', function() { behavesLikeIt('it'); it('throws an error when it receives a non-fn argument', function() { expect(function() { env.it('undefined arg', null); }).toThrowError( /it expects a function argument; received \[object (Null|DOMWindow|Object)\]/ ); }); it('does not throw when it is not given a fn argument', function() { expect(function() { env.it('pending spec'); }).not.toThrow(); }); it('accepts an async function', function() { expect(function() { env.it('async', async function() {}); }).not.toThrow(); }); it('throws an error when the timeout value is too large for setTimeout', function() { expect(function() { env.it('huge timeout', function() {}, 2147483648); }).toThrowError('Timeout value cannot be greater than 2147483647'); }); }); describe('#xit', function() { behavesLikeIt('xit'); it('calls spec.exclude with "Temporarily disabled with xit"', function() { const excludeSpy = jasmine.createSpy(); spyOn(jasmineUnderTest.SuiteBuilder.prototype, 'it_').and.returnValue({ exclude: excludeSpy }); env.xit('foo', function() {}); expect(excludeSpy).toHaveBeenCalledWith('Temporarily disabled with xit'); }); it('calls spec.pend with "Temporarily disabled with xit"', function() { const pendSpy = jasmine.createSpy(); const realExclude = jasmineUnderTest.Spec.prototype.exclude; spyOn(jasmineUnderTest.SuiteBuilder.prototype, 'it_').and.returnValue({ exclude: realExclude, pend: pendSpy }); env.xit('foo', function() {}); expect(pendSpy).toHaveBeenCalledWith('Temporarily disabled with xit'); }); it('throws an error when it receives a non-fn argument', function() { expect(function() { env.xit('undefined arg', null); }).toThrowError( /xit expects a function argument; received \[object (Null|DOMWindow|Object)\]/ ); }); it('does not throw when it is not given a fn argument', function() { expect(function() { env.xit('pending spec'); }).not.toThrow(); }); it('accepts an async function', function() { expect(function() { env.xit('async', async function() {}); }).not.toThrow(); }); }); describe('#fit', function() { behavesLikeIt('fit'); it('throws an error when it receives a non-fn argument', function() { expect(function() { env.fit('undefined arg', undefined); }).toThrowError( /fit expects a function argument; received \[object (Undefined|DOMWindow|Object)\]/ ); }); it('throws an error when the timeout value is too large for setTimeout', function() { expect(function() { env.fit('huge timeout', function() {}, 2147483648); }).toThrowError('Timeout value cannot be greater than 2147483647'); }); }); describe('#beforeEach', function() { it('throws an error when it receives a non-fn argument', function() { expect(function() { env.beforeEach(undefined); }).toThrowError( /beforeEach expects a function argument; received \[object (Undefined|DOMWindow|Object)\]/ ); }); it('accepts an async function', function() { expect(function() { env.beforeEach(async function() {}); }).not.toThrow(); }); it('throws an error when the timeout value is too large for setTimeout', function() { expect(function() { env.beforeEach(function() {}, 2147483648); }).toThrowError('Timeout value cannot be greater than 2147483647'); }); }); describe('#beforeAll', function() { it('throws an error when it receives a non-fn argument', function() { expect(function() { env.beforeAll(undefined); }).toThrowError( /beforeAll expects a function argument; received \[object (Undefined|DOMWindow|Object)\]/ ); }); it('accepts an async function', function() { expect(function() { env.beforeAll(async function() {}); }).not.toThrow(); }); it('throws an error when the timeout value is too large for setTimeout', function() { expect(function() { env.beforeAll(function() {}, 2147483648); }).toThrowError('Timeout value cannot be greater than 2147483647'); }); }); describe('#afterEach', function() { it('throws an error when it receives a non-fn argument', function() { expect(function() { env.afterEach(undefined); }).toThrowError( /afterEach expects a function argument; received \[object (Undefined|DOMWindow|Object)\]/ ); }); it('accepts an async function', function() { expect(function() { env.afterEach(async function() {}); }).not.toThrow(); }); it('throws an error when the timeout value is too large for setTimeout', function() { expect(function() { env.afterEach(function() {}, 2147483648); }).toThrowError('Timeout value cannot be greater than 2147483647'); }); }); describe('#afterAll', function() { it('throws an error when it receives a non-fn argument', function() { expect(function() { env.afterAll(undefined); }).toThrowError( /afterAll expects a function argument; received \[object (Undefined|DOMWindow|Object)\]/ ); }); it('accepts an async function', function() { expect(function() { env.afterAll(async function() {}); }).not.toThrow(); }); it('throws an error when the timeout value is too large for setTimeout', function() { expect(function() { env.afterAll(function() {}, 2147483648); }).toThrowError('Timeout value cannot be greater than 2147483647'); }); }); describe('when not constructed with suppressLoadErrors: true', function() { it('installs a global error handler on construction', function() { const globalErrors = jasmine.createSpyObj('globalErrors', [ 'install', 'uninstall', 'pushListener', 'popListener', 'removeOverrideListener' ]); spyOn(jasmineUnderTest, 'GlobalErrors').and.returnValue(globalErrors); env.cleanup_(); env = new jasmineUnderTest.Env(); expect(globalErrors.install).toHaveBeenCalled(); }); }); describe('when constructed with suppressLoadErrors: true', function() { it('does not install a global error handler until execute is called', function() { const globalErrors = jasmine.createSpyObj('globalErrors', [ 'install', 'uninstall', 'pushListener', 'popListener', 'removeOverrideListener' ]); spyOn(jasmineUnderTest, 'GlobalErrors').and.returnValue(globalErrors); env.cleanup_(); env = new jasmineUnderTest.Env({ suppressLoadErrors: true }); expect(globalErrors.install).not.toHaveBeenCalled(); env.execute(); expect(globalErrors.install).toHaveBeenCalled(); }); }); it('creates an expectationFactory that uses the current custom equality testers and object formatters', async function() { function customEqualityTester() {} function customObjectFormatter() {} function prettyPrinter() {} const RealSpec = jasmineUnderTest.Spec; let specInstance; let expectationFactory; spyOn(jasmineUnderTest, 'MatchersUtil'); spyOn(jasmineUnderTest, 'makePrettyPrinter').and.returnValue(prettyPrinter); spyOn(jasmineUnderTest, 'Spec').and.callFake(function(options) { expectationFactory = options.expectationFactory; specInstance = new RealSpec(options); return specInstance; }); env.it('spec', function() { env.addCustomEqualityTester(customEqualityTester); env.addCustomObjectFormatter(customObjectFormatter); expectationFactory('actual', specInstance); }); await env.execute(); expect(jasmineUnderTest.makePrettyPrinter).toHaveBeenCalledWith([ customObjectFormatter ]); expect(jasmineUnderTest.MatchersUtil).toHaveBeenCalledWith({ customTesters: [customEqualityTester], pp: prettyPrinter }); }); it('creates an asyncExpectationFactory that uses the current custom equality testers and object formatters', async function() { function customEqualityTester() {} function customObjectFormatter() {} function prettyPrinter() {} const RealSpec = jasmineUnderTest.Spec; let specInstance; let asyncExpectationFactory; spyOn(jasmineUnderTest, 'MatchersUtil'); spyOn(jasmineUnderTest, 'makePrettyPrinter').and.returnValue(prettyPrinter); spyOn(jasmineUnderTest, 'Spec').and.callFake(function(options) { asyncExpectationFactory = options.asyncExpectationFactory; specInstance = new RealSpec(options); return specInstance; }); env.it('spec', function() { env.addCustomEqualityTester(customEqualityTester); env.addCustomObjectFormatter(customObjectFormatter); asyncExpectationFactory('actual', specInstance); }); await env.execute(); expect(jasmineUnderTest.makePrettyPrinter).toHaveBeenCalledWith([ customObjectFormatter ]); expect(jasmineUnderTest.MatchersUtil).toHaveBeenCalledWith({ customTesters: [customEqualityTester], pp: prettyPrinter }); }); it("does not expose the suite as 'this'", function() { let suiteThis; spyOn(env, 'deprecated'); env.describe('a suite', function() { suiteThis = this; env.it('has a spec'); }); expect(suiteThis).not.toBeInstanceOf(jasmineUnderTest.Suite); }); describe('#execute', function() { it('returns a promise', function() { expect(env.execute()).toBeInstanceOf(Promise); }); it('should reset the topSuite when run twice', function() { spyOn(jasmineUnderTest.Suite.prototype, 'reset'); return env .execute() // 1 .then(function() { return env.execute(); // 2 }) .then(function() { expect( jasmineUnderTest.Suite.prototype.reset ).toHaveBeenCalledOnceWith(); const id = jasmineUnderTest.Suite.prototype.reset.calls.thisFor(0).id; expect(id).toBeTruthy(); expect(id).toEqual(env.topSuite().id); }); }); }); describe('#spyOnGlobalErrorsAsync', function() { it('throws if the callback does not return a promise', async function() { const msg = 'The callback to spyOnGlobalErrorsAsync must be an async or ' + 'promise-returning function'; await expectAsync( env.spyOnGlobalErrorsAsync(() => undefined) ).toBeRejectedWithError(msg); await expectAsync( env.spyOnGlobalErrorsAsync(() => 'not a promise') ).toBeRejectedWithError(msg); }); }); }); jasmine-4.5.0/spec/core/ExceptionFormatterSpec.js000066400000000000000000000274711432731766000220470ustar00rootroot00000000000000describe('ExceptionFormatter', function() { describe('#message', function() { it('formats Firefox exception messages', function() { const sampleFirefoxException = { fileName: 'foo.js', lineNumber: '1978', message: 'you got your foo in my bar', name: 'A Classic Mistake' }, exceptionFormatter = new jasmineUnderTest.ExceptionFormatter(), message = exceptionFormatter.message(sampleFirefoxException); expect(message).toEqual( 'A Classic Mistake: you got your foo in my bar in foo.js (line 1978)' ); }); it('formats Webkit exception messages', function() { const sampleWebkitException = { sourceURL: 'foo.js', line: '1978', message: 'you got your foo in my bar', name: 'A Classic Mistake' }, exceptionFormatter = new jasmineUnderTest.ExceptionFormatter(), message = exceptionFormatter.message(sampleWebkitException); expect(message).toEqual( 'A Classic Mistake: you got your foo in my bar in foo.js (line 1978)' ); }); it('formats V8 exception messages', function() { const sampleV8 = { message: 'you got your foo in my bar', name: 'A Classic Mistake' }, exceptionFormatter = new jasmineUnderTest.ExceptionFormatter(), message = exceptionFormatter.message(sampleV8); expect(message).toEqual('A Classic Mistake: you got your foo in my bar'); }); it('formats unnamed exceptions with message', function() { const unnamedError = { message: 'This is an unnamed error message.' }; const exceptionFormatter = new jasmineUnderTest.ExceptionFormatter(), message = exceptionFormatter.message(unnamedError); expect(message).toEqual('This is an unnamed error message.'); }); it('formats empty exceptions with toString format', function() { const EmptyError = function() {}; EmptyError.prototype.toString = function() { return '[EmptyError]'; }; const emptyError = new EmptyError(); const exceptionFormatter = new jasmineUnderTest.ExceptionFormatter(), message = exceptionFormatter.message(emptyError); expect(message).toEqual('[EmptyError] thrown'); }); it("formats thrown exceptions that aren't errors", function() { const thrown = 'crazy error', exceptionFormatter = new jasmineUnderTest.ExceptionFormatter(), message = exceptionFormatter.message(thrown); expect(message).toEqual('crazy error thrown'); }); }); describe('#stack', function() { it('formats stack traces', function() { const error = new Error('an error'); expect(new jasmineUnderTest.ExceptionFormatter().stack(error)).toMatch( /ExceptionFormatterSpec\.js.*\d+/ ); }); it('filters Jasmine stack frames from V8-style traces but leaves unmatched lines intact', function() { const error = { message: 'nope', stack: 'C:\\__spec__\\core\\UtilSpec.ts:120\n' + " new Error('nope');\n" + ' ^\n' + '\n' + 'Error: nope\n' + ' at fn1 (C:\\__spec__\\core\\UtilSpec.js:115:19)\n' + ' -> C:\\__spec__\\core\\UtilSpec.ts:120:15\n' + ' at fn2 (C:\\__jasmine__\\lib\\jasmine-core\\jasmine.js:7533:40)\n' + ' at fn3 (C:\\__jasmine__\\lib\\jasmine-core\\jasmine.js:7575:25)\n' + ' at fn4 (node:internal/timers:462:21)\n' }; const subject = new jasmineUnderTest.ExceptionFormatter({ jasmineFile: 'C:\\__jasmine__\\lib\\jasmine-core\\jasmine.js' }); const result = subject.stack(error); expect(result).toEqual( 'C:\\__spec__\\core\\UtilSpec.ts:120\n' + " new Error('nope');\n" + ' ^\n' + 'Error: nope\n' + ' at fn1 (C:\\__spec__\\core\\UtilSpec.js:115:19)\n' + ' -> C:\\__spec__\\core\\UtilSpec.ts:120:15\n' + ' at \n' + ' at fn4 (node:internal/timers:462:21)' ); }); it('filters Jasmine stack frames from V8 style traces', function() { const error = { message: 'nope', stack: 'Error: nope\n' + ' at fn1 (http://localhost:8888/__spec__/core/UtilSpec.js:115:19)\n' + ' at fn2 (http://localhost:8888/__jasmine__/jasmine.js:4320:20)\n' + ' at fn3 (http://localhost:8888/__jasmine__/jasmine.js:4320:20)\n' + ' at fn4 (http://localhost:8888/__spec__/core/UtilSpec.js:110:19)\n' }; const subject = new jasmineUnderTest.ExceptionFormatter({ jasmineFile: 'http://localhost:8888/__jasmine__/jasmine.js' }); const result = subject.stack(error); expect(result).toEqual( 'Error: nope\n' + ' at fn1 (http://localhost:8888/__spec__/core/UtilSpec.js:115:19)\n' + ' at \n' + ' at fn4 (http://localhost:8888/__spec__/core/UtilSpec.js:110:19)' ); }); it('filters Jasmine stack frames from Webkit style traces', function() { const error = { stack: 'http://localhost:8888/__spec__/core/UtilSpec.js:115:28\n' + 'fn1@http://localhost:8888/__jasmine__/jasmine.js:4320:27\n' + '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' + '\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); const lines = result.split('\n'); if (lines[0].match(/an error/)) { lines.shift(); } expect(lines[0]).toMatch(/ExceptionFormatterSpec.js/); expect(lines[1]).toMatch(//); // Node has some number of additional frames below Jasmine. for (let i = 2; i < lines.length; i++) { expect(lines[i]).not.toMatch(/jasmine.js/); } }); it('handles multiline error messages in this environment', function() { const msg = 'an error\nwith two lines'; const error = new Error(msg); if (error.stack.indexOf(msg) === -1) { pending("Stack traces don't have messages in this environment"); } const subject = new jasmineUnderTest.ExceptionFormatter({ jasmineFile: jasmine.util.jasmineFile() }); const result = subject.stack(error); const lines = result.split('\n'); expect(lines[0]).toMatch(/an error/); expect(lines[1]).toMatch(/with two lines/); expect(lines[2]).toMatch(/ExceptionFormatterSpec.js/); expect(lines[3]).toMatch(//); }); it('returns null if no Error provided', function() { expect(new jasmineUnderTest.ExceptionFormatter().stack()).toBeNull(); }); it('includes error properties in stack', function() { const error = new Error('an error'); error.someProperty = 'hello there'; const result = new jasmineUnderTest.ExceptionFormatter().stack(error); expect(result).toMatch(/error properties:.*someProperty.*hello there/); }); describe('When omitMessage is true', function() { it('filters the message from V8-style stack traces', function() { const error = { message: 'nope', stack: 'Error: nope\n' + ' at fn1 (http://localhost:8888/__spec__/core/UtilSpec.js:115:19)\n' + ' at fn2 (http://localhost:8888/__jasmine__/jasmine.js:4320:20)\n' + ' at fn3 (http://localhost:8888/__jasmine__/jasmine.js:4320:20)\n' + ' at fn4 (http://localhost:8888/__spec__/core/UtilSpec.js:110:19)\n' }; const subject = new jasmineUnderTest.ExceptionFormatter({ jasmineFile: 'http://localhost:8888/__jasmine__/jasmine.js' }); const result = subject.stack(error, { omitMessage: true }); expect(result).toEqual( ' at fn1 (http://localhost:8888/__spec__/core/UtilSpec.js:115:19)\n' + ' at \n' + ' at fn4 (http://localhost:8888/__spec__/core/UtilSpec.js:110:19)' ); }); it('handles Webkit style traces that do not include a message', function() { const error = { stack: 'http://localhost:8888/__spec__/core/UtilSpec.js:115:28\n' + 'fn1@http://localhost:8888/__jasmine__/jasmine.js:4320:27\n' + '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, { omitMessage: true }); expect(result).toEqual( 'http://localhost:8888/__spec__/core/UtilSpec.js:115:28\n' + '\n' + 'http://localhost:8888/__spec__/core/UtilSpec.js:115:28' ); }); it('ensures that stack traces do not include the message in this environment', function() { const error = new Error('an error'); const subject = new jasmineUnderTest.ExceptionFormatter({ jasmineFile: jasmine.util.jasmineFile() }); const result = subject.stack(error, { omitMessage: true }); expect(result).not.toContain('an error'); }); }); 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'); const proximateCause = new Error('proximate cause', { cause: rootCause }); const symptom = new Error('symptom', { cause: proximateCause }); const lines = subject.stack(symptom).split('\n'); // Not all environments include the message in the stack trace. const hasRootMessage = lines[0].indexOf('symptom') !== -1; const firstSymptomStackIx = hasRootMessage ? 1 : 0; expect(lines[firstSymptomStackIx]) .withContext('first symptom stack frame') .toContain('ExceptionFormatterSpec.js'); const proximateCauseMsgIx = lines.indexOf( 'Caused by: Error: proximate cause' ); expect(proximateCauseMsgIx) .withContext('index of proximate cause message') .toBeGreaterThan(firstSymptomStackIx); expect(lines[proximateCauseMsgIx + 1]) .withContext('first proximate cause stack frame') .toContain('ExceptionFormatterSpec.js'); const rootCauseMsgIx = lines.indexOf('Caused by: Error: root cause'); expect(rootCauseMsgIx) .withContext('index of root cause message') .toBeGreaterThan(proximateCauseMsgIx + 1); expect(lines[rootCauseMsgIx + 1]) .withContext('first root cause stack frame') .toContain('ExceptionFormatterSpec.js'); }); }); }); }); jasmine-4.5.0/spec/core/ExceptionsSpec.js000066400000000000000000000025561432731766000203430ustar00rootroot00000000000000describe('Exceptions:', function() { let env; beforeEach(function() { env = new jasmineUnderTest.Env(); }); afterEach(function() { env.cleanup_(); }); it('should handle exceptions thrown, but continue', async function() { const secondTest = jasmine.createSpy('second test'); env.describe('Suite for handles exceptions', function() { env.it( 'should be a test that fails because it throws an exception', function() { throw new Error(); } ); env.it( 'should be a passing test that runs after exceptions are thrown from a async test', secondTest ); }); await env.execute(); expect(secondTest).toHaveBeenCalled(); }); it('should handle exceptions thrown directly in top-level describe blocks and continue', async function() { const secondDescribe = jasmine .createSpy('second describe') .and.callFake(function() { env.it('has a test', function() {}); }); env.describe('a suite that throws an exception', function() { env.it('is a test that should pass', function() { this.expect(true).toEqual(true); }); throw new Error('top level error'); }); env.describe("a suite that doesn't throw an exception", secondDescribe); await env.execute(); expect(secondDescribe).toHaveBeenCalled(); }); }); jasmine-4.5.0/spec/core/ExpectationFilterChainSpec.js000066400000000000000000000104451432731766000226120ustar00rootroot00000000000000describe('ExpectationFilterChain', function() { describe('#addFilter', function() { it('returns a new filter chain with the added filter', function() { const first = jasmine.createSpy('first'), second = jasmine.createSpy('second'), orig = new jasmineUnderTest.ExpectationFilterChain({ modifyFailureMessage: first }), added = orig.addFilter({ selectComparisonFunc: second }); added.modifyFailureMessage(); expect(first).toHaveBeenCalled(); added.selectComparisonFunc(); expect(second).toHaveBeenCalled(); }); it('does not modify the original filter chain', function() { const orig = new jasmineUnderTest.ExpectationFilterChain({}), f = jasmine.createSpy('f'); orig.addFilter({ selectComparisonFunc: f }); orig.selectComparisonFunc(); expect(f).not.toHaveBeenCalled(); }); }); describe('#selectComparisonFunc', function() { describe('When no filters have #selectComparisonFunc', function() { it('returns undefined', function() { const chain = new jasmineUnderTest.ExpectationFilterChain(); chain.addFilter({}); expect(chain.selectComparisonFunc()).toBeUndefined(); }); }); describe('When some filters have #selectComparisonFunc', function() { it('calls the first filter that has #selectComparisonFunc', function() { const first = jasmine.createSpy('first').and.returnValue('first'), second = jasmine.createSpy('second').and.returnValue('second'), chain = new jasmineUnderTest.ExpectationFilterChain() .addFilter({ selectComparisonFunc: first }) .addFilter({ selectComparisonFunc: second }), matcher = {}, result = chain.selectComparisonFunc(matcher); expect(first).toHaveBeenCalledWith(matcher); expect(second).not.toHaveBeenCalled(); expect(result).toEqual('first'); }); }); }); describe('#buildFailureMessage', function() { describe('When no filters have #buildFailureMessage', function() { it('returns undefined', function() { const chain = new jasmineUnderTest.ExpectationFilterChain(); chain.addFilter({}); expect(chain.buildFailureMessage()).toBeUndefined(); }); }); describe('When some filters have #buildFailureMessage', function() { it('calls the first filter that has #buildFailureMessage', function() { const first = jasmine.createSpy('first').and.returnValue('first'), second = jasmine.createSpy('second').and.returnValue('second'), chain = new jasmineUnderTest.ExpectationFilterChain() .addFilter({ buildFailureMessage: first }) .addFilter({ buildFailureMessage: second }), matcherResult = { pass: false }, matcherName = 'foo', args = [], matchersUtil = {}; const result = chain.buildFailureMessage( matcherResult, matcherName, args, matchersUtil ); expect(first).toHaveBeenCalledWith( matcherResult, matcherName, args, matchersUtil ); expect(second).not.toHaveBeenCalled(); expect(result).toEqual('first'); }); }); }); describe('#modifyFailureMessage', function() { describe('When no filters have #modifyFailureMessage', function() { it('returns the original message', function() { const chain = new jasmineUnderTest.ExpectationFilterChain(); chain.addFilter({}); expect(chain.modifyFailureMessage('msg')).toEqual('msg'); }); }); describe('When some filters have #modifyFailureMessage', function() { it('calls the first filter that has #modifyFailureMessage', function() { const first = jasmine.createSpy('first').and.returnValue('first'), second = jasmine.createSpy('second').and.returnValue('second'), chain = new jasmineUnderTest.ExpectationFilterChain() .addFilter({ modifyFailureMessage: first }) .addFilter({ modifyFailureMessage: second }), result = chain.modifyFailureMessage('original'); expect(first).toHaveBeenCalledWith('original'); expect(second).not.toHaveBeenCalled(); expect(result).toEqual('first'); }); }); }); }); jasmine-4.5.0/spec/core/ExpectationSpec.js000066400000000000000000000464431432731766000205100ustar00rootroot00000000000000describe('Expectation', function() { it('makes custom matchers available to this expectation', function() { const matchers = { toFoo: function() {}, toBar: function() {} }, expectation = jasmineUnderTest.Expectation.factory({ customMatchers: matchers }); expect(expectation.toFoo).toBeDefined(); expect(expectation.toBar).toBeDefined(); }); it('.addCoreMatchers makes matchers available to any expectation', function() { const coreMatchers = { toQuux: function() {} }; jasmineUnderTest.Expectation.addCoreMatchers(coreMatchers); const expectation = jasmineUnderTest.Expectation.factory({}); expect(expectation.toQuux).toBeDefined(); }); it("wraps matchers's compare functions, passing in matcher dependencies", function() { const fakeCompare = function() { return { pass: true }; }, matcherFactory = jasmine .createSpy('matcher') .and.returnValue({ compare: fakeCompare }), matchers = { toFoo: matcherFactory }, matchersUtil = { buildFailureMessage: jasmine.createSpy('buildFailureMessage') }, addExpectationResult = jasmine.createSpy('addExpectationResult'); const expectation = jasmineUnderTest.Expectation.factory({ matchersUtil: matchersUtil, customMatchers: matchers, actual: 'an actual', addExpectationResult: addExpectationResult }); expectation.toFoo('hello'); expect(matcherFactory).toHaveBeenCalledWith(matchersUtil); }); it("wraps matchers's compare functions, passing the actual and expected", function() { const fakeCompare = jasmine .createSpy('fake-compare') .and.returnValue({ pass: true }), matchers = { toFoo: function() { return { compare: fakeCompare }; } }, matchersUtil = { buildFailureMessage: jasmine.createSpy('buildFailureMessage') }, addExpectationResult = jasmine.createSpy('addExpectationResult'); const expectation = jasmineUnderTest.Expectation.factory({ matchersUtil: matchersUtil, customMatchers: matchers, actual: 'an actual', addExpectationResult: addExpectationResult }); expectation.toFoo('hello'); expect(fakeCompare).toHaveBeenCalledWith('an actual', 'hello'); }); it('reports a passing result to the spec when the comparison passes', function() { const matchers = { toFoo: function() { return { compare: function() { return { pass: true }; } }; } }, matchersUtil = { buildFailureMessage: jasmine.createSpy('buildFailureMessage') }, addExpectationResult = jasmine.createSpy('addExpectationResult'); const expectation = jasmineUnderTest.Expectation.factory({ customMatchers: matchers, matchersUtil: matchersUtil, actual: 'an actual', addExpectationResult: addExpectationResult }); expectation.toFoo('hello'); expect(addExpectationResult).toHaveBeenCalledWith(true, { matcherName: 'toFoo', passed: true, message: '', error: undefined, expected: 'hello', actual: 'an actual', errorForStack: undefined }); }); it('reports a failing result to the spec when the comparison fails', function() { const matchers = { toFoo: function() { return { compare: function() { return { pass: false }; } }; } }, matchersUtil = { buildFailureMessage: function() { return ''; } }, addExpectationResult = jasmine.createSpy('addExpectationResult'); const expectation = jasmineUnderTest.Expectation.factory({ customMatchers: matchers, matchersUtil: matchersUtil, actual: 'an actual', addExpectationResult: addExpectationResult }); expectation.toFoo('hello'); expect(addExpectationResult).toHaveBeenCalledWith(false, { matcherName: 'toFoo', passed: false, expected: 'hello', actual: 'an actual', message: '', error: undefined, errorForStack: undefined }); }); 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 { pass: false, message: 'I am a custom message' }; } }; } }, addExpectationResult = jasmine.createSpy('addExpectationResult'); const expectation = jasmineUnderTest.Expectation.factory({ actual: 'an actual', customMatchers: matchers, addExpectationResult: addExpectationResult }); expectation.toFoo('hello'); expect(addExpectationResult).toHaveBeenCalledWith(false, { matcherName: 'toFoo', passed: false, expected: 'hello', actual: 'an actual', message: 'I am a custom message', error: undefined, errorForStack: undefined }); }); 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 { pass: false, message: function() { return 'I am a custom message'; } }; } }; } }, addExpectationResult = jasmine.createSpy('addExpectationResult'); const expectation = jasmineUnderTest.Expectation.factory({ customMatchers: matchers, actual: 'an actual', addExpectationResult: addExpectationResult }); expectation.toFoo('hello'); expect(addExpectationResult).toHaveBeenCalledWith(false, { matcherName: 'toFoo', passed: false, expected: 'hello', actual: 'an actual', message: 'I am a custom message', error: undefined, errorForStack: undefined }); }); 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 { pass: false }; } }; } }, addExpectationResult = jasmine.createSpy('addExpectationResult'), actual = 'an actual'; const expectation = jasmineUnderTest.Expectation.factory({ customMatchers: matchers, actual: 'an actual', addExpectationResult: addExpectationResult }).not; expectation.toFoo('hello'); expect(addExpectationResult).toHaveBeenCalledWith(true, { matcherName: 'toFoo', passed: true, message: '', error: undefined, expected: 'hello', actual: actual, errorForStack: undefined }); }); it('reports a failing result to the spec when the comparison passes for a negative expectation', function() { const matchers = { toFoo: function() { return { compare: function() { return { pass: true }; } }; } }, matchersUtil = { buildFailureMessage: function() { return 'default message'; } }, addExpectationResult = jasmine.createSpy('addExpectationResult'), actual = 'an actual'; const expectation = jasmineUnderTest.Expectation.factory({ customMatchers: matchers, actual: 'an actual', matchersUtil: matchersUtil, addExpectationResult: addExpectationResult }).not; expectation.toFoo('hello'); expect(addExpectationResult).toHaveBeenCalledWith(false, { matcherName: 'toFoo', passed: false, expected: 'hello', actual: actual, message: 'default message', error: undefined, errorForStack: undefined }); }); 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 { pass: true, message: 'I am a custom message' }; } }; } }, addExpectationResult = jasmine.createSpy('addExpectationResult'), actual = 'an actual'; const expectation = jasmineUnderTest.Expectation.factory({ customMatchers: matchers, actual: 'an actual', addExpectationResult: addExpectationResult }).not; expectation.toFoo('hello'); expect(addExpectationResult).toHaveBeenCalledWith(false, { matcherName: 'toFoo', passed: false, expected: 'hello', actual: actual, message: 'I am a custom message', error: undefined, errorForStack: undefined }); }); 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 { pass: true }; }, negativeCompare: function() { return { pass: true }; } }; } }, addExpectationResult = jasmine.createSpy('addExpectationResult'), actual = 'an actual'; const expectation = jasmineUnderTest.Expectation.factory({ customMatchers: matchers, actual: 'an actual', addExpectationResult: addExpectationResult }).not; expectation.toFoo('hello'); expect(addExpectationResult).toHaveBeenCalledWith(true, { matcherName: 'toFoo', passed: true, expected: 'hello', actual: actual, message: '', error: undefined, errorForStack: undefined }); }); 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 { pass: true }; }, negativeCompare: function() { return { pass: false, message: "I'm a custom message" }; } }; } }, addExpectationResult = jasmine.createSpy('addExpectationResult'), actual = 'an actual'; const expectation = jasmineUnderTest.Expectation.factory({ customMatchers: matchers, actual: 'an actual', addExpectationResult: addExpectationResult }).not; expectation.toFoo('hello'); expect(addExpectationResult).toHaveBeenCalledWith(false, { matcherName: 'toFoo', passed: false, expected: 'hello', actual: actual, message: "I'm a custom message", error: undefined, errorForStack: undefined }); }); it('reports a custom error message to the spec', function() { const customError = new Error('I am a custom error'); const matchers = { toFoo: function() { return { compare: function() { return { pass: false, message: 'I am a custom message', error: customError }; } }; } }, addExpectationResult = jasmine.createSpy('addExpectationResult'); const expectation = jasmineUnderTest.Expectation.factory({ actual: 'an actual', customMatchers: matchers, addExpectationResult: addExpectationResult }); expectation.toFoo('hello'); expect(addExpectationResult).toHaveBeenCalledWith(false, { matcherName: 'toFoo', passed: false, expected: 'hello', actual: 'an actual', message: 'I am a custom message', error: customError, errorForStack: undefined }); }); it("reports a custom message to the spec when a 'not' comparison fails", function() { const customError = new Error('I am a custom error'); const matchers = { toFoo: function() { return { compare: function() { return { pass: true, message: 'I am a custom message', error: customError }; } }; } }, addExpectationResult = jasmine.createSpy('addExpectationResult'); const expectation = jasmineUnderTest.Expectation.factory({ actual: 'an actual', customMatchers: matchers, addExpectationResult: addExpectationResult }).not; expectation.toFoo('hello'); expect(addExpectationResult).toHaveBeenCalledWith(false, { matcherName: 'toFoo', passed: false, expected: 'hello', actual: 'an actual', message: 'I am a custom message', error: customError, errorForStack: undefined }); }); it("reports a custom message func to the spec when a 'not' comparison fails", function() { const customError = new Error('I am a custom error'); const matchers = { toFoo: function() { return { compare: function() { return { pass: true, message: function() { return 'I am a custom message'; }, error: customError }; } }; } }, addExpectationResult = jasmine.createSpy('addExpectationResult'); const expectation = jasmineUnderTest.Expectation.factory({ actual: 'an actual', customMatchers: matchers, addExpectationResult: addExpectationResult }).not; expectation.toFoo('hello'); expect(addExpectationResult).toHaveBeenCalledWith(false, { matcherName: 'toFoo', passed: false, expected: 'hello', actual: 'an actual', message: 'I am a custom message', error: customError, errorForStack: undefined }); }); describe('#withContext', function() { it('prepends the context to the generated failure message', function() { const matchers = { toFoo: function() { return { compare: function() { return { pass: false }; } }; } }, matchersUtil = { buildFailureMessage: function() { return 'failure message'; } }, addExpectationResult = jasmine.createSpy('addExpectationResult'), expectation = jasmineUnderTest.Expectation.factory({ customMatchers: matchers, matchersUtil: matchersUtil, actual: 'an actual', addExpectationResult: addExpectationResult }); expectation.withContext('Some context').toFoo('hello'); expect(addExpectationResult).toHaveBeenCalledWith( false, jasmine.objectContaining({ message: 'Some context: failure message' }) ); }); it('prepends the context to a custom failure message', function() { const matchers = { toFoo: function() { return { compare: function() { return { pass: false, message: 'msg' }; } }; } }, addExpectationResult = jasmine.createSpy('addExpectationResult'), expectation = jasmineUnderTest.Expectation.factory({ customMatchers: matchers, actual: 'an actual', addExpectationResult: addExpectationResult }); expectation.withContext('Some context').toFoo('hello'); expect(addExpectationResult).toHaveBeenCalledWith( false, jasmine.objectContaining({ message: 'Some context: msg' }) ); }); it('indents a multiline failure message', function() { const matchers = { toFoo: function() { return { compare: function() { return { pass: false, message: 'a\nmultiline\nmessage' }; } }; } }, addExpectationResult = jasmine.createSpy('addExpectationResult'), expectation = jasmineUnderTest.Expectation.factory({ customMatchers: matchers, actual: 'an actual', addExpectationResult: addExpectationResult }); expectation.withContext('Some context').toFoo('hello'); const actualMessage = addExpectationResult.calls.argsFor(0)[1].message; expect(actualMessage).toEqual( 'Some context:\n a\n multiline\n message' ); }); it('prepends the context to a custom failure message from a function', function() { const matchers = { toFoo: function() { return { compare: function() { return { pass: false, message: function() { return 'msg'; } }; } }; } }, addExpectationResult = jasmine.createSpy('addExpectationResult'), expectation = jasmineUnderTest.Expectation.factory({ customMatchers: matchers, actual: 'an actual', addExpectationResult: addExpectationResult }); expectation.withContext('Some context').toFoo('hello'); expect(addExpectationResult).toHaveBeenCalledWith( false, jasmine.objectContaining({ message: 'Some context: msg' }) ); }); it('works with #not', function() { const matchers = { toFoo: function() { return { compare: function() { return { pass: true }; } }; } }, addExpectationResult = jasmine.createSpy('addExpectationResult'), pp = jasmineUnderTest.makePrettyPrinter(), expectation = jasmineUnderTest.Expectation.factory({ customMatchers: matchers, matchersUtil: new jasmineUnderTest.MatchersUtil({ pp: pp }), actual: 'an actual', addExpectationResult: addExpectationResult }); expectation.withContext('Some context').not.toFoo(); expect(addExpectationResult).toHaveBeenCalledWith( false, jasmine.objectContaining({ message: "Some context: Expected 'an actual' not to foo." }) ); }); it('works with #not and a custom message', function() { const customError = new Error('I am a custom error'); const matchers = { toFoo: function() { return { compare: function() { return { pass: true, message: function() { return 'I am a custom message'; }, error: customError }; } }; } }, addExpectationResult = jasmine.createSpy('addExpectationResult'), expectation = jasmineUnderTest.Expectation.factory({ actual: 'an actual', customMatchers: matchers, addExpectationResult: addExpectationResult }); expectation.withContext('Some context').not.toFoo('hello'); expect(addExpectationResult).toHaveBeenCalledWith( false, jasmine.objectContaining({ message: 'Some context: I am a custom message' }) ); }); }); }); jasmine-4.5.0/spec/core/GlobalErrorsSpec.js000066400000000000000000000443351432731766000206200ustar00rootroot00000000000000describe('GlobalErrors', function() { it('calls the added handler on error', function() { const fakeGlobal = minimalBrowserGlobal(); const handler = jasmine.createSpy('errorHandler'); const errors = new jasmineUnderTest.GlobalErrors(fakeGlobal); errors.install(); errors.pushListener(handler); fakeGlobal.onerror('foo'); expect(handler).toHaveBeenCalledWith('foo'); }); it('enables external interception of error by overriding global.onerror', function() { const fakeGlobal = minimalBrowserGlobal(); const handler = jasmine.createSpy('errorHandler'); const hijackHandler = jasmine.createSpy('hijackErrorHandler'); const errors = new jasmineUnderTest.GlobalErrors(fakeGlobal); errors.install(); errors.pushListener(handler); fakeGlobal.onerror = hijackHandler; fakeGlobal.onerror('foo'); expect(hijackHandler).toHaveBeenCalledWith('foo'); expect(handler).not.toHaveBeenCalled(); }); it('calls the global error handler with all parameters', function() { const fakeGlobal = 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( fooError.message, 'foo.js', 1, 1, fooError ); }); it('only calls the most recent handler', function() { const fakeGlobal = minimalBrowserGlobal(); const handler1 = jasmine.createSpy('errorHandler1'); const handler2 = jasmine.createSpy('errorHandler2'); const errors = new jasmineUnderTest.GlobalErrors(fakeGlobal); errors.install(); errors.pushListener(handler1); errors.pushListener(handler2); fakeGlobal.onerror('foo'); expect(handler1).not.toHaveBeenCalled(); expect(handler2).toHaveBeenCalledWith('foo'); }); it('calls previous handlers when one is removed', function() { const fakeGlobal = minimalBrowserGlobal(); const handler1 = jasmine.createSpy('errorHandler1'); const handler2 = jasmine.createSpy('errorHandler2'); const errors = new jasmineUnderTest.GlobalErrors(fakeGlobal); errors.install(); errors.pushListener(handler1); errors.pushListener(handler2); errors.popListener(handler2); fakeGlobal.onerror('foo'); expect(handler1).toHaveBeenCalledWith('foo'); expect(handler2).not.toHaveBeenCalled(); }); it('throws when no listener is passed to #popListener', function() { const errors = new jasmineUnderTest.GlobalErrors({}); expect(function() { errors.popListener(); }).toThrowError('popListener expects a listener'); }); 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(); expect(fakeGlobal.onerror).not.toBe(originalCallback); errors.uninstall(); expect(fakeGlobal.onerror).toBe(originalCallback); }); it('rethrows the original error when there is no handler', function() { const fakeGlobal = minimalBrowserGlobal(); const errors = new jasmineUnderTest.GlobalErrors(fakeGlobal); const originalError = new Error('nope'); errors.install(); try { fakeGlobal.onerror(originalError); } catch (e) { expect(e).toBe(originalError); } errors.uninstall(); }); it('reports uncaught exceptions in node.js', function() { 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(fakeGlobal.process.on).toHaveBeenCalledWith( 'uncaughtException', jasmine.any(Function) ); expect(fakeGlobal.process.listeners).toHaveBeenCalledWith( 'uncaughtException' ); expect(fakeGlobal.process.removeAllListeners).toHaveBeenCalledWith( 'uncaughtException' ); errors.pushListener(handler); const addedListener = fakeGlobal.process.on.calls.argsFor(0)[1]; addedListener(new Error('bar')); expect(handler).toHaveBeenCalledWith(new Error('bar')); expect(handler.calls.argsFor(0)[0].jasmineMessage).toBe( 'Uncaught exception: Error: bar' ); errors.uninstall(); 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 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(fakeGlobal.process.on).toHaveBeenCalledWith( 'unhandledRejection', jasmine.any(Function) ); expect(fakeGlobal.process.listeners).toHaveBeenCalledWith( 'unhandledRejection' ); expect(fakeGlobal.process.removeAllListeners).toHaveBeenCalledWith( 'unhandledRejection' ); errors.pushListener(handler); const addedListener = fakeGlobal.process.on.calls.argsFor(1)[1]; addedListener(new Error('bar')); expect(handler).toHaveBeenCalledWith(new Error('bar')); expect(handler.calls.argsFor(0)[0].jasmineMessage).toBe( 'Unhandled promise rejection: Error: bar' ); errors.uninstall(); expect(fakeGlobal.process.removeListener).toHaveBeenCalledWith( 'unhandledRejection', addedListener ); expect(fakeGlobal.process.on).toHaveBeenCalledWith( 'unhandledRejection', 'foo' ); }); it('reports rejections with non-`Error` reasons', function() { 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); 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(...).)' ) ); }); it('reports rejections with no reason provided', function() { 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); 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().)' ) ); }); }); describe('Reporting unhandled promise rejections in the browser', function() { it('subscribes and unsubscribes from the unhandledrejection event', function() { const fakeGlobal = jasmine.createSpyObj('globalErrors', [ 'addEventListener', 'removeEventListener', 'onerror' ]), errors = new jasmineUnderTest.GlobalErrors(fakeGlobal); errors.install(); expect(fakeGlobal.addEventListener).toHaveBeenCalledWith( 'unhandledrejection', jasmine.any(Function) ); const addedListener = fakeGlobal.addEventListener.calls.argsFor(0)[1]; errors.uninstall(); expect(fakeGlobal.removeEventListener).toHaveBeenCalledWith( 'unhandledrejection', addedListener ); }); 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'); addedListener({ reason: reason }); expect(handler).toHaveBeenCalledWith( jasmine.objectContaining({ jasmineMessage: 'Unhandled promise rejection: Error: bar', message: reason.message, stack: reason.stack }) ); }); describe('Enabling external interception of reported rejections by overriding global.onerror', function() { it('overriding global.onerror intercepts rejections whose reason is a string', function() { const fakeGlobal = jasmine.createSpyObj('globalErrors', [ 'addEventListener' ]), handler = jasmine.createSpy('errorHandler'), hijackHandler = jasmine.createSpy('hijackErrorHandler'), errors = new jasmineUnderTest.GlobalErrors(fakeGlobal); errors.install(); errors.pushListener(handler); fakeGlobal.onerror = hijackHandler; const addedListener = fakeGlobal.addEventListener.calls.argsFor(0)[1]; addedListener({ reason: 'nope' }); expect(hijackHandler).toHaveBeenCalledWith( 'Unhandled promise rejection: nope' ); expect(handler).not.toHaveBeenCalled(); }); it('overriding global.onerror intercepts rejections whose reason is an Error', function() { const fakeGlobal = jasmine.createSpyObj('globalErrors', [ 'addEventListener' ]), handler = jasmine.createSpy('errorHandler'), hijackHandler = jasmine.createSpy('hijackErrorHandler'), errors = new jasmineUnderTest.GlobalErrors(fakeGlobal); errors.install(); errors.pushListener(handler); fakeGlobal.onerror = hijackHandler; const addedListener = fakeGlobal.addEventListener.calls.argsFor(0)[1]; const reason = new Error('bar'); addedListener({ reason: reason }); expect(hijackHandler).toHaveBeenCalledWith( jasmine.objectContaining({ jasmineMessage: 'Unhandled promise rejection: Error: bar', message: reason.message, stack: reason.stack }) ); expect(handler).not.toHaveBeenCalled(); }); }); }); describe('#setOverrideListener', function() { it('overrides the existing handlers in browsers until removed', function() { const fakeGlobal = minimalBrowserGlobal(); const handler0 = jasmine.createSpy('handler0'); const handler1 = jasmine.createSpy('handler1'); const overrideHandler = jasmine.createSpy('overrideHandler'); const errors = new jasmineUnderTest.GlobalErrors(fakeGlobal); errors.install(); errors.pushListener(handler0); errors.setOverrideListener(overrideHandler, () => {}); errors.pushListener(handler1); 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(); fakeGlobal.onerror('baz'); expect(overrideHandler).not.toHaveBeenCalledWith('baz'); expect(handler1).toHaveBeenCalledWith('baz'); }); it('overrides the existing handlers in Node until removed', function() { 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(fakeGlobal); errors.install(); errors.pushListener(handler0); errors.setOverrideListener(overrideHandler); errors.pushListener(handler1); globalEventListeners['uncaughtException'](new Error('foo')); expect(overrideHandler).toHaveBeenCalledWith(new Error('foo')); expect(handler0).not.toHaveBeenCalled(); expect(handler1).not.toHaveBeenCalled(); errors.removeOverrideListener(); 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 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(fakeGlobal); errors.install(); errors.pushListener(handler); errors.setOverrideListener(overrideHandler, () => {}); const reason = new Error('bar'); globalEventListeners['unhandledrejection']({ reason: reason }); expect(overrideHandler).toHaveBeenCalledWith( jasmine.objectContaining({ jasmineMessage: 'Unhandled promise rejection: Error: bar', message: reason.message, stack: reason.stack }) ); expect(handler).not.toHaveBeenCalled(); }); it('handles unhandled promise rejections in Node', function() { 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(fakeGlobal); errors.install(); errors.pushListener(handler0); errors.setOverrideListener(overrideHandler, () => {}); errors.pushListener(handler1); globalEventListeners['unhandledRejection'](new Error('nope')); expect(overrideHandler).toHaveBeenCalledWith(new Error('nope')); expect(handler0).not.toHaveBeenCalled(); expect(handler1).not.toHaveBeenCalled(); }); it('throws if there is already an override handler', function() { const errors = new jasmineUnderTest.GlobalErrors(minimalBrowserGlobal()); errors.setOverrideListener(() => {}, () => {}); expect(function() { errors.setOverrideListener(() => {}, () => {}); }).toThrowError("Can't set more than one override listener at a time"); }); }); describe('#removeOverrideListener', function() { it("calls the handler's onRemove callback", function() { const onRemove = jasmine.createSpy('onRemove'); const errors = new jasmineUnderTest.GlobalErrors(minimalBrowserGlobal()); errors.setOverrideListener(() => {}, onRemove); errors.removeOverrideListener(); expect(onRemove).toHaveBeenCalledWith(); }); it('does not throw if there is no handler', function() { const errors = new jasmineUnderTest.GlobalErrors(minimalBrowserGlobal()); expect(() => errors.removeOverrideListener()).not.toThrow(); }); }); function minimalBrowserGlobal() { return { addEventListener() {}, removeEventListener() {}, onerror: null }; } }); jasmine-4.5.0/spec/core/JsApiReporterSpec.js000066400000000000000000000121721432731766000207460ustar00rootroot00000000000000describe('JsApiReporter', function() { it('knows when a full environment is started', function() { const reporter = new jasmineUnderTest.JsApiReporter({}); expect(reporter.started).toBe(false); expect(reporter.finished).toBe(false); reporter.jasmineStarted(); expect(reporter.started).toBe(true); expect(reporter.finished).toBe(false); }); it('knows when a full environment is done', function() { const reporter = new jasmineUnderTest.JsApiReporter({}); expect(reporter.started).toBe(false); expect(reporter.finished).toBe(false); reporter.jasmineStarted(); reporter.jasmineDone({}); expect(reporter.finished).toBe(true); }); it("defaults to 'loaded' status", function() { const reporter = new jasmineUnderTest.JsApiReporter({}); expect(reporter.status()).toEqual('loaded'); }); it("reports 'started' when Jasmine has started", function() { const reporter = new jasmineUnderTest.JsApiReporter({}); reporter.jasmineStarted(); expect(reporter.status()).toEqual('started'); }); it("reports 'done' when Jasmine is done", function() { const reporter = new jasmineUnderTest.JsApiReporter({}); reporter.jasmineDone({}); expect(reporter.status()).toEqual('done'); }); it('tracks a suite', function() { const reporter = new jasmineUnderTest.JsApiReporter({}); reporter.suiteStarted({ id: 123, description: 'A suite' }); const suites = reporter.suites(); expect(suites).toEqual({ 123: { id: 123, description: 'A suite' } }); reporter.suiteDone({ id: 123, description: 'A suite', status: 'passed' }); expect(suites).toEqual({ 123: { id: 123, description: 'A suite', status: 'passed' } }); }); describe('#specResults', function() { let reporter, specResult1, specResult2; beforeEach(function() { reporter = new jasmineUnderTest.JsApiReporter({}); specResult1 = { id: 1, description: 'A spec' }; specResult2 = { id: 2, description: 'Another spec' }; reporter.specDone(specResult1); reporter.specDone(specResult2); }); it('should return a slice of results', function() { expect(reporter.specResults(0, 1)).toEqual([specResult1]); expect(reporter.specResults(1, 1)).toEqual([specResult2]); }); describe('when the results do not exist', function() { it('should return a slice of shorter length', function() { expect(reporter.specResults(0, 3)).toEqual([specResult1, specResult2]); expect(reporter.specResults(2, 3)).toEqual([]); }); }); }); describe('#suiteResults', function() { let reporter, suiteStarted1, suiteResult1, suiteResult2; beforeEach(function() { reporter = new jasmineUnderTest.JsApiReporter({}); suiteStarted1 = { id: 1 }; suiteResult1 = { id: 1, status: 'failed', failedExpectations: [{ message: 'My After All Exception' }] }; suiteResult2 = { id: 2, status: 'passed' }; reporter.suiteStarted(suiteStarted1); reporter.suiteDone(suiteResult1); reporter.suiteDone(suiteResult2); }); it('should not include suite starts', function() { expect(reporter.suiteResults(0, 3).length).toEqual(2); }); it('should return a slice of results', function() { expect(reporter.suiteResults(0, 1)).toEqual([suiteResult1]); expect(reporter.suiteResults(1, 1)).toEqual([suiteResult2]); }); it('returns nothing for out of bounds indices', function() { expect(reporter.suiteResults(0, 3)).toEqual([suiteResult1, suiteResult2]); expect(reporter.suiteResults(2, 3)).toEqual([]); }); }); describe('#executionTime', function() { it('should start the timer when jasmine starts', function() { const timerSpy = jasmine.createSpyObj('timer', ['start', 'elapsed']), reporter = new jasmineUnderTest.JsApiReporter({ timer: timerSpy }); reporter.jasmineStarted(); expect(timerSpy.start).toHaveBeenCalled(); }); it('should return the time it took the specs to run, in ms', function() { const timerSpy = jasmine.createSpyObj('timer', ['start', 'elapsed']), reporter = new jasmineUnderTest.JsApiReporter({ timer: timerSpy }); timerSpy.elapsed.and.returnValue(1000); reporter.jasmineDone(); expect(reporter.executionTime()).toEqual(1000); }); describe("when the specs haven't finished being run", function() { it('should return undefined', function() { const timerSpy = jasmine.createSpyObj('timer', ['start', 'elapsed']), reporter = new jasmineUnderTest.JsApiReporter({ timer: timerSpy }); expect(reporter.executionTime()).toBeUndefined(); }); }); }); describe('#runDetails', function() { it('should have details about the run', function() { const reporter = new jasmineUnderTest.JsApiReporter({}); reporter.jasmineDone({ some: { run: 'details' } }); expect(reporter.runDetails).toEqual({ some: { run: 'details' } }); }); }); }); jasmine-4.5.0/spec/core/MockDateSpec.js000066400000000000000000000125501432731766000177040ustar00rootroot00000000000000describe('FakeDate', function() { it('does not fail if no global date is found', function() { const fakeGlobal = {}, mockDate = new jasmineUnderTest.MockDate(fakeGlobal); expect(function() { mockDate.install(); mockDate.tick(0); mockDate.uninstall(); }).not.toThrow(); }); it('replaces the global Date when it is installed', function() { const globalDate = jasmine .createSpy('global Date') .and.callFake(function() { return { getTime: function() {} }; }), fakeGlobal = { Date: globalDate }, mockDate = new jasmineUnderTest.MockDate(fakeGlobal); expect(fakeGlobal.Date).toEqual(globalDate); mockDate.install(); expect(fakeGlobal.Date).not.toEqual(globalDate); }); it('replaces the global Date on uninstall', function() { const globalDate = jasmine .createSpy('global Date') .and.callFake(function() { return { getTime: function() {} }; }), fakeGlobal = { Date: globalDate }, mockDate = new jasmineUnderTest.MockDate(fakeGlobal); mockDate.install(); mockDate.uninstall(); expect(fakeGlobal.Date).toEqual(globalDate); }); it('takes the current time as the base when installing without parameters', function() { const globalDate = jasmine .createSpy('global Date') .and.callFake(function() { return { getTime: function() { return 1000; } }; }), fakeGlobal = { Date: globalDate }, mockDate = new jasmineUnderTest.MockDate(fakeGlobal); mockDate.install(); globalDate.calls.reset(); new fakeGlobal.Date(); expect(globalDate).toHaveBeenCalledWith(1000); }); it('can accept a date as time base when installing', function() { const fakeGlobal = { Date: Date }, mockDate = new jasmineUnderTest.MockDate(fakeGlobal), baseDate = new Date(); spyOn(baseDate, 'getTime').and.returnValue(123); mockDate.install(baseDate); expect(new fakeGlobal.Date().getTime()).toEqual(123); }); it('makes real dates', function() { const fakeGlobal = { Date: Date }, mockDate = new jasmineUnderTest.MockDate(fakeGlobal); mockDate.install(); expect(new fakeGlobal.Date()).toEqual(jasmine.any(Date)); expect(new fakeGlobal.Date() instanceof fakeGlobal.Date).toBe(true); }); it('fakes current time when using Date.now()', function() { const globalDate = jasmine .createSpy('global Date') .and.callFake(function() { return { getTime: function() { return 1000; } }; }), fakeGlobal = { Date: globalDate }; globalDate.now = function() {}; const mockDate = new jasmineUnderTest.MockDate(fakeGlobal); mockDate.install(); expect(fakeGlobal.Date.now()).toEqual(1000); }); it('makes time passes using tick', function() { const globalDate = jasmine .createSpy('global Date') .and.callFake(function() { return { getTime: function() { return 1000; } }; }), fakeGlobal = { Date: globalDate }; globalDate.now = function() {}; const mockDate = new jasmineUnderTest.MockDate(fakeGlobal); mockDate.install(); mockDate.tick(100); expect(fakeGlobal.Date.now()).toEqual(1100); mockDate.tick(1000); expect(fakeGlobal.Date.now()).toEqual(2100); }); it('allows to increase 0 milliseconds using tick', function() { const globalDate = jasmine .createSpy('global Date') .and.callFake(function() { return { getTime: function() { return 1000; } }; }), fakeGlobal = { Date: globalDate }; globalDate.now = function() {}; const mockDate = new jasmineUnderTest.MockDate(fakeGlobal); mockDate.install(); mockDate.tick(0); expect(fakeGlobal.Date.now()).toEqual(1000); mockDate.tick(); expect(fakeGlobal.Date.now()).toEqual(1000); }); it('allows creation of a Date in a different time than the mocked time', function() { const fakeGlobal = { Date: Date }, mockDate = new jasmineUnderTest.MockDate(fakeGlobal); mockDate.install(); const otherDate = new fakeGlobal.Date(2013, 9, 23, 0, 0, 1, 0); expect(otherDate.getTime()).toEqual( new Date(2013, 9, 23, 0, 0, 1, 0).getTime() ); }); it("allows creation of a Date that isn't fully specified", function() { const fakeGlobal = { Date: Date }, mockDate = new jasmineUnderTest.MockDate(fakeGlobal); mockDate.install(); const otherDate = new fakeGlobal.Date(2013, 9, 23); expect(otherDate.getTime()).toEqual(new Date(2013, 9, 23).getTime()); }); it('allows creation of a Date with millis', function() { const fakeGlobal = { Date: Date }, mockDate = new jasmineUnderTest.MockDate(fakeGlobal), now = new Date(2014, 3, 15).getTime(); mockDate.install(); const otherDate = new fakeGlobal.Date(now); expect(otherDate.getTime()).toEqual(now); }); it('copies all Date properties to the mocked date', function() { const fakeGlobal = { Date: Date }, mockDate = new jasmineUnderTest.MockDate(fakeGlobal); mockDate.install(); expect(fakeGlobal.Date.UTC(2013, 9, 23)).toEqual(Date.UTC(2013, 9, 23)); }); }); jasmine-4.5.0/spec/core/PrettyPrintSpec.js000066400000000000000000000447461432731766000205350ustar00rootroot00000000000000describe('PrettyPrinter', function() { it('should wrap strings in single quotes', function() { const pp = jasmineUnderTest.makePrettyPrinter(); expect(pp('some string')).toEqual("'some string'"); expect(pp("som' string")).toEqual("'som' string'"); }); it('stringifies empty string primitives and objects recognizably', function() { const pp = jasmineUnderTest.makePrettyPrinter(); expect(pp(new String(''))).toEqual(pp('')); expect(pp(new String(''))).toEqual("''"); expect(pp([new String('')])).toEqual(pp([''])); expect(pp([new String('')])).toEqual("[ '' ]"); }); it('should stringify primitives properly', function() { const pp = jasmineUnderTest.makePrettyPrinter(); expect(pp(true)).toEqual('true'); expect(pp(false)).toEqual('false'); expect(pp(null)).toEqual('null'); expect(pp(jasmine.undefined)).toEqual('undefined'); expect(pp(3)).toEqual('3'); expect(pp(-3.14)).toEqual('-3.14'); expect(pp(-0)).toEqual('-0'); }); describe('stringify sets', function() { it('should stringify sets properly', function() { const set = new Set(); set.add(1); set.add(2); const pp = jasmineUnderTest.makePrettyPrinter(); expect(pp(set)).toEqual('Set( 1, 2 )'); }); it('should truncate sets with more elements than jasmineUnderTest.MAX_PRETTY_PRINT_ARRAY_LENGTH', function() { const originalMaxSize = jasmineUnderTest.MAX_PRETTY_PRINT_ARRAY_LENGTH; try { jasmineUnderTest.MAX_PRETTY_PRINT_ARRAY_LENGTH = 2; const set = new Set(); set.add('a'); set.add('b'); set.add('c'); const pp = jasmineUnderTest.makePrettyPrinter(); expect(pp(set)).toEqual("Set( 'a', 'b', ... )"); } finally { jasmineUnderTest.MAX_PRETTY_PRINT_ARRAY_LENGTH = originalMaxSize; } }); }); describe('stringify maps', function() { it('should stringify maps properly', function() { const map = new Map(); map.set(1, 2); const pp = jasmineUnderTest.makePrettyPrinter(); expect(pp(map)).toEqual('Map( [ 1, 2 ] )'); }); it('should truncate maps with more elements than jasmineUnderTest.MAX_PRETTY_PRINT_ARRAY_LENGTH', function() { const originalMaxSize = jasmineUnderTest.MAX_PRETTY_PRINT_ARRAY_LENGTH; try { jasmineUnderTest.MAX_PRETTY_PRINT_ARRAY_LENGTH = 2; const map = new Map(); map.set('a', 1); map.set('b', 2); map.set('c', 3); const pp = jasmineUnderTest.makePrettyPrinter(); expect(pp(map)).toEqual("Map( [ 'a', 1 ], [ 'b', 2 ], ... )"); } finally { jasmineUnderTest.MAX_PRETTY_PRINT_ARRAY_LENGTH = originalMaxSize; } }); }); describe('stringify arrays', function() { it('should stringify arrays properly', function() { const pp = jasmineUnderTest.makePrettyPrinter(); expect(pp([1, 2])).toEqual('[ 1, 2 ]'); expect(pp([1, 'foo', {}, jasmine.undefined, null])).toEqual( "[ 1, 'foo', Object({ }), undefined, null ]" ); }); it('includes symbols', function() { const pp = jasmineUnderTest.makePrettyPrinter(); expect(pp([1, Symbol('foo'), 2])).toEqual('[ 1, Symbol(foo), 2 ]'); }); it('should truncate arrays that are longer than jasmineUnderTest.MAX_PRETTY_PRINT_ARRAY_LENGTH', function() { const originalMaxLength = jasmineUnderTest.MAX_PRETTY_PRINT_ARRAY_LENGTH; const array = [1, 2, 3]; const pp = jasmineUnderTest.makePrettyPrinter(); try { jasmineUnderTest.MAX_PRETTY_PRINT_ARRAY_LENGTH = 2; expect(pp(array)).toEqual('[ 1, 2, ... ]'); } finally { jasmineUnderTest.MAX_PRETTY_PRINT_ARRAY_LENGTH = originalMaxLength; } }); it('should stringify arrays with properties properly', function() { const pp = jasmineUnderTest.makePrettyPrinter(); const arr = [1, 2]; arr.foo = 'bar'; arr.baz = {}; expect(pp(arr)).toEqual("[ 1, 2, foo: 'bar', baz: Object({ }) ]"); }); it('should stringify empty arrays with properties properly', function() { const pp = jasmineUnderTest.makePrettyPrinter(); const empty = []; empty.foo = 'bar'; empty.baz = {}; expect(pp(empty)).toEqual("[ foo: 'bar', baz: Object({ }) ]"); }); it('should stringify long arrays with properties properly', function() { const pp = jasmineUnderTest.makePrettyPrinter(); const originalMaxLength = jasmineUnderTest.MAX_PRETTY_PRINT_ARRAY_LENGTH; const long = [1, 2, 3]; long.foo = 'bar'; long.baz = {}; try { jasmineUnderTest.MAX_PRETTY_PRINT_ARRAY_LENGTH = 2; expect(pp(long)).toEqual( "[ 1, 2, ..., foo: 'bar', baz: Object({ }) ]" ); } finally { jasmineUnderTest.MAX_PRETTY_PRINT_ARRAY_LENGTH = originalMaxLength; } }); it('should indicate circular array references', function() { const pp = jasmineUnderTest.makePrettyPrinter(); const array1 = [1, 2]; const array2 = [array1]; array1.push(array2); expect(pp(array1)).toEqual('[ 1, 2, [ ] ]'); }); it('should not indicate circular references incorrectly', function() { const pp = jasmineUnderTest.makePrettyPrinter(); const array = [[1]]; expect(pp(array)).toEqual('[ [ 1 ] ]'); }); }); it('should stringify objects properly', function() { const pp = jasmineUnderTest.makePrettyPrinter(); expect(pp({ foo: 'bar' })).toEqual("Object({ foo: 'bar' })"); expect( pp({ foo: 'bar', baz: 3, nullValue: null, undefinedValue: jasmine.undefined }) ).toEqual( "Object({ foo: 'bar', baz: 3, nullValue: null, undefinedValue: undefined })" ); expect(pp({ foo: function() {}, bar: [1, 2, 3] })).toEqual( 'Object({ foo: Function, bar: [ 1, 2, 3 ] })' ); }); it('includes symbol keys in objects', function() { const pp = jasmineUnderTest.makePrettyPrinter(); const obj = {}; obj[Symbol('foo')] = 'bar'; expect(pp(obj)).toEqual("Object({ Symbol(foo): 'bar' })"); }); it('stringifies string and symbol keys differently', function() { const pp = jasmineUnderTest.makePrettyPrinter(); const symObj = {}; const strObj = {}; const k = 'foo'; const v = 'bar'; symObj[Symbol(k)] = v; strObj[k] = v; expect(pp(symObj)).not.toEqual(pp(strObj)); }); it('should stringify objects that almost look like DOM nodes', function() { const pp = jasmineUnderTest.makePrettyPrinter(); expect(pp({ nodeType: 1 })).toEqual('Object({ nodeType: 1 })'); }); it('should truncate objects with too many keys', function() { const pp = jasmineUnderTest.makePrettyPrinter(); const originalMaxLength = jasmineUnderTest.MAX_PRETTY_PRINT_ARRAY_LENGTH; const long = { a: 1, b: 2, c: 3 }; try { jasmineUnderTest.MAX_PRETTY_PRINT_ARRAY_LENGTH = 2; expect(pp(long)).toEqual('Object({ a: 1, b: 2, ... })'); } finally { jasmineUnderTest.MAX_PRETTY_PRINT_ARRAY_LENGTH = originalMaxLength; } }); function withMaxChars(maxChars, fn) { const originalMaxChars = jasmineUnderTest.MAX_PRETTY_PRINT_CHARS; try { jasmineUnderTest.MAX_PRETTY_PRINT_CHARS = maxChars; fn(); } finally { jasmineUnderTest.MAX_PRETTY_PRINT_CHARS = originalMaxChars; } } it('should truncate outputs that are too long', function() { const pp = jasmineUnderTest.makePrettyPrinter(); const big = [{ a: 1, b: 'a long string' }, {}]; withMaxChars(34, function() { expect(pp(big)).toEqual("[ Object({ a: 1, b: 'a long st ..."); }); }); it('should not serialize more objects after hitting MAX_PRETTY_PRINT_CHARS', function() { const a = { jasmineToString: function() { return 'object a'; } }, b = { jasmineToString: function() { return 'object b'; } }, c = { jasmineToString: jasmine .createSpy('c jasmineToString') .and.returnValue('') }, d = { jasmineToString: jasmine .createSpy('d jasmineToString') .and.returnValue('') }, pp = jasmineUnderTest.makePrettyPrinter(); withMaxChars(30, function() { pp([{ a: a, b: b, c: c }, d]); expect(c.jasmineToString).not.toHaveBeenCalled(); expect(d.jasmineToString).not.toHaveBeenCalled(); }); }); it("should print 'null' as the constructor of an object with its own constructor property", function() { const pp = jasmineUnderTest.makePrettyPrinter(); expect(pp({ constructor: function() {} })).toContain('null({'); expect(pp({ constructor: 'foo' })).toContain('null({'); }); it('should not include inherited properties when stringifying an object', function() { const pp = jasmineUnderTest.makePrettyPrinter(); const SomeClass = function SomeClass() {}; SomeClass.prototype.foo = 'inherited foo'; const instance = new SomeClass(); instance.bar = 'my own bar'; expect(pp(instance)).toEqual("SomeClass({ bar: 'my own bar' })"); }); it('should not recurse objects and arrays more deeply than jasmineUnderTest.MAX_PRETTY_PRINT_DEPTH', function() { const pp = jasmineUnderTest.makePrettyPrinter(); const originalMaxDepth = jasmineUnderTest.MAX_PRETTY_PRINT_DEPTH; const nestedObject = { level1: { level2: { level3: { level4: 'leaf' } } } }; const nestedArray = [1, [2, [3, [4, 'leaf']]]]; try { jasmineUnderTest.MAX_PRETTY_PRINT_DEPTH = 2; expect(pp(nestedObject)).toEqual( 'Object({ level1: Object({ level2: Object }) })' ); expect(pp(nestedArray)).toEqual('[ 1, [ 2, Array ] ]'); jasmineUnderTest.MAX_PRETTY_PRINT_DEPTH = 3; expect(pp(nestedObject)).toEqual( 'Object({ level1: Object({ level2: Object({ level3: Object }) }) })' ); expect(pp(nestedArray)).toEqual('[ 1, [ 2, [ 3, Array ] ] ]'); jasmineUnderTest.MAX_PRETTY_PRINT_DEPTH = 4; expect(pp(nestedObject)).toEqual( "Object({ level1: Object({ level2: Object({ level3: Object({ level4: 'leaf' }) }) }) })" ); expect(pp(nestedArray)).toEqual("[ 1, [ 2, [ 3, [ 4, 'leaf' ] ] ] ]"); } finally { jasmineUnderTest.MAX_PRETTY_PRINT_DEPTH = originalMaxDepth; } }); it('should stringify immutable circular objects', function() { const pp = jasmineUnderTest.makePrettyPrinter(); let frozenObject = { foo: { bar: 'baz' } }; frozenObject.circular = frozenObject; frozenObject = Object.freeze(frozenObject); expect(pp(frozenObject)).toEqual( "Object({ foo: Object({ bar: 'baz' }), circular: })" ); }); it('should stringify RegExp objects properly', function() { const pp = jasmineUnderTest.makePrettyPrinter(); expect(pp(/x|y|z/)).toEqual('/x|y|z/'); }); it('should indicate circular object references', function() { const pp = jasmineUnderTest.makePrettyPrinter(); const sampleValue = { foo: 'hello' }; sampleValue.nested = sampleValue; expect(pp(sampleValue)).toEqual( "Object({ foo: 'hello', nested: })" ); }); it('should use the return value of getters', function() { const pp = jasmineUnderTest.makePrettyPrinter(); const sampleValue = { id: 1, get calculatedValue() { return 'the getter return value'; } }; expect(pp(sampleValue)).toEqual( "Object({ id: 1, calculatedValue: 'the getter return value' })" ); }); it('should not do HTML escaping of strings', function() { const pp = jasmineUnderTest.makePrettyPrinter(); expect(pp('some html string &', false)).toEqual( "'some html string &'" ); }); it('should abbreviate the global (usually window) object', function() { const pp = jasmineUnderTest.makePrettyPrinter(); expect(pp(jasmine.getGlobal())).toEqual(''); }); it('should stringify Date objects properly', function() { const pp = jasmineUnderTest.makePrettyPrinter(); const now = new Date(); expect(pp(now)).toEqual('Date(' + now.toString() + ')'); }); describe('with a spy object', function() { let env, pp; beforeEach(function() { env = new jasmineUnderTest.Env(); pp = jasmineUnderTest.makePrettyPrinter(); }); afterEach(function() { env.cleanup_(); }); it('should stringify spy objects properly', function() { const TestObject = { someFunction: function() {} }; const spyRegistry = new jasmineUnderTest.SpyRegistry({ currentSpies: function() { return []; }, createSpy: function(name, originalFn) { return jasmineUnderTest.Spy(name, originalFn); } }); spyRegistry.spyOn(TestObject, 'someFunction'); expect(pp(TestObject.someFunction)).toEqual('spy on someFunction'); expect(pp(env.createSpy('something'))).toEqual('spy on something'); }); it('should stringify spyOn toString properly', function() { const TestObject = { someFunction: function() {} }, env = new jasmineUnderTest.Env(), pp = jasmineUnderTest.makePrettyPrinter(); const spyRegistry = new jasmineUnderTest.SpyRegistry({ currentSpies: function() { return []; }, createSpy: function(name, originalFn) { return jasmineUnderTest.Spy(name, originalFn); } }); spyRegistry.spyOn(TestObject, 'toString'); const testSpyObj = env.createSpyObj('TheClassName', ['toString']); expect(pp(testSpyObj)).toEqual('spy on TheClassName.toString'); }); }); it('should stringify objects that implement jasmineToString', function() { const pp = jasmineUnderTest.makePrettyPrinter(); const obj = { jasmineToString: function() { return 'strung'; } }; expect(pp(obj)).toEqual('strung'); }); it('should pass itself to jasmineToString', function() { const pp = jasmineUnderTest.makePrettyPrinter([]); const obj = { jasmineToString: jasmine.createSpy('jasmineToString').and.returnValue('') }; pp(obj); expect(obj.jasmineToString).toHaveBeenCalledWith(pp); }); it('should stringify objects that implement custom toString', function() { const pp = jasmineUnderTest.makePrettyPrinter(); const obj = { toString: function() { return 'my toString'; } }; expect(pp(obj)).toEqual('my toString'); // Simulate object from another global context (e.g. an iframe or Web Worker) that does not actually have a custom // toString despite obj.toString !== Object.prototype.toString const objFromOtherContext = { foo: 'bar', toString: function() { return Object.prototype.toString.call(this); } }; expect(pp(objFromOtherContext)).toEqual( "Object({ foo: 'bar', toString: Function })" ); }); it("should stringify objects have have a toString that isn't a function", function() { const pp = jasmineUnderTest.makePrettyPrinter(); const obj = { toString: 'foo' }; expect(pp(obj)).toEqual("Object({ toString: 'foo' })"); }); it('should stringify objects from anonymous constructors with custom toString', function() { const pp = jasmineUnderTest.makePrettyPrinter(); const MyAnonymousConstructor = (function() { return function() {}; })(); MyAnonymousConstructor.toString = function() { return ''; }; const a = new MyAnonymousConstructor(); expect(pp(a)).toEqual('({ })'); }); it('should handle objects with null prototype', function() { const pp = jasmineUnderTest.makePrettyPrinter(); const obj = Object.create(null); obj.foo = 'bar'; expect(pp(obj)).toEqual("null({ foo: 'bar' })"); }); it('should gracefully handle objects with invalid toString implementations', function() { const pp = jasmineUnderTest.makePrettyPrinter(); const obj = { foo: { toString: function() { // Invalid: toString returning a number return 3; } }, bar: { toString: function() { // Really invalid: a nested bad toString(). return { toString: function() { return new Date(); } }; } }, // Valid: an actual number baz: 3, // Valid: an actual Error object qux: new Error('bar'), // baddy: { toString: function() { throw new Error('I am a bad toString'); } } }; expect(pp(obj)).toEqual( 'Object({ foo: [object Number], bar: [object Object], baz: 3, qux: Error: bar, baddy: has-invalid-toString-method })' ); }); describe('Custom object formatters', function() { it('should use the first custom object formatter that does not return undefined', function() { const customObjectFormatters = [ function() { return undefined; }, function(obj) { return '2nd: ' + obj.foo; }, function(obj) { return '3rd: ' + obj.foo; } ], pp = jasmineUnderTest.makePrettyPrinter(customObjectFormatters), obj = { foo: 'bar' }; expect(pp(obj)).toEqual('2nd: bar'); }); it('should fall back to built in logic if all custom object formatters return undefined', function() { const customObjectFormatters = [ function() { return undefined; } ], pp = jasmineUnderTest.makePrettyPrinter(customObjectFormatters), obj = { foo: 'bar' }; expect(pp(obj)).toEqual("Object({ foo: 'bar' })"); }); }); describe('#customFormat_', function() { it('should use the first custom object formatter that does not return undefined', function() { const customObjectFormatters = [ function() { return undefined; }, function(obj) { return '2nd: ' + obj.foo; }, function(obj) { return '3rd: ' + obj.foo; } ], pp = jasmineUnderTest.makePrettyPrinter(customObjectFormatters), obj = { foo: 'bar' }; expect(pp.customFormat_(obj)).toEqual('2nd: bar'); }); it('should return undefined if all custom object formatters return undefined', function() { const customObjectFormatters = [ function() { return undefined; } ], pp = jasmineUnderTest.makePrettyPrinter(customObjectFormatters), obj = { foo: 'bar' }; expect(pp.customFormat_(obj)).toBeUndefined(); }); }); }); jasmine-4.5.0/spec/core/QueueRunnerSpec.js000066400000000000000000001002661432731766000204750ustar00rootroot00000000000000describe('QueueRunner', function() { it("runs all the functions it's passed", function() { const calls = [], queueableFn1 = { fn: jasmine.createSpy('fn1') }, queueableFn2 = { fn: jasmine.createSpy('fn2') }, queueRunner = new jasmineUnderTest.QueueRunner({ queueableFns: [queueableFn1, queueableFn2] }); queueableFn1.fn.and.callFake(function() { calls.push('fn1'); }); queueableFn2.fn.and.callFake(function() { calls.push('fn2'); }); queueRunner.execute(); expect(calls).toEqual(['fn1', 'fn2']); }); it("calls each function with a consistent 'this'-- an empty object", function() { const queueableFn1 = { fn: jasmine.createSpy('fn1') }; const queueableFn2 = { fn: jasmine.createSpy('fn2') }; let asyncContext; const queueableFn3 = { fn: function(done) { asyncContext = this; done(); } }, queueRunner = new jasmineUnderTest.QueueRunner({ queueableFns: [queueableFn1, queueableFn2, queueableFn3] }); queueRunner.execute(); const context = queueableFn1.fn.calls.first().object; expect(context).toEqual(new jasmineUnderTest.UserContext()); expect(queueableFn2.fn.calls.first().object).toBe(context); expect(asyncContext).toBe(context); }); describe('with an asynchronous function', function() { beforeEach(function() { jasmine.clock().install(); }); afterEach(function() { jasmine.clock().uninstall(); }); it('supports asynchronous functions, only advancing to next function after a done() callback', function() { //TODO: it would be nice if spy arity could match the fake, so we could do something like: //createSpy('asyncfn').and.callFake(function(done) {}); const onComplete = jasmine.createSpy('onComplete'), beforeCallback = jasmine.createSpy('beforeCallback'), fnCallback = jasmine.createSpy('fnCallback'), afterCallback = jasmine.createSpy('afterCallback'), queueableFn1 = { fn: function(done) { beforeCallback(); setTimeout(done, 100); } }, queueableFn2 = { fn: function(done) { fnCallback(); setTimeout(done, 100); } }, queueableFn3 = { fn: function(done) { afterCallback(); setTimeout(done, 100); } }, queueRunner = new jasmineUnderTest.QueueRunner({ queueableFns: [queueableFn1, queueableFn2, queueableFn3], onComplete: onComplete }); queueRunner.execute(); expect(beforeCallback).toHaveBeenCalled(); expect(fnCallback).not.toHaveBeenCalled(); expect(afterCallback).not.toHaveBeenCalled(); expect(onComplete).not.toHaveBeenCalled(); jasmine.clock().tick(100); expect(fnCallback).toHaveBeenCalled(); expect(afterCallback).not.toHaveBeenCalled(); expect(onComplete).not.toHaveBeenCalled(); jasmine.clock().tick(100); expect(afterCallback).toHaveBeenCalled(); expect(onComplete).not.toHaveBeenCalled(); jasmine.clock().tick(100); expect(onComplete).toHaveBeenCalled(); }); it('explicitly fails an async function with a provided fail function and moves to the next function', function() { const queueableFn1 = { fn: function(done) { setTimeout(function() { done.fail('foo'); }, 100); } }, queueableFn2 = { fn: jasmine.createSpy('fn2') }, failFn = jasmine.createSpy('fail'), queueRunner = new jasmineUnderTest.QueueRunner({ queueableFns: [queueableFn1, queueableFn2], fail: failFn }); queueRunner.execute(); expect(failFn).not.toHaveBeenCalled(); expect(queueableFn2.fn).not.toHaveBeenCalled(); jasmine.clock().tick(100); expect(failFn).toHaveBeenCalledWith('foo'); expect(queueableFn2.fn).toHaveBeenCalled(); }); describe('When next is called with an argument', function() { it('explicitly fails and moves to the next function', function() { const err = 'anything except undefined', queueableFn1 = { fn: function(done) { setTimeout(function() { done(err); }, 100); } }, queueableFn2 = { fn: jasmine.createSpy('fn2') }, failFn = jasmine.createSpy('fail'), queueRunner = new jasmineUnderTest.QueueRunner({ queueableFns: [queueableFn1, queueableFn2], fail: failFn }); queueRunner.execute(); expect(failFn).not.toHaveBeenCalled(); expect(queueableFn2.fn).not.toHaveBeenCalled(); jasmine.clock().tick(100); expect(failFn).toHaveBeenCalledWith(err); expect(queueableFn2.fn).toHaveBeenCalled(); }); describe('as a result of a promise', function() { describe('and the argument is an Error', function() { // Since promise support was added, Jasmine has failed specs that // return a promise that resolves to an error. That's probably not // the desired behavior but it's also not something we should change // except on a major release and with a deprecation warning in // advance. it('explicitly fails and moves to the next function', function(done) { const err = new Error('foo'), queueableFn1 = { fn: function() { return Promise.resolve(err); } }, queueableFn2 = { fn: jasmine.createSpy('fn2') }, failFn = jasmine.createSpy('fail'), queueRunner = new jasmineUnderTest.QueueRunner({ queueableFns: [queueableFn1, queueableFn2], fail: failFn, onComplete: function() { expect(failFn).toHaveBeenCalledWith(err); expect(queueableFn2.fn).toHaveBeenCalled(); done(); } }); queueRunner.execute(); }); it('does not log a deprecation', function(done) { const err = new Error('foo'), queueableFn1 = { fn: function() { return Promise.resolve(err); } }, deprecated = jasmine.createSpy('deprecated'), queueRunner = new jasmineUnderTest.QueueRunner({ queueableFns: [queueableFn1], deprecated: deprecated, onComplete: function() { expect(deprecated).not.toHaveBeenCalled(); done(); } }); queueRunner.execute(); }); }); describe('and the argument is not an Error', function() { it('does not log a deprecation or report a failure', function(done) { 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(); } }); queueRunner.execute(); }); }); }); }); it('does not cause an explicit fail if execution is being stopped', function() { const err = new jasmineUnderTest.StopExecutionError('foo'), queueableFn1 = { fn: function(done) { setTimeout(function() { done(err); }, 100); } }, queueableFn2 = { fn: jasmine.createSpy('fn2') }, failFn = jasmine.createSpy('fail'), queueRunner = new jasmineUnderTest.QueueRunner({ queueableFns: [queueableFn1, queueableFn2], fail: failFn }); queueRunner.execute(); expect(failFn).not.toHaveBeenCalled(); expect(queueableFn2.fn).not.toHaveBeenCalled(); jasmine.clock().tick(100); expect(failFn).not.toHaveBeenCalled(); expect(queueableFn2.fn).toHaveBeenCalled(); }); 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'), onException = jasmine.createSpy('onException'), queueRunner = new jasmineUnderTest.QueueRunner({ queueableFns: [beforeFn, queueableFn], onComplete: onComplete, onException: onException }); queueRunner.execute(); expect(queueableFn.fn).not.toHaveBeenCalled(); jasmine.clock().tick(timeout); expect(onException).toHaveBeenCalledWith(jasmine.any(Error)); expect(queueableFn.fn).toHaveBeenCalled(); expect(onComplete).toHaveBeenCalled(); }); it('does not call onMultipleDone if an asynchrnous function completes after timing out', function() { const timeout = 3; let queueableFnDone; const queueableFn = { fn: function(done) { queueableFnDone = done; }, type: 'queueable', timeout: timeout }; const onComplete = jasmine.createSpy('onComplete'); const onMultipleDone = jasmine.createSpy('onMultipleDone'); const queueRunner = new jasmineUnderTest.QueueRunner({ queueableFns: [queueableFn], onComplete: onComplete, onMultipleDone: onMultipleDone }); queueRunner.execute(); jasmine.clock().tick(timeout); queueableFnDone(); expect(onComplete).toHaveBeenCalled(); expect(onMultipleDone).not.toHaveBeenCalled(); }); 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'), onException = jasmine.createSpy('onException'), queueRunner = new jasmineUnderTest.QueueRunner({ queueableFns: [beforeFn, queueableFn], onComplete: onComplete, onException: onException }); queueRunner.execute(); expect(queueableFn.fn).not.toHaveBeenCalled(); jasmine.clock().tick(jasmineUnderTest.DEFAULT_TIMEOUT_INTERVAL); expect(onException).not.toHaveBeenCalled(); expect(queueableFn.fn).not.toHaveBeenCalled(); expect(onComplete).not.toHaveBeenCalled(); }); 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!'); } }, onComplete = jasmine.createSpy('onComplete'), onException = jasmine.createSpy('onException'), queueRunner = new jasmineUnderTest.QueueRunner({ queueableFns: [queueableFn], onComplete: onComplete, onException: onException }); queueRunner.execute(); expect(onComplete).toHaveBeenCalled(); expect(onException).toHaveBeenCalled(); jasmine.clock().tick(jasmineUnderTest.DEFAULT_TIMEOUT_INTERVAL); expect(onException.calls.count()).toEqual(1); }); it('clears the timeout when the done callback is called', function() { const queueableFn = { fn: function(done) { done(); } }, onComplete = jasmine.createSpy('onComplete'), onException = jasmine.createSpy('onException'), queueRunner = new jasmineUnderTest.QueueRunner({ queueableFns: [queueableFn], onComplete: onComplete, onException: onException }); queueRunner.execute(); jasmine.clock().tick(1); expect(onComplete).toHaveBeenCalled(); jasmine.clock().tick(jasmineUnderTest.DEFAULT_TIMEOUT_INTERVAL); expect(onException).not.toHaveBeenCalled(); }); it('only moves to the next spec the first time you call done', function() { const queueableFn = { fn: function(done) { done(); done(); } }, nextQueueableFn = { fn: jasmine.createSpy('nextFn') }, onMultipleDone = jasmine.createSpy('onMultipleDone'), queueRunner = new jasmineUnderTest.QueueRunner({ queueableFns: [queueableFn, nextQueueableFn], onMultipleDone: onMultipleDone }); queueRunner.execute(); jasmine.clock().tick(1); expect(nextQueueableFn.fn.calls.count()).toEqual(1); expect(onMultipleDone).toHaveBeenCalled(); }); it('does not move to the next spec if done is called after an exception has ended the spec', function() { const queueableFn = { fn: function(done) { setTimeout(done, 1); throw new Error('error!'); } }, 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() { // Some promises want handlers to return anything but undefined to help catch "forgotten returns". let doneReturn; const queueableFn = { fn: function(done) { doneReturn = done(); } }; const queueRunner = new jasmineUnderTest.QueueRunner({ queueableFns: [queueableFn] }); queueRunner.execute(); expect(doneReturn).toBe(null); }); 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(); }, timeout: 1 }, nextQueueableFn = { fn: jasmine.createSpy('nextFunction') }, onException = jasmine.createSpy('onException'), globalErrors = { pushListener: jasmine.createSpy('pushListener'), popListener: jasmine.createSpy('popListener') }, queueRunner = new jasmineUnderTest.QueueRunner({ queueableFns: [queueableFn, nextQueueableFn], onException: onException, globalErrors: globalErrors }), throwAsync = function() { globalErrors.pushListener.calls .mostRecent() .args[0](new Error('foo')); jasmine.clock().tick(2); }; nextQueueableFn.fn.and.callFake(function() { // should remove the same function that was added expect(globalErrors.popListener).toHaveBeenCalledWith( globalErrors.pushListener.calls.argsFor(1)[0] ); }); queueRunner.execute(); function errorWithMessage(message) { return { asymmetricMatch: function(other) { return new RegExp(message).test(other.message); }, toString: function() { return ''; } }; } expect(onException).not.toHaveBeenCalledWith( errorWithMessage(/DEFAULT_TIMEOUT_INTERVAL/) ); expect(onException).toHaveBeenCalledWith(errorWithMessage(/^foo$/)); expect(nextQueueableFn.fn).toHaveBeenCalled(); }); it('handles exceptions thrown while waiting for the stack to clear', function() { const queueableFn = { fn: function(done) { done(); } }, errorListeners = [], globalErrors = { pushListener: function(f) { errorListeners.push(f); }, popListener: function() { errorListeners.pop(); } }, clearStack = jasmine.createSpy('clearStack'), onException = jasmine.createSpy('onException'), queueRunner = new jasmineUnderTest.QueueRunner({ queueableFns: [queueableFn], globalErrors: globalErrors, clearStack: clearStack, onException: onException }), error = new Error('nope'); queueRunner.execute(); jasmine.clock().tick(); expect(clearStack).toHaveBeenCalled(); expect(errorListeners.length).toEqual(1); errorListeners[0](error); clearStack.calls.argsFor(0)[0](); expect(onException).toHaveBeenCalledWith(error); }); }); describe('with a function that returns a promise', function() { function StubPromise() {} StubPromise.prototype.then = function(resolve, reject) { this.resolveHandler = resolve; this.rejectHandler = reject; }; beforeEach(function() { jasmine.clock().install(); }); afterEach(function() { jasmine.clock().uninstall(); }); it('runs the function asynchronously, advancing once the promise is settled', function() { const onComplete = jasmine.createSpy('onComplete'), fnCallback = jasmine.createSpy('fnCallback'), p1 = new StubPromise(), p2 = new StubPromise(), queueableFn1 = { fn: function() { setTimeout(function() { p1.resolveHandler(); }, 100); return p1; } }, queueableFn2 = { fn: function() { fnCallback(); setTimeout(function() { p2.resolveHandler(); }, 100); return p2; } }, queueRunner = new jasmineUnderTest.QueueRunner({ queueableFns: [queueableFn1, queueableFn2], onComplete: onComplete }); queueRunner.execute(); expect(fnCallback).not.toHaveBeenCalled(); expect(onComplete).not.toHaveBeenCalled(); jasmine.clock().tick(100); expect(fnCallback).toHaveBeenCalled(); expect(onComplete).not.toHaveBeenCalled(); jasmine.clock().tick(100); expect(onComplete).toHaveBeenCalled(); }); it('handles a rejected promise like an unhandled exception', function() { const promise = new StubPromise(), queueableFn1 = { fn: function() { setTimeout(function() { promise.rejectHandler('foo'); }, 100); return promise; } }, queueableFn2 = { fn: jasmine.createSpy('fn2') }, onExceptionCallback = jasmine.createSpy('on exception callback'), queueRunner = new jasmineUnderTest.QueueRunner({ queueableFns: [queueableFn1, queueableFn2], onException: onExceptionCallback }); queueRunner.execute(); expect(onExceptionCallback).not.toHaveBeenCalled(); expect(queueableFn2.fn).not.toHaveBeenCalled(); jasmine.clock().tick(100); expect(onExceptionCallback).toHaveBeenCalledWith('foo'); expect(queueableFn2.fn).toHaveBeenCalled(); }); 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(); } }, onException = jasmine.createSpy('onException'), queueRunner = new jasmineUnderTest.QueueRunner({ queueableFns: [queueableFn], onException: onException }); queueRunner.execute(); expect(onException).toHaveBeenCalledWith( 'An asynchronous ' + 'before/it/after function took a done callback but also returned a ' + 'promise. ' + 'Either remove the done callback (recommended) or change the function ' + 'to not return a promise.' ); }); 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({ queueableFns: [{ fn: fn }], onException: onException }); queueRunner.execute(); expect(onException).toHaveBeenCalledWith( 'An asynchronous ' + 'before/it/after function was defined with the async keyword but ' + 'also took a done callback. Either remove the done callback ' + '(recommended) or remove the async keyword.' ); }); }); 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({ onException: onExceptionCallback }); queueRunner.execute(); 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', fn: function() { throw new Error('fake error'); } }, onExceptionCallback = jasmine.createSpy('on exception callback'), queueRunner = new jasmineUnderTest.QueueRunner({ queueableFns: [queueableFn], onException: onExceptionCallback }); queueRunner.execute(); expect(onExceptionCallback).toHaveBeenCalledWith(jasmine.any(Error)); }); 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'); } }, nextQueueableFn = { fn: jasmine.createSpy('nextFunction') }, queueRunner = new jasmineUnderTest.QueueRunner({ queueableFns: [queueableFn, nextQueueableFn] }); queueRunner.execute(); expect(nextQueueableFn.fn).toHaveBeenCalled(); }); describe('When configured with a skip policy', function() { it('instantiates the skip policy', function() { const SkipPolicy = jasmine.createSpy('SkipPolicy ctor'); const queueableFns = [{ fn: () => {} }, { fn: () => {} }]; new jasmineUnderTest.QueueRunner({ queueableFns, SkipPolicy }); expect(SkipPolicy).toHaveBeenCalledWith(queueableFns); }); it('uses the skip policy to determine which fn to run next', function() { const queueableFns = [ { fn: jasmine.createSpy('fn0') }, { fn: jasmine.createSpy('fn1') }, { fn: jasmine.createSpy('fn2').and.throwError(new Error('nope')) }, { fn: jasmine.createSpy('fn3') } ]; const skipPolicy = jasmine.createSpyObj('skipPolicy', [ 'skipTo', 'fnErrored' ]); skipPolicy.skipTo.and.callFake(function(lastRanIx) { return lastRanIx === 0 ? 2 : lastRanIx + 1; }); const queueRunner = new jasmineUnderTest.QueueRunner({ queueableFns, SkipPolicy: function() { return skipPolicy; } }); queueRunner.execute(); expect(skipPolicy.skipTo).toHaveBeenCalledWith(0); expect(skipPolicy.skipTo).toHaveBeenCalledWith(2); expect(skipPolicy.fnErrored).toHaveBeenCalledWith(2); expect(queueableFns[0].fn).toHaveBeenCalled(); expect(queueableFns[1].fn).not.toHaveBeenCalled(); expect(queueableFns[2].fn).toHaveBeenCalled(); expect(queueableFns[3].fn).toHaveBeenCalled(); }); it('throws if the skip policy returns the current fn', function() { const skipPolicy = { skipTo: i => i }; const queueableFns = [{ fn: () => {} }]; const queueRunner = new jasmineUnderTest.QueueRunner({ queueableFns, SkipPolicy: function() { return skipPolicy; } }); expect(function() { queueRunner.execute(); }).toThrowError("Can't skip to the same queueable fn that just finished"); }); }); describe('When configured to complete on first error', function() { it('skips to cleanup functions on the first exception', function() { const queueableFn = { fn: function() { throw new Error('error'); } }, nextQueueableFn = { fn: jasmine.createSpy('nextFunction') }, cleanupFn = { fn: jasmine.createSpy('cleanup'), type: 'specCleanup' }, onComplete = jasmine.createSpy('onComplete'), queueRunner = new jasmineUnderTest.QueueRunner({ queueableFns: [queueableFn, nextQueueableFn, cleanupFn], onComplete: onComplete, SkipPolicy: jasmineUnderTest.CompleteOnFirstErrorSkipPolicy }); queueRunner.execute(); expect(nextQueueableFn.fn).not.toHaveBeenCalled(); expect(cleanupFn.fn).toHaveBeenCalled(); expect(onComplete).toHaveBeenCalledWith( jasmine.any(jasmineUnderTest.StopExecutionError) ); }); it('does not skip when a cleanup function throws', function() { const queueableFn = { fn: function() {} }, cleanupFn1 = { fn: function() { throw new Error('error'); }, type: 'afterEach' }, cleanupFn2 = { fn: jasmine.createSpy('cleanupFn2'), type: 'afterEach' }, queueRunner = new jasmineUnderTest.QueueRunner({ queueableFns: [queueableFn, cleanupFn1, cleanupFn2], SkipPolicy: jasmineUnderTest.CompleteOnFirstErrorSkipPolicy }); queueRunner.execute(); expect(cleanupFn2.fn).toHaveBeenCalled(); }); describe('with an asynchronous function', function() { beforeEach(function() { jasmine.clock().install(); }); afterEach(function() { jasmine.clock().uninstall(); }); it('skips to cleanup functions once the fn completes after an unhandled exception', function() { const errorListeners = []; let queueableFnDone; const queueableFn = { fn: function(done) { queueableFnDone = done; } }; const nextQueueableFn = { fn: jasmine.createSpy('nextFunction') }; const cleanupFn = { fn: jasmine.createSpy('cleanup'), type: 'specCleanup' }; const queueRunner = new jasmineUnderTest.QueueRunner({ globalErrors: { pushListener: function(f) { errorListeners.push(f); }, popListener: function() { errorListeners.pop(); } }, queueableFns: [queueableFn, nextQueueableFn, cleanupFn], SkipPolicy: jasmineUnderTest.CompleteOnFirstErrorSkipPolicy }); queueRunner.execute(); errorListeners[errorListeners.length - 1](new Error('error')); expect(cleanupFn.fn).not.toHaveBeenCalled(); queueableFnDone(); expect(nextQueueableFn.fn).not.toHaveBeenCalled(); expect(cleanupFn.fn).toHaveBeenCalled(); }); it('skips to cleanup functions when next.fail is called', function() { const queueableFn = { fn: function(done) { done.fail('nope'); } }, nextQueueableFn = { fn: jasmine.createSpy('nextFunction') }, cleanupFn = { fn: jasmine.createSpy('cleanup'), type: 'specCleanup' }, queueRunner = new jasmineUnderTest.QueueRunner({ queueableFns: [queueableFn, nextQueueableFn, cleanupFn], SkipPolicy: jasmineUnderTest.CompleteOnFirstErrorSkipPolicy }); queueRunner.execute(); jasmine.clock().tick(); expect(nextQueueableFn.fn).not.toHaveBeenCalled(); expect(cleanupFn.fn).toHaveBeenCalled(); }); it('skips to cleanup functions when next is called with an Error', function() { const queueableFn = { fn: function(done) { done(new Error('nope')); } }, nextQueueableFn = { fn: jasmine.createSpy('nextFunction') }, cleanupFn = { fn: jasmine.createSpy('cleanup'), type: 'specCleanup' }, queueRunner = new jasmineUnderTest.QueueRunner({ queueableFns: [queueableFn, nextQueueableFn, cleanupFn], SkipPolicy: jasmineUnderTest.CompleteOnFirstErrorSkipPolicy }); queueRunner.execute(); jasmine.clock().tick(); expect(nextQueueableFn.fn).not.toHaveBeenCalled(); expect(cleanupFn.fn).toHaveBeenCalled(); }); }); }); it('calls a provided complete callback when done', function() { const queueableFn = { fn: jasmine.createSpy('fn') }, completeCallback = jasmine.createSpy('completeCallback'), queueRunner = new jasmineUnderTest.QueueRunner({ queueableFns: [queueableFn], onComplete: completeCallback }); queueRunner.execute(); expect(completeCallback).toHaveBeenCalled(); }); describe('clearing the stack', function() { beforeEach(function() { jasmine.clock().install(); }); afterEach(function() { jasmine.clock().uninstall(); }); it('calls a provided stack clearing function when done', function() { const asyncFn = { fn: function(done) { done(); } }, afterFn = { fn: jasmine.createSpy('afterFn') }, completeCallback = jasmine.createSpy('completeCallback'), clearStack = jasmine.createSpy('clearStack'), queueRunner = new jasmineUnderTest.QueueRunner({ queueableFns: [asyncFn, afterFn], clearStack: clearStack, onComplete: completeCallback }); clearStack.and.callFake(function(fn) { fn(); }); queueRunner.execute(); jasmine.clock().tick(); expect(afterFn.fn).toHaveBeenCalled(); expect(clearStack).toHaveBeenCalled(); clearStack.calls.argsFor(0)[0](); expect(completeCallback).toHaveBeenCalled(); }); }); describe('when user context has not been defined', function() { beforeEach(function() { const fn = jasmine.createSpy('fn1'); this.fn = fn; this.queueRunner = new jasmineUnderTest.QueueRunner({ queueableFns: [{ fn: fn }] }); }); it('runs the functions on the scope of a UserContext', function() { let context; this.fn.and.callFake(function() { context = this; }); this.queueRunner.execute(); expect(context.constructor).toBe(jasmineUnderTest.UserContext); }); }); describe('when user context has been defined', function() { beforeEach(function() { const fn = jasmine.createSpy('fn1'); let context; this.fn = fn; this.context = context = new jasmineUnderTest.UserContext(); this.queueRunner = new jasmineUnderTest.QueueRunner({ queueableFns: [{ fn: fn }], userContext: context }); }); it('runs the functions on the scope of a UserContext', function() { let context; this.fn.and.callFake(function() { context = this; }); this.queueRunner.execute(); expect(context).toBe(this.context); }); }); }); jasmine-4.5.0/spec/core/ReportDispatcherSpec.js000066400000000000000000000126521432731766000215020ustar00rootroot00000000000000describe('ReportDispatcher', function() { it('builds an interface of requested methods', function() { const dispatcher = new jasmineUnderTest.ReportDispatcher([ 'foo', 'bar', 'baz' ]); expect(dispatcher.foo).toBeDefined(); expect(dispatcher.bar).toBeDefined(); expect(dispatcher.baz).toBeDefined(); }); it('dispatches requested methods to added reporters', function() { const queueRunnerFactory = jasmine.createSpy('queueRunner'), dispatcher = new jasmineUnderTest.ReportDispatcher( ['foo', 'bar'], queueRunnerFactory ), reporter = jasmine.createSpyObj('reporter', ['foo', 'bar']), anotherReporter = jasmine.createSpyObj('reporter', ['foo', 'bar']); dispatcher.addReporter(reporter); dispatcher.addReporter(anotherReporter); dispatcher.foo(123, 456); expect(queueRunnerFactory).toHaveBeenCalledWith( jasmine.objectContaining({ queueableFns: [ { fn: jasmine.any(Function) }, { fn: jasmine.any(Function) } ], isReporter: true }) ); let fns = queueRunnerFactory.calls.mostRecent().args[0].queueableFns; fns[0].fn(); expect(reporter.foo).toHaveBeenCalledWith(123, 456); expect(reporter.foo.calls.mostRecent().object).toBe(reporter); fns[1].fn(); expect(anotherReporter.foo).toHaveBeenCalledWith(123, 456); expect(anotherReporter.foo.calls.mostRecent().object).toBe(anotherReporter); queueRunnerFactory.calls.reset(); dispatcher.bar('a', 'b'); expect(queueRunnerFactory).toHaveBeenCalledWith( jasmine.objectContaining({ queueableFns: [ { fn: jasmine.any(Function) }, { fn: jasmine.any(Function) } ], isReporter: true }) ); fns = queueRunnerFactory.calls.mostRecent().args[0].queueableFns; fns[0].fn(); expect(reporter.bar).toHaveBeenCalledWith('a', 'b'); fns[1].fn(); expect(anotherReporter.bar).toHaveBeenCalledWith('a', 'b'); }); it("does not dispatch to a reporter if the reporter doesn't accept the method", function() { const queueRunnerFactory = jasmine.createSpy('queueRunner'), dispatcher = new jasmineUnderTest.ReportDispatcher( ['foo'], queueRunnerFactory ), reporter = jasmine.createSpyObj('reporter', ['baz']); dispatcher.addReporter(reporter); dispatcher.foo(123, 456); expect(queueRunnerFactory).toHaveBeenCalledWith( jasmine.objectContaining({ queueableFns: [] }) ); }); it("allows providing a fallback reporter in case there's no other reporter", function() { const queueRunnerFactory = jasmine.createSpy('queueRunner'), dispatcher = new jasmineUnderTest.ReportDispatcher( ['foo', 'bar'], queueRunnerFactory ), reporter = jasmine.createSpyObj('reporter', ['foo', 'bar']); dispatcher.provideFallbackReporter(reporter); dispatcher.foo(123, 456); expect(queueRunnerFactory).toHaveBeenCalledWith( jasmine.objectContaining({ queueableFns: [{ fn: jasmine.any(Function) }], isReporter: true }) ); const fns = queueRunnerFactory.calls.mostRecent().args[0].queueableFns; fns[0].fn(); expect(reporter.foo).toHaveBeenCalledWith(123, 456); }); it('does not call fallback reporting methods when another reporter is provided', function() { const queueRunnerFactory = jasmine.createSpy('queueRunner'), dispatcher = new jasmineUnderTest.ReportDispatcher( ['foo', 'bar'], queueRunnerFactory ), reporter = jasmine.createSpyObj('reporter', ['foo', 'bar']), fallbackReporter = jasmine.createSpyObj('otherReporter', ['foo', 'bar']); dispatcher.provideFallbackReporter(fallbackReporter); dispatcher.addReporter(reporter); dispatcher.foo(123, 456); expect(queueRunnerFactory).toHaveBeenCalledWith( jasmine.objectContaining({ queueableFns: [{ fn: jasmine.any(Function) }], isReporter: true }) ); const fns = queueRunnerFactory.calls.mostRecent().args[0].queueableFns; fns[0].fn(); expect(reporter.foo).toHaveBeenCalledWith(123, 456); expect(fallbackReporter.foo).not.toHaveBeenCalledWith(123, 456); }); it('allows registered reporters to be cleared', function() { const queueRunnerFactory = jasmine.createSpy('queueRunner'), dispatcher = new jasmineUnderTest.ReportDispatcher( ['foo', 'bar'], queueRunnerFactory ), reporter1 = jasmine.createSpyObj('reporter1', ['foo', 'bar']), reporter2 = jasmine.createSpyObj('reporter2', ['foo', 'bar']); dispatcher.addReporter(reporter1); dispatcher.foo(123); expect(queueRunnerFactory).toHaveBeenCalledWith( jasmine.objectContaining({ queueableFns: [{ fn: jasmine.any(Function) }], isReporter: true }) ); let fns = queueRunnerFactory.calls.mostRecent().args[0].queueableFns; fns[0].fn(); expect(reporter1.foo).toHaveBeenCalledWith(123); dispatcher.clearReporters(); dispatcher.addReporter(reporter2); dispatcher.bar(456); expect(queueRunnerFactory).toHaveBeenCalledWith( jasmine.objectContaining({ queueableFns: [{ fn: jasmine.any(Function) }], isReporter: true }) ); fns = queueRunnerFactory.calls.mostRecent().args[0].queueableFns; fns[0].fn(); expect(reporter1.bar).not.toHaveBeenCalled(); expect(reporter2.bar).toHaveBeenCalledWith(456); }); }); jasmine-4.5.0/spec/core/RunableResourcesSpec.js000066400000000000000000000447271432731766000215130ustar00rootroot00000000000000describe('RunableResources', function() { describe('#spies', function() { behavesLikeAPerRunableMutableArray( 'spies', 'Spies must be created in a before function or a spec', false ); }); describe('#customSpyStrategies', function() { behavesLikeAPerRunableMutableObject( 'customSpyStrategies', 'Custom spy strategies must be added in a before function or a spec' ); }); describe('#customEqualityTesters', function() { behavesLikeAPerRunableMutableArray( 'customEqualityTesters', 'Custom Equalities must be added in a before function or a spec' ); }); describe('#customObjectFormatters', function() { behavesLikeAPerRunableMutableArray( 'customObjectFormatters', 'Custom object formatters must be added in a before function or a spec' ); }); describe('#customMatchers', function() { behavesLikeAPerRunableMutableObject( 'customMatchers', 'Matchers must be added in a before function or a spec' ); }); describe('#addCustomMatchers', function() { it("adds all properties to the current runable's matchers", function() { const currentRunableId = 1; const runableResources = new jasmineUnderTest.RunableResources({ globalErrors: stubGlobalErrors(), getCurrentRunableId: () => currentRunableId }); runableResources.initForRunable(1); function toBeFoo() {} function toBeBar() {} function toBeBaz() {} runableResources.addCustomMatchers({ toBeFoo }); expect(runableResources.customMatchers()).toEqual({ toBeFoo }); runableResources.addCustomMatchers({ toBeBar, toBeBaz }); expect(runableResources.customMatchers()).toEqual({ toBeFoo, toBeBar, toBeBaz }); }); }); describe('#customAsyncMatchers', function() { behavesLikeAPerRunableMutableObject( 'customAsyncMatchers', 'Async Matchers must be added in a before function or a spec' ); }); describe('#addCustomAsyncMatchers', function() { it("adds all properties to the current runable's matchers", function() { const currentRunableId = 1; const runableResources = new jasmineUnderTest.RunableResources({ globalErrors: stubGlobalErrors(), getCurrentRunableId: () => currentRunableId }); runableResources.initForRunable(1); function toBeFoo() {} function toBeBar() {} function toBeBaz() {} runableResources.addCustomAsyncMatchers({ toBeFoo }); expect(runableResources.customAsyncMatchers()).toEqual({ toBeFoo }); runableResources.addCustomAsyncMatchers({ toBeBar, toBeBaz }); expect(runableResources.customAsyncMatchers()).toEqual({ toBeFoo, toBeBar, toBeBaz }); }); }); describe('#defaultSpyStrategy', function() { it('returns undefined for a newly initialized resource', function() { let currentRunableId = 1; const runableResources = new jasmineUnderTest.RunableResources({ globalErrors: stubGlobalErrors(), getCurrentRunableId: () => currentRunableId }); runableResources.initForRunable(1); expect(runableResources.defaultSpyStrategy()).toBeUndefined(); }); it('returns the value previously set by #setDefaultSpyStrategy', function() { let currentRunableId = 1; const runableResources = new jasmineUnderTest.RunableResources({ globalErrors: stubGlobalErrors(), getCurrentRunableId: () => currentRunableId }); runableResources.initForRunable(1); const fn = () => {}; runableResources.setDefaultSpyStrategy(fn); expect(runableResources.defaultSpyStrategy()).toBe(fn); }); it('is per-runable', function() { let currentRunableId = 1; const runableResources = new jasmineUnderTest.RunableResources({ globalErrors: stubGlobalErrors(), getCurrentRunableId: () => currentRunableId }); runableResources.initForRunable(1); runableResources.setDefaultSpyStrategy(() => {}); currentRunableId = 2; runableResources.initForRunable(2); expect(runableResources.defaultSpyStrategy()).toBeUndefined(); }); it('does not require a current runable', function() { const runableResources = new jasmineUnderTest.RunableResources({ globalErrors: stubGlobalErrors(), getCurrentRunableId: () => null }); expect(runableResources.defaultSpyStrategy()).toBeUndefined(); }); it("inherits the parent runable's value", function() { let currentRunableId = 1; const runableResources = new jasmineUnderTest.RunableResources({ globalErrors: stubGlobalErrors(), getCurrentRunableId: () => currentRunableId }); runableResources.initForRunable(1); const fn = () => {}; runableResources.setDefaultSpyStrategy(fn); currentRunableId = 2; runableResources.initForRunable(2, 1); expect(runableResources.defaultSpyStrategy()).toBe(fn); }); }); describe('#setDefaultSpyStrategy', function() { it('throws a user-facing error when there is no current runable', function() { const runableResources = new jasmineUnderTest.RunableResources({ globalErrors: stubGlobalErrors(), getCurrentRunableId: () => null }); expect(function() { runableResources.setDefaultSpyStrategy(); }).toThrowError( 'Default spy strategy must be set in a before function or a spec' ); }); }); describe('#makePrettyPrinter', function() { it('returns a pretty printer configured with the current customObjectFormatters', function() { const runableResources = new jasmineUnderTest.RunableResources({ globalErrors: stubGlobalErrors(), getCurrentRunableId: () => 1 }); runableResources.initForRunable(1); function cof() {} runableResources.customObjectFormatters().push(cof); spyOn(jasmineUnderTest, 'makePrettyPrinter').and.callThrough(); const pp = runableResources.makePrettyPrinter(); expect(jasmineUnderTest.makePrettyPrinter).toHaveBeenCalledOnceWith([ cof ]); expect(pp).toBe( jasmineUnderTest.makePrettyPrinter.calls.first().returnValue ); }); }); describe('#makeMatchersUtil', function() { describe('When there is a current runable', function() { it('returns a MatchersUtil configured with the current resources', function() { const runableResources = new jasmineUnderTest.RunableResources({ globalErrors: stubGlobalErrors(), getCurrentRunableId: () => 1 }); runableResources.initForRunable(1); function cof() {} runableResources.customObjectFormatters().push(cof); function ceq() {} runableResources.customEqualityTesters().push(ceq); const expectedPP = {}; const expectedMatchersUtil = {}; spyOn(jasmineUnderTest, 'makePrettyPrinter').and.returnValue( expectedPP ); spyOn(jasmineUnderTest, 'MatchersUtil').and.returnValue( expectedMatchersUtil ); const matchersUtil = runableResources.makeMatchersUtil(); expect(matchersUtil).toBe(expectedMatchersUtil); expect(jasmineUnderTest.makePrettyPrinter).toHaveBeenCalledOnceWith([ cof ]); // We need === equality on the pp passed to MatchersUtil expect(jasmineUnderTest.MatchersUtil).toHaveBeenCalledOnceWith( jasmine.objectContaining({ customTesters: [ceq] }) ); expect(jasmineUnderTest.MatchersUtil.calls.argsFor(0)[0].pp).toBe( expectedPP ); }); }); describe('When there is no current runable', function() { it('returns a MatchersUtil configured with defaults', function() { const runableResources = new jasmineUnderTest.RunableResources({ globalErrors: stubGlobalErrors(), getCurrentRunableId: () => null }); const expectedMatchersUtil = {}; spyOn(jasmineUnderTest, 'MatchersUtil').and.returnValue( expectedMatchersUtil ); const matchersUtil = runableResources.makeMatchersUtil(); expect(matchersUtil).toBe(expectedMatchersUtil); // We need === equality on the pp passed to MatchersUtil expect(jasmineUnderTest.MatchersUtil).toHaveBeenCalledTimes(1); expect(jasmineUnderTest.MatchersUtil.calls.argsFor(0)[0].pp).toBe( jasmineUnderTest.basicPrettyPrinter_ ); expect( jasmineUnderTest.MatchersUtil.calls.argsFor(0)[0].customTesters ).toBeUndefined(); }); }); }); describe('.spyFactory', function() { describe('When there is no current runable', function() { it('is configured with default strategies and matchersUtil', function() { const runableResources = new jasmineUnderTest.RunableResources({ globalErrors: stubGlobalErrors(), getCurrentRunableId: () => null }); spyOn(jasmineUnderTest, 'Spy'); const matchersUtil = {}; spyOn(runableResources, 'makeMatchersUtil').and.returnValue( matchersUtil ); runableResources.spyFactory.createSpy('foo'); expect(jasmineUnderTest.Spy).toHaveBeenCalledWith( 'foo', is(matchersUtil), jasmine.objectContaining({ customStrategies: {}, defaultStrategyFn: undefined }) ); }); }); describe('When there is a current runable', function() { it("is configured with the current runable's strategies and matchersUtil", function() { const runableResources = new jasmineUnderTest.RunableResources({ globalErrors: stubGlobalErrors(), getCurrentRunableId: () => 1 }); runableResources.initForRunable(1); function customStrategy() {} function defaultStrategy() {} runableResources.customSpyStrategies().foo = customStrategy; runableResources.setDefaultSpyStrategy(defaultStrategy); spyOn(jasmineUnderTest, 'Spy'); const matchersUtil = {}; spyOn(runableResources, 'makeMatchersUtil').and.returnValue( matchersUtil ); runableResources.spyFactory.createSpy('foo'); expect(jasmineUnderTest.Spy).toHaveBeenCalledWith( 'foo', is(matchersUtil), jasmine.objectContaining({ customStrategies: { foo: customStrategy }, defaultStrategyFn: defaultStrategy }) ); }); }); function is(expected) { return { asymmetricMatch: function(actual) { return actual === expected; }, jasmineToString: function(pp) { return ''; } }; } }); describe('.spyRegistry', function() { it("writes to the current runable's spies", function() { const runableResources = new jasmineUnderTest.RunableResources({ globalErrors: stubGlobalErrors(), getCurrentRunableId: () => 1 }); runableResources.initForRunable(1); function foo() {} const spyObj = { foo }; runableResources.spyRegistry.spyOn(spyObj, 'foo'); expect(runableResources.spies()).toEqual([ jasmine.objectContaining({ restoreObjectToOriginalState: jasmine.any(Function) }) ]); expect(jasmineUnderTest.isSpy(spyObj.foo)).toBeTrue(); runableResources.spyRegistry.clearSpies(); expect(spyObj.foo).toBe(foo); }); }); describe('#clearForRunable', function() { it('removes resources for the specified runable', function() { const runableResources = new jasmineUnderTest.RunableResources({ globalErrors: stubGlobalErrors(), getCurrentRunableId: () => 1 }); runableResources.initForRunable(1); expect(function() { runableResources.spies(); }).not.toThrow(); runableResources.clearForRunable(1); expect(function() { runableResources.spies(); }).toThrowError('Spies must be created in a before function or a spec'); }); it('clears spies', function() { const runableResources = new jasmineUnderTest.RunableResources({ globalErrors: stubGlobalErrors(), getCurrentRunableId: () => 1 }); runableResources.initForRunable(1); function foo() {} const spyObj = { foo }; runableResources.spyRegistry.spyOn(spyObj, 'foo'); expect(spyObj.foo).not.toBe(foo); runableResources.clearForRunable(1); expect(spyObj.foo).toBe(foo); }); it('clears the global error spy', function() { const globalErrors = jasmine.createSpyObj('globalErrors', [ 'removeOverrideListener' ]); const runableResources = new jasmineUnderTest.RunableResources({ getCurrentRunableId: () => 1, globalErrors }); runableResources.initForRunable(1); runableResources.clearForRunable(1); expect(globalErrors.removeOverrideListener).toHaveBeenCalled(); }); it('does not remove resources for other runables', function() { const runableResources = new jasmineUnderTest.RunableResources({ globalErrors: stubGlobalErrors(), getCurrentRunableId: () => 1 }); runableResources.initForRunable(1); function cof() {} runableResources.customObjectFormatters().push(cof); runableResources.clearForRunable(2); expect(runableResources.customObjectFormatters()).toEqual([cof]); }); }); function behavesLikeAPerRunableMutableArray( methodName, errorMsg, inherits = true ) { it('is initially empty', function() { const currentRunableId = 1; const runableResources = new jasmineUnderTest.RunableResources({ globalErrors: stubGlobalErrors(), getCurrentRunableId: () => currentRunableId }); runableResources.initForRunable(1); expect(runableResources[methodName]()).toEqual([]); }); it('is mutable', function() { const currentRunableId = 1; const runableResources = new jasmineUnderTest.RunableResources({ globalErrors: stubGlobalErrors(), getCurrentRunableId: () => currentRunableId }); runableResources.initForRunable(1); function newItem() {} runableResources[methodName]().push(newItem); expect(runableResources[methodName]()).toEqual([newItem]); }); it('is per-runable', function() { let currentRunableId = 1; const runableResources = new jasmineUnderTest.RunableResources({ globalErrors: stubGlobalErrors(), getCurrentRunableId: () => currentRunableId }); runableResources.initForRunable(1); runableResources[methodName]().push(() => {}); runableResources.initForRunable(2); currentRunableId = 2; expect(runableResources[methodName]()).toEqual([]); }); it('throws a user-facing error when there is no current runable', function() { const runableResources = new jasmineUnderTest.RunableResources({ globalErrors: stubGlobalErrors(), getCurrentRunableId: () => null }); expect(function() { runableResources[methodName](); }).toThrowError(errorMsg); }); if (inherits) { it('inherits from the parent runable', function() { let currentRunableId = 1; const runableResources = new jasmineUnderTest.RunableResources({ globalErrors: stubGlobalErrors(), getCurrentRunableId: () => currentRunableId }); runableResources.initForRunable(1); function parentItem() {} runableResources[methodName]().push(parentItem); runableResources.initForRunable(2, 1); currentRunableId = 2; function childItem() {} runableResources[methodName]().push(childItem); expect(runableResources[methodName]()).toEqual([parentItem, childItem]); currentRunableId = 1; expect(runableResources[methodName]()).toEqual([parentItem]); }); } } function behavesLikeAPerRunableMutableObject(methodName, errorMsg) { it('is initially empty', function() { const currentRunableId = 1; const runableResources = new jasmineUnderTest.RunableResources({ globalErrors: stubGlobalErrors(), getCurrentRunableId: () => currentRunableId }); runableResources.initForRunable(1); expect(runableResources[methodName]()).toEqual({}); }); it('is mutable', function() { const currentRunableId = 1; const runableResources = new jasmineUnderTest.RunableResources({ globalErrors: stubGlobalErrors(), getCurrentRunableId: () => currentRunableId }); runableResources.initForRunable(1); function newItem() {} runableResources[methodName]().foo = newItem; expect(runableResources[methodName]()).toEqual({ foo: newItem }); }); it('is per-runable', function() { let currentRunableId = 1; const runableResources = new jasmineUnderTest.RunableResources({ globalErrors: stubGlobalErrors(), getCurrentRunableId: () => currentRunableId }); runableResources.initForRunable(1); runableResources[methodName]().foo = function() {}; runableResources.initForRunable(2); currentRunableId = 2; expect(runableResources[methodName]()).toEqual({}); }); it('throws a user-facing error when there is no current runable', function() { const runableResources = new jasmineUnderTest.RunableResources({ globalErrors: stubGlobalErrors(), getCurrentRunableId: () => null }); expect(function() { runableResources[methodName](); }).toThrowError(errorMsg); }); it('inherits from the parent runable', function() { let currentRunableId = 1; const runableResources = new jasmineUnderTest.RunableResources({ globalErrors: stubGlobalErrors(), getCurrentRunableId: () => currentRunableId }); runableResources.initForRunable(1); function parentItem() {} runableResources[methodName]().parentName = parentItem; runableResources.initForRunable(2, 1); currentRunableId = 2; function childItem() {} runableResources[methodName]().childName = childItem; expect(runableResources[methodName]()).toEqual({ parentName: parentItem, childName: childItem }); currentRunableId = 1; expect(runableResources[methodName]()).toEqual({ parentName: parentItem }); }); } function stubGlobalErrors() { return { removeOverrideListener() {} }; } }); jasmine-4.5.0/spec/core/SkipAfterBeforeAllErrorPolicySpec.js000066400000000000000000000051251432731766000240530ustar00rootroot00000000000000describe('SkipAfterBeforeAllErrorPolicy', function() { describe('#skipTo', function() { describe('When nothing has errored', function() { it('does not skip anything', function() { const policy = new jasmineUnderTest.SkipAfterBeforeAllErrorPolicy( arrayOfArbitraryFns(4) ); expect(policy.skipTo(0)).toEqual(1); expect(policy.skipTo(1)).toEqual(2); expect(policy.skipTo(2)).toEqual(3); expect(policy.skipTo(3)).toEqual(4); }); }); describe('When anything but a beforeAll has errored', function() { it('does not skip anything', function() { const policy = new jasmineUnderTest.SkipAfterBeforeAllErrorPolicy( arrayOfArbitraryFns(4) ); policy.fnErrored(0); expect(policy.skipTo(0)).toEqual(1); policy.fnErrored(1); expect(policy.skipTo(1)).toEqual(2); policy.fnErrored(2); expect(policy.skipTo(2)).toEqual(3); policy.fnErrored(3); expect(policy.skipTo(3)).toEqual(4); }); }); describe('When a beforeAll has errored', function() { it('skips subsequent functions other than afterAll', function() { const suite = {}; const fns = [ { type: 'beforeAll', fn: () => {}, suite }, { fn: () => {} }, { fn: () => {} }, { type: 'afterAll', fn: () => {} }, { type: 'afterAll', fn: () => {} } ]; const policy = new jasmineUnderTest.SkipAfterBeforeAllErrorPolicy(fns); policy.fnErrored(0); expect(policy.skipTo(0)).toEqual(3); expect(policy.skipTo(3)).toEqual(4); }); }); }); describe('#fnErrored', function() { describe('When the fn is a beforeAll', function() { it("sets the suite's hadBeforeAllFailure property to true", function() { const suite = {}; const fns = [{ type: 'beforeAll', fn: () => {}, suite }]; const policy = new jasmineUnderTest.SkipAfterBeforeAllErrorPolicy(fns); policy.fnErrored(0); expect(suite.hadBeforeAllFailure).toBeTrue(); }); }); describe('When the fn is not a beforeAll', function() { it('does not try to access the suite, which is probably not there', function() { const fns = [{ fn: () => {} /* no suite */ }]; const policy = new jasmineUnderTest.SkipAfterBeforeAllErrorPolicy(fns); expect(() => policy.fnErrored(0)).not.toThrow(); }); }); }); }); function arrayOfArbitraryFns(n) { const result = []; for (let i = 0; i < n; i++) { result.push({ fn: () => {} }); } return result; } jasmine-4.5.0/spec/core/SpecSpec.js000066400000000000000000000537401432731766000171150ustar00rootroot00000000000000describe('Spec', function() { it('#isPendingSpecException returns true for a pending spec exception', function() { const e = new Error(jasmineUnderTest.Spec.pendingSpecExceptionMessage); expect(jasmineUnderTest.Spec.isPendingSpecException(e)).toBe(true); }); it('#isPendingSpecException returns true for a pending spec exception (even when FF bug is present)', function() { const fakeError = { toString: function() { return 'Error: ' + jasmineUnderTest.Spec.pendingSpecExceptionMessage; } }; expect(jasmineUnderTest.Spec.isPendingSpecException(fakeError)).toBe(true); }); it('#isPendingSpecException returns true for a pending spec exception with a custom message', function() { expect( jasmineUnderTest.Spec.isPendingSpecException( jasmineUnderTest.Spec.pendingSpecExceptionMessage + 'foo' ) ).toBe(true); }); it('#isPendingSpecException returns false for not a pending spec exception', function() { const e = new Error('foo'); expect(jasmineUnderTest.Spec.isPendingSpecException(e)).toBe(false); }); it("#isPendingSpecException returns false for thrown values that don't have toString", function() { expect(jasmineUnderTest.Spec.isPendingSpecException(void 0)).toBe(false); }); it('delegates execution to a QueueRunner', function() { const fakeQueueRunner = jasmine.createSpy('fakeQueueRunner'), spec = new jasmineUnderTest.Spec({ description: 'my test', id: 'some-id', queueableFn: { fn: function() {} } }); spec.execute(fakeQueueRunner); expect(fakeQueueRunner).toHaveBeenCalled(); }); it('should call the start callback on execution', function() { const fakeQueueRunner = jasmine.createSpy('fakeQueueRunner'), startCallback = jasmine.createSpy('startCallback'), spec = new jasmineUnderTest.Spec({ id: 123, description: 'foo bar', queueableFn: { fn: function() {} }, onStart: startCallback }); spec.execute(fakeQueueRunner); fakeQueueRunner.calls.mostRecent().args[0].queueableFns[0].fn(); expect(startCallback).toHaveBeenCalled(); expect(startCallback.calls.first().object).toEqual(spec); }); it('should call the start callback on execution but before any befores are called', function() { const fakeQueueRunner = jasmine.createSpy('fakeQueueRunner'); let beforesWereCalled = false; const startCallback = jasmine .createSpy('start-callback') .and.callFake(function() { expect(beforesWereCalled).toBe(false); }); const spec = new jasmineUnderTest.Spec({ queueableFn: { fn: function() {} }, beforeFns: function() { return [ function() { beforesWereCalled = true; } ]; }, onStart: startCallback }); spec.execute(fakeQueueRunner); fakeQueueRunner.calls.mostRecent().args[0].queueableFns[0].fn(); expect(startCallback).toHaveBeenCalled(); }); it('provides all before fns and after fns to be run', function() { const fakeQueueRunner = jasmine.createSpy('fakeQueueRunner'), before = jasmine.createSpy('before'), after = jasmine.createSpy('after'), queueableFn = { fn: jasmine.createSpy('test body').and.callFake(function() { expect(before).toHaveBeenCalled(); expect(after).not.toHaveBeenCalled(); }) }, spec = new jasmineUnderTest.Spec({ queueableFn: queueableFn, beforeAndAfterFns: function() { return { befores: [before], afters: [after] }; } }); spec.execute(fakeQueueRunner); const options = fakeQueueRunner.calls.mostRecent().args[0]; expect(options.queueableFns).toEqual([ { fn: jasmine.any(Function) }, before, queueableFn, after, { fn: jasmine.any(Function), type: 'specCleanup' } ]); }); it("tells the queue runner that it's a leaf node", function() { const fakeQueueRunner = jasmine.createSpy('fakeQueueRunner'), spec = new jasmineUnderTest.Spec({ queueableFn: { fn: function() {} }, beforeAndAfterFns: function() { return { befores: [], afters: [] }; } }); spec.execute(fakeQueueRunner); expect(fakeQueueRunner).toHaveBeenCalledWith( jasmine.objectContaining({ isLeaf: true }) ); }); it('is marked pending if created without a function body', function() { const startCallback = jasmine.createSpy('startCallback'), resultCallback = jasmine.createSpy('resultCallback'), spec = new jasmineUnderTest.Spec({ onStart: startCallback, queueableFn: { fn: null }, resultCallback: resultCallback }); expect(spec.status()).toBe('pending'); }); it('can be excluded at execution time by a parent', function() { const fakeQueueRunner = jasmine.createSpy('fakeQueueRunner'), startCallback = jasmine.createSpy('startCallback'), specBody = jasmine.createSpy('specBody'), resultCallback = jasmine.createSpy('resultCallback'), spec = new jasmineUnderTest.Spec({ onStart: startCallback, queueableFn: { fn: specBody }, resultCallback: resultCallback }); spec.execute(fakeQueueRunner, 'cally-back', true); expect(fakeQueueRunner).toHaveBeenCalledWith( jasmine.objectContaining({ onComplete: jasmine.any(Function), queueableFns: [ { fn: jasmine.any(Function) }, { fn: jasmine.any(Function), type: 'specCleanup' } ] }) ); expect(specBody).not.toHaveBeenCalled(); const args = fakeQueueRunner.calls.mostRecent().args[0]; args.queueableFns[0].fn(); expect(startCallback).toHaveBeenCalled(); args.queueableFns[args.queueableFns.length - 1].fn(); expect(resultCallback).toHaveBeenCalled(); expect(spec.result.status).toBe('excluded'); }); it('can be marked pending, but still calls callbacks when executed', function() { const fakeQueueRunner = jasmine.createSpy('fakeQueueRunner'), startCallback = jasmine.createSpy('startCallback'), resultCallback = jasmine.createSpy('resultCallback'), spec = new jasmineUnderTest.Spec({ onStart: startCallback, resultCallback: resultCallback, description: 'with a spec', getSpecName: function() { return 'a suite with a spec'; }, queueableFn: { fn: null } }); spec.pend(); expect(spec.status()).toBe('pending'); spec.execute(fakeQueueRunner); expect(fakeQueueRunner).toHaveBeenCalled(); const args = fakeQueueRunner.calls.mostRecent().args[0]; args.queueableFns[0].fn(); expect(startCallback).toHaveBeenCalled(); args.queueableFns[1].fn('things'); expect(resultCallback).toHaveBeenCalledWith( { id: spec.id, status: 'pending', description: 'with a spec', fullName: 'a suite with a spec', failedExpectations: [], passedExpectations: [], deprecationWarnings: [], pendingReason: '', duration: jasmine.any(Number), properties: null, debugLogs: null }, 'things' ); }); it('should call the done callback on execution complete', function() { const done = jasmine.createSpy('done callback'), spec = new jasmineUnderTest.Spec({ queueableFn: { fn: function() {} }, catchExceptions: function() { return false; }, resultCallback: function() {} }); spec.execute(attrs => attrs.onComplete(), done); expect(done).toHaveBeenCalled(); }); it('should call the done callback with an error if the spec is failed', function() { const done = jasmine.createSpy('done callback'), spec = new jasmineUnderTest.Spec({ queueableFn: { fn: function() {} }, catchExceptions: function() { return false; }, resultCallback: function() {} }); function queueRunnerFactory(attrs) { spec.result.status = 'failed'; attrs.onComplete(); } spec.execute(queueRunnerFactory, done); expect(done).toHaveBeenCalledWith( jasmine.any(jasmineUnderTest.StopExecutionError) ); }); it('should report the duration of the test', function() { const timer = jasmine.createSpyObj('timer', { start: null, elapsed: 77000 }); let duration = undefined; const spec = new jasmineUnderTest.Spec({ queueableFn: { fn: jasmine.createSpy('spec body') }, catchExceptions: function() { return false; }, resultCallback: function(result) { duration = result.duration; }, timer: timer }); function queueRunnerFactory(config) { config.queueableFns.forEach(function(qf) { qf.fn(); }); config.onComplete(); } spec.execute(queueRunnerFactory, function() {}); expect(duration).toBe(77000); }); it('should report properties set during the test', function() { const done = jasmine.createSpy('done callback'), spec = new jasmineUnderTest.Spec({ queueableFn: { fn: jasmine.createSpy('spec body') }, catchExceptions: function() { return false; }, resultCallback: function() {} }); spec.setSpecProperty('a', 4); spec.execute(attrs => attrs.onComplete(), done); expect(spec.result.properties).toEqual({ a: 4 }); }); it('#status returns passing by default', function() { const spec = new jasmineUnderTest.Spec({ queueableFn: { fn: jasmine.createSpy('spec body') } }); expect(spec.status()).toBe('passed'); }); it('#status returns passed if all expectations in the spec have passed', function() { const spec = new jasmineUnderTest.Spec({ queueableFn: { fn: jasmine.createSpy('spec body') } }); spec.addExpectationResult(true, {}); expect(spec.status()).toBe('passed'); }); it('#status returns failed if any expectations in the spec have failed', function() { const spec = new jasmineUnderTest.Spec({ queueableFn: { fn: jasmine.createSpy('spec body') } }); spec.addExpectationResult(true, {}); spec.addExpectationResult(false, {}); expect(spec.status()).toBe('failed'); }); it('keeps track of passed and failed expectations', function() { const fakeQueueRunner = jasmine.createSpy('queueRunner'), resultCallback = jasmine.createSpy('resultCallback'), spec = new jasmineUnderTest.Spec({ queueableFn: { fn: jasmine.createSpy('spec body') }, resultCallback: resultCallback }); spec.addExpectationResult(true, { message: 'expectation1' }); spec.addExpectationResult(false, { message: 'expectation2' }); spec.execute(fakeQueueRunner); const fns = fakeQueueRunner.calls.mostRecent().args[0].queueableFns; fns[fns.length - 1].fn(); expect(resultCallback.calls.first().args[0].passedExpectations).toEqual([ jasmine.objectContaining({ message: 'expectation1' }) ]); expect(resultCallback.calls.first().args[0].failedExpectations).toEqual([ jasmine.objectContaining({ message: 'expectation2' }) ]); }); it("throws an ExpectationFailed error upon receiving a failed expectation when 'throwOnExpectationFailure' is set", function() { const fakeQueueRunner = jasmine.createSpy('queueRunner'), resultCallback = jasmine.createSpy('resultCallback'), spec = new jasmineUnderTest.Spec({ queueableFn: { fn: function() {} }, resultCallback: resultCallback, throwOnExpectationFailure: true }); spec.addExpectationResult(true, { message: 'passed' }); expect(function() { spec.addExpectationResult(false, { message: 'failed' }); }).toThrowError(jasmineUnderTest.errors.ExpectationFailed); spec.execute(fakeQueueRunner); const fns = fakeQueueRunner.calls.mostRecent().args[0].queueableFns; fns[fns.length - 1].fn(); expect(resultCallback.calls.first().args[0].passedExpectations).toEqual([ jasmine.objectContaining({ message: 'passed' }) ]); expect(resultCallback.calls.first().args[0].failedExpectations).toEqual([ jasmine.objectContaining({ message: 'failed' }) ]); }); it('forwards late expectation failures to onLateError', function() { const onLateError = jasmine.createSpy('onLateError'); const spec = new jasmineUnderTest.Spec({ onLateError, queueableFn: { fn: function() {} } }); const data = { matcherName: '', passed: false, expected: '', actual: '', error: new Error('nope') }; spec.reportedDone = true; spec.addExpectationResult(false, data, true); expect(onLateError).toHaveBeenCalledWith( jasmine.objectContaining({ message: jasmine.stringMatching(/^Error: nope/) }) ); expect(spec.result.failedExpectations).toEqual([]); }); it('does not forward non-late expectation failures to onLateError', function() { const onLateError = jasmine.createSpy('onLateError'); const spec = new jasmineUnderTest.Spec({ onLateError, queueableFn: { fn: function() {} } }); const data = { matcherName: '', passed: false, expected: '', actual: '', error: new Error('nope') }; spec.addExpectationResult(false, data, true); expect(onLateError).not.toHaveBeenCalled(); }); it('forwards late handleException calls to onLateError', function() { const onLateError = jasmine.createSpy('onLateError'); const spec = new jasmineUnderTest.Spec({ onLateError, queueableFn: { fn: function() {} } }); spec.reportedDone = true; spec.handleException(new Error('oops')); expect(onLateError).toHaveBeenCalledWith( jasmine.objectContaining({ message: jasmine.stringMatching(/^Error: oops/) }) ); expect(spec.result.failedExpectations).toEqual([]); }); it('does not forward non-late handleException calls to onLateError', function() { const onLateError = jasmine.createSpy('onLateError'); const spec = new jasmineUnderTest.Spec({ onLateError, queueableFn: { fn: function() {} } }); const error = new Error('oops'); spec.handleException(error); expect(onLateError).not.toHaveBeenCalled(); expect(spec.result.failedExpectations.length).toEqual(1); }); it('clears the reportedDone flag when reset', function() { const spec = new jasmineUnderTest.Spec({ queueableFn: { fn: function() {} } }); spec.reportedDone = true; spec.reset(); expect(spec.reportedDone).toBeFalse(); }); it('does not throw an ExpectationFailed error when handling an error', function() { const resultCallback = jasmine.createSpy('resultCallback'), spec = new jasmineUnderTest.Spec({ queueableFn: { fn: function() {} }, resultCallback: resultCallback, throwOnExpectationFailure: true }); spec.handleException('failing exception'); }); it('can return its full name', function() { const specNameSpy = jasmine .createSpy('specNameSpy') .and.returnValue('expected val'); const spec = new jasmineUnderTest.Spec({ getSpecName: specNameSpy, queueableFn: { fn: null } }); expect(spec.getFullName()).toBe('expected val'); expect(specNameSpy.calls.mostRecent().args[0].id).toEqual(spec.id); }); describe('when a spec is marked pending during execution', function() { it('should mark the spec as pending', function() { const fakeQueueRunner = function(opts) { opts.onException( new Error(jasmineUnderTest.Spec.pendingSpecExceptionMessage) ); }, spec = new jasmineUnderTest.Spec({ description: 'my test', id: 'some-id', queueableFn: { fn: function() {} } }); spec.execute(fakeQueueRunner); expect(spec.status()).toEqual('pending'); expect(spec.result.pendingReason).toEqual(''); }); it('should set the pendingReason', function() { const fakeQueueRunner = function(opts) { opts.onException( new Error( jasmineUnderTest.Spec.pendingSpecExceptionMessage + 'custom message' ) ); }, spec = new jasmineUnderTest.Spec({ description: 'my test', id: 'some-id', queueableFn: { fn: function() {} } }); spec.execute(fakeQueueRunner); expect(spec.status()).toEqual('pending'); expect(spec.result.pendingReason).toEqual('custom message'); }); }); it('should log a failure when handling an exception', function() { const fakeQueueRunner = jasmine.createSpy('queueRunner'), resultCallback = jasmine.createSpy('resultCallback'), spec = new jasmineUnderTest.Spec({ queueableFn: { fn: function() {} }, resultCallback: resultCallback }); spec.handleException('foo'); spec.execute(fakeQueueRunner); const args = fakeQueueRunner.calls.mostRecent().args[0]; args.queueableFns[args.queueableFns.length - 1].fn(); expect(resultCallback.calls.first().args[0].failedExpectations).toEqual([ { message: 'foo thrown', matcherName: '', passed: false, expected: '', actual: '', stack: null } ]); }); it('should not log an additional failure when handling an ExpectationFailed error', function() { const fakeQueueRunner = jasmine.createSpy('queueRunner'), resultCallback = jasmine.createSpy('resultCallback'), spec = new jasmineUnderTest.Spec({ queueableFn: { fn: function() {} }, resultCallback: resultCallback }); spec.handleException(new jasmineUnderTest.errors.ExpectationFailed()); spec.execute(fakeQueueRunner); const args = fakeQueueRunner.calls.mostRecent().args[0]; args.queueableFns[args.queueableFns.length - 1].fn(); expect(resultCallback.calls.first().args[0].failedExpectations).toEqual([]); }); it('treats multiple done calls as late errors', function() { const queueRunnerFactory = jasmine.createSpy('queueRunnerFactory'), onLateError = jasmine.createSpy('onLateError'), spec = new jasmineUnderTest.Spec({ onLateError: onLateError, queueableFn: { fn: function() {} }, getSpecName: function() { return 'a spec'; } }); spec.execute(queueRunnerFactory); expect(queueRunnerFactory).toHaveBeenCalled(); queueRunnerFactory.calls.argsFor(0)[0].onMultipleDone(); expect(onLateError).toHaveBeenCalledTimes(1); expect(onLateError.calls.argsFor(0)[0]).toBeInstanceOf(Error); expect(onLateError.calls.argsFor(0)[0].message).toEqual( 'An asynchronous spec, beforeEach, or afterEach function called its ' + "'done' callback more than once.\n(in spec: a spec)" ); }); describe('#trace', function() { it('adds the messages to the result', function() { const timer = jasmine.createSpyObj('timer', ['start', 'elapsed']), spec = new jasmineUnderTest.Spec({ queueableFn: { fn: function() {} }, timer: timer }), t1 = 123, t2 = 456; spec.execute(() => {}); expect(spec.result.debugLogs).toBeNull(); timer.elapsed.and.returnValue(t1); spec.debugLog('msg 1'); expect(spec.result.debugLogs).toEqual([ { message: 'msg 1', timestamp: t1 } ]); timer.elapsed.and.returnValue(t2); spec.debugLog('msg 2'); expect(spec.result.debugLogs).toEqual([ { message: 'msg 1', timestamp: t1 }, { message: 'msg 2', timestamp: t2 } ]); }); describe('When the spec passes', function() { it('omits the messages from the reported result', function() { const resultCallback = jasmine.createSpy('resultCallback'), spec = new jasmineUnderTest.Spec({ queueableFn: { fn: function() {} }, resultCallback: resultCallback }); function queueRunnerFactory(config) { spec.debugLog('msg'); for (const fn of config.queueableFns) { fn.fn(); } config.onComplete(false); } spec.execute(queueRunnerFactory, function() {}); expect(resultCallback).toHaveBeenCalledWith( jasmine.objectContaining({ debugLogs: null }), undefined ); }); it('removes the messages to save memory', function() { const resultCallback = jasmine.createSpy('resultCallback'), spec = new jasmineUnderTest.Spec({ queueableFn: { fn: function() {} }, resultCallback: resultCallback }); function queueRunnerFactory(config) { spec.debugLog('msg'); for (const fn of config.queueableFns) { fn.fn(); } config.onComplete(false); } spec.execute(queueRunnerFactory, function() {}); expect(resultCallback).toHaveBeenCalled(); expect(spec.result.debugLogs).toBeNull(); }); }); describe('When the spec fails', function() { it('includes the messages in the reported result', function() { const resultCallback = jasmine.createSpy('resultCallback'), timer = jasmine.createSpyObj('timer', ['start', 'elapsed']), spec = new jasmineUnderTest.Spec({ queueableFn: { fn: function() {} }, resultCallback: resultCallback, timer: timer }), timestamp = 12345; timer.elapsed.and.returnValue(timestamp); function queueRunnerFactory(config) { spec.debugLog('msg'); spec.handleException(new Error('nope')); for (const fn of config.queueableFns) { fn.fn(); } config.onComplete(true); } spec.execute(queueRunnerFactory, function() {}); expect(resultCallback).toHaveBeenCalledWith( jasmine.objectContaining({ debugLogs: [{ message: 'msg', timestamp: timestamp }] }), undefined ); }); }); }); }); jasmine-4.5.0/spec/core/SpyRegistrySpec.js000066400000000000000000000500221432731766000205150ustar00rootroot00000000000000describe('SpyRegistry', function() { function createSpy(name, originalFn) { return jasmineUnderTest.Spy(name, originalFn); } describe('#spyOn', function() { it('checks for the existence of the object', function() { const spyRegistry = new jasmineUnderTest.SpyRegistry({ createSpy: createSpy }); expect(function() { spyRegistry.spyOn(void 0, 'pants'); }).toThrowError(/could not find an object/); }); it('checks that a method name was passed', function() { const spyRegistry = new jasmineUnderTest.SpyRegistry(), subject = {}; expect(function() { spyRegistry.spyOn(subject); }).toThrowError(/No method name supplied/); }); it('checks that the object is not `null`', function() { const spyRegistry = new jasmineUnderTest.SpyRegistry(); expect(function() { spyRegistry.spyOn(null, 'pants'); }).toThrowError(/could not find an object/); }); it('checks that the method name is not `null`', function() { const spyRegistry = new jasmineUnderTest.SpyRegistry(), subject = {}; expect(function() { spyRegistry.spyOn(subject, null); }).toThrowError(/No method name supplied/); }); it('checks for the existence of the method', function() { const spyRegistry = new jasmineUnderTest.SpyRegistry(), subject = {}; expect(function() { spyRegistry.spyOn(subject, 'pants'); }).toThrowError(/method does not exist/); }); it('checks if it has already been spied upon', function() { const spies = [], spyRegistry = new jasmineUnderTest.SpyRegistry({ currentSpies: function() { return spies; }, createSpy: createSpy }), subject = { spiedFunc: function() {} }; spyRegistry.spyOn(subject, 'spiedFunc'); expect(function() { spyRegistry.spyOn(subject, 'spiedFunc'); }).toThrowError(/has already been spied upon/); }); it('checks if it can be spied upon', function() { const scope = {}; function myFunc() { return 1; } Object.defineProperty(scope, 'myFunc', { get: function() { return myFunc; } }); const spies = [], spyRegistry = new jasmineUnderTest.SpyRegistry({ currentSpies: function() { return spies; } }), subject = { spiedFunc: scope.myFunc }; expect(function() { spyRegistry.spyOn(scope, 'myFunc'); }).toThrowError(/is not declared writable or has no setter/); expect(function() { spyRegistry.spyOn(subject, 'spiedFunc'); }).not.toThrowError(/is not declared writable or has no setter/); }); it('overrides the method on the object and returns the spy', function() { const originalFunctionWasCalled = false, spyRegistry = new jasmineUnderTest.SpyRegistry({ createSpy: createSpy }), subject = { spiedFunc: function() { originalFunctionWasCalled = true; } }; const spy = spyRegistry.spyOn(subject, 'spiedFunc'); expect(subject.spiedFunc).toEqual(spy); subject.spiedFunc(); expect(originalFunctionWasCalled).toBe(false); }); }); describe('#spyOnProperty', function() { it('checks for the existence of the object', function() { const spyRegistry = new jasmineUnderTest.SpyRegistry(); expect(function() { spyRegistry.spyOnProperty(void 0, 'pants'); }).toThrowError(/could not find an object/); }); it('checks that a property name was passed', function() { const spyRegistry = new jasmineUnderTest.SpyRegistry(), subject = {}; expect(function() { spyRegistry.spyOnProperty(subject); }).toThrowError(/No property name supplied/); }); it('checks for the existence of the method', function() { const spyRegistry = new jasmineUnderTest.SpyRegistry(), subject = {}; expect(function() { spyRegistry.spyOnProperty(subject, 'pants'); }).toThrowError(/property does not exist/); }); it('checks for the existence of access type', function() { const spyRegistry = new jasmineUnderTest.SpyRegistry(), subject = {}; Object.defineProperty(subject, 'pants', { get: function() { return 1; }, configurable: true }); expect(function() { spyRegistry.spyOnProperty(subject, 'pants', 'set'); }).toThrowError(/does not have access type/); }); it('checks if it can be spied upon', function() { const subject = {}; Object.defineProperty(subject, 'myProp', { get: function() {} }); Object.defineProperty(subject, 'spiedProp', { get: function() {}, configurable: true }); const spyRegistry = new jasmineUnderTest.SpyRegistry(); expect(function() { spyRegistry.spyOnProperty(subject, 'myProp'); }).toThrowError(/is not declared configurable/); expect(function() { spyRegistry.spyOnProperty(subject, 'spiedProp'); }).not.toThrowError(/is not declared configurable/); }); it('overrides the property getter on the object and returns the spy', function() { const spyRegistry = new jasmineUnderTest.SpyRegistry({ createSpy: createSpy }), subject = {}, returnValue = 1; Object.defineProperty(subject, 'spiedProperty', { get: function() { return returnValue; }, configurable: true }); expect(subject.spiedProperty).toEqual(returnValue); const spy = spyRegistry.spyOnProperty(subject, 'spiedProperty'); const getter = Object.getOwnPropertyDescriptor(subject, 'spiedProperty') .get; expect(getter).toEqual(spy); expect(subject.spiedProperty).toBeUndefined(); }); it('overrides the property setter on the object and returns the spy', function() { const spyRegistry = new jasmineUnderTest.SpyRegistry({ createSpy: createSpy }), subject = {}, returnValue = 1; Object.defineProperty(subject, 'spiedProperty', { get: function() { return returnValue; }, set: function() {}, configurable: true }); const spy = spyRegistry.spyOnProperty(subject, 'spiedProperty', 'set'); const setter = Object.getOwnPropertyDescriptor(subject, 'spiedProperty') .set; expect(subject.spiedProperty).toEqual(returnValue); expect(setter).toEqual(spy); }); describe('when the property is already spied upon', function() { it('throws an error if respy is not allowed', function() { const spyRegistry = new jasmineUnderTest.SpyRegistry({ createSpy: createSpy }), subject = {}; Object.defineProperty(subject, 'spiedProp', { get: function() { return 1; }, configurable: true }); spyRegistry.spyOnProperty(subject, 'spiedProp'); expect(function() { spyRegistry.spyOnProperty(subject, 'spiedProp'); }).toThrowError(/spiedProp#get has already been spied upon/); }); it('returns the original spy if respy is allowed', function() { const spyRegistry = new jasmineUnderTest.SpyRegistry({ createSpy: createSpy }), subject = {}; spyRegistry.allowRespy(true); Object.defineProperty(subject, 'spiedProp', { get: function() { return 1; }, configurable: true }); const originalSpy = spyRegistry.spyOnProperty(subject, 'spiedProp'); expect(spyRegistry.spyOnProperty(subject, 'spiedProp')).toBe( originalSpy ); }); }); }); describe('#spyOnAllFunctions', function() { it('checks for the existence of the object', function() { const spyRegistry = new jasmineUnderTest.SpyRegistry(); expect(function() { spyRegistry.spyOnAllFunctions(void 0); }).toThrowError(/spyOnAllFunctions could not find an object to spy upon/); }); it('overrides all writable and configurable functions of the object and its parents', function() { const spyRegistry = new jasmineUnderTest.SpyRegistry({ createSpy: function() { return 'I am a spy'; } }); const createNoop = function() { return function() { /**/ }; }; const noop1 = createNoop(); const noop2 = createNoop(); const noop3 = createNoop(); const noop4 = createNoop(); const noop5 = createNoop(); const parent = { parentSpied1: noop1 }; const subject = Object.create(parent); Object.defineProperty(subject, 'spied1', { value: noop1, writable: true, configurable: true, enumerable: true }); Object.defineProperty(subject, 'spied2', { value: noop2, writable: true, configurable: true, enumerable: true }); let _spied3 = noop3; Object.defineProperty(subject, 'spied3', { configurable: true, set: function(val) { _spied3 = val; }, get: function() { return _spied3; }, enumerable: true }); subject.spied4 = noop4; Object.defineProperty(subject, 'notSpied2', { value: noop2, writable: false, configurable: true, enumerable: true }); Object.defineProperty(subject, 'notSpied3', { value: noop3, writable: true, configurable: false, enumerable: true }); Object.defineProperty(subject, 'notSpied4', { configurable: false, set: function() { /**/ }, get: function() { return noop4; }, enumerable: true }); Object.defineProperty(subject, 'notSpied5', { value: noop5, writable: true, configurable: true, enumerable: false }); subject.notSpied6 = 6; const spiedObject = spyRegistry.spyOnAllFunctions(subject); expect(subject.parentSpied1).toBe('I am a spy'); expect(subject.notSpied2).toBe(noop2); expect(subject.notSpied3).toBe(noop3); expect(subject.notSpied4).toBe(noop4); expect(subject.notSpied5).toBe(noop5); expect(subject.notSpied6).toBe(6); expect(subject.spied1).toBe('I am a spy'); expect(subject.spied2).toBe('I am a spy'); expect(subject.spied3).toBe('I am a spy'); expect(subject.spied4).toBe('I am a spy'); expect(spiedObject).toBe(subject); }); it('overrides prototype methods on the object', function() { const spyRegistry = new jasmineUnderTest.SpyRegistry({ createSpy: function() { return 'I am a spy'; } }); const noop1 = function() {}; const noop2 = function() {}; const MyClass = function() { this.spied1 = noop1; }; MyClass.prototype.spied2 = noop2; const subject = new MyClass(); spyRegistry.spyOnAllFunctions(subject); expect(subject.spied1).toBe('I am a spy'); expect(subject.spied2).toBe('I am a spy'); expect(MyClass.prototype.spied2).toBe(noop2); }); it('does not override non-enumerable properties (like Object.prototype methods)', function() { const spyRegistry = new jasmineUnderTest.SpyRegistry({ createSpy: function() { return 'I am a spy'; } }); const subject = { spied1: function() {} }; spyRegistry.spyOnAllFunctions(subject); expect(subject.spied1).toBe('I am a spy'); expect(subject.toString).not.toBe('I am a spy'); expect(subject.hasOwnProperty).not.toBe('I am a spy'); }); describe('when includeNonEnumerable is true', function() { it('does not override Object.prototype methods', function() { const spyRegistry = new jasmineUnderTest.SpyRegistry({ createSpy: function() { return 'I am a spy'; } }); const subject = { spied1: function() {} }; spyRegistry.spyOnAllFunctions(subject, true); expect(subject.spied1).toBe('I am a spy'); expect(subject.toString).not.toBe('I am a spy'); expect(subject.hasOwnProperty).not.toBe('I am a spy'); }); it('overrides non-enumerable properties', function() { const spyRegistry = new jasmineUnderTest.SpyRegistry({ createSpy: function() { return 'I am a spy'; } }); const subject = { spied1: function() {}, spied2: function() {} }; Object.defineProperty(subject, 'spied2', { enumerable: false, writable: true, configurable: true }); spyRegistry.spyOnAllFunctions(subject, true); expect(subject.spied1).toBe('I am a spy'); expect(subject.spied2).toBe('I am a spy'); }); it('should not spy on non-enumerable functions named constructor', function() { const spyRegistry = new jasmineUnderTest.SpyRegistry({ createSpy: function() { return 'I am a spy'; } }); const subject = { constructor: function() {} }; Object.defineProperty(subject, 'constructor', { enumerable: false, writable: true, configurable: true }); spyRegistry.spyOnAllFunctions(subject, true); expect(subject.constructor).not.toBe('I am a spy'); }); it('should spy on enumerable functions named constructor', function() { const spyRegistry = new jasmineUnderTest.SpyRegistry({ createSpy: function() { return 'I am a spy'; } }); const subject = { constructor: function() {} }; spyRegistry.spyOnAllFunctions(subject, true); expect(subject.constructor).toBe('I am a spy'); }); it('should not throw an exception if we try and access strict mode restricted properties', function() { const spyRegistry = new jasmineUnderTest.SpyRegistry({ createSpy: function() { return 'I am a spy'; } }); const subject = function() {}; const fn = function() { spyRegistry.spyOnAllFunctions(subject, true); }; expect(fn).not.toThrow(); }); it('should not spy on properties which are more permissable further up the prototype chain', function() { const spyRegistry = new jasmineUnderTest.SpyRegistry({ createSpy: function() { return 'I am a spy'; } }); const subjectParent = Object.defineProperty({}, 'sharedProp', { value: function() {}, writable: true, configurable: true }); const subject = Object.create(subjectParent); Object.defineProperty(subject, 'sharedProp', { value: function() {} }); const fn = function() { spyRegistry.spyOnAllFunctions(subject, true); }; expect(fn).not.toThrow(); expect(subject).not.toBe('I am a spy'); }); }); }); describe('#clearSpies', function() { it('restores the original functions on the spied-upon objects', function() { const spies = [], spyRegistry = new jasmineUnderTest.SpyRegistry({ currentSpies: function() { return spies; }, createSpy: createSpy }), originalFunction = function() {}, subject = { spiedFunc: originalFunction }; spyRegistry.spyOn(subject, 'spiedFunc'); spyRegistry.clearSpies(); expect(subject.spiedFunc).toBe(originalFunction); }); it('restores the original functions, even when that spy has been replace and re-spied upon', function() { const spies = [], spyRegistry = new jasmineUnderTest.SpyRegistry({ currentSpies: function() { return spies; }, createSpy: createSpy }), originalFunction = function() {}, subject = { spiedFunc: originalFunction }; spyRegistry.spyOn(subject, 'spiedFunc'); // replace the original spy with some other function subject.spiedFunc = function() {}; // spy on the function in that location again spyRegistry.spyOn(subject, 'spiedFunc'); spyRegistry.clearSpies(); expect(subject.spiedFunc).toBe(originalFunction); }); it("does not add a property that the spied-upon object didn't originally have", function() { const spies = [], spyRegistry = new jasmineUnderTest.SpyRegistry({ currentSpies: function() { return spies; }, createSpy: createSpy }), originalFunction = function() {}, subjectParent = { spiedFunc: originalFunction }; const subject = Object.create(subjectParent); expect(subject.hasOwnProperty('spiedFunc')).toBe(false); spyRegistry.spyOn(subject, 'spiedFunc'); spyRegistry.clearSpies(); expect(subject.hasOwnProperty('spiedFunc')).toBe(false); expect(subject.spiedFunc).toBe(originalFunction); }); it("restores the original function when it's inherited and cannot be deleted", function() { const spies = [], spyRegistry = new jasmineUnderTest.SpyRegistry({ currentSpies: function() { return spies; }, createSpy: createSpy }), originalFunction = function() {}, subjectParent = { spiedFunc: originalFunction }; const subject = Object.create(subjectParent); spyRegistry.spyOn(subject, 'spiedFunc'); // simulate a spy that cannot be deleted Object.defineProperty(subject, 'spiedFunc', { configurable: false }); spyRegistry.clearSpies(); expect(jasmineUnderTest.isSpy(subject.spiedFunc)).toBe(false); }); it('restores window.onerror by overwriting, not deleting', function() { function FakeWindow() {} FakeWindow.prototype.onerror = function() {}; const spies = [], global = new FakeWindow(), spyRegistry = new jasmineUnderTest.SpyRegistry({ currentSpies: function() { return spies; }, createSpy: createSpy, global: global }); spyRegistry.spyOn(global, 'onerror'); spyRegistry.clearSpies(); expect(global.onerror).toBe(FakeWindow.prototype.onerror); expect(global.hasOwnProperty('onerror')).toBe(true); }); }); describe('spying on properties', function() { it('restores the original properties on the spied-upon objects', function() { const spies = [], spyRegistry = new jasmineUnderTest.SpyRegistry({ currentSpies: function() { return spies; }, createSpy: createSpy }), originalReturn = 1, subject = {}; Object.defineProperty(subject, 'spiedProp', { get: function() { return originalReturn; }, configurable: true }); spyRegistry.spyOnProperty(subject, 'spiedProp'); spyRegistry.clearSpies(); expect(subject.spiedProp).toBe(originalReturn); }); it("does not add a property that the spied-upon object didn't originally have", function() { const spies = [], spyRegistry = new jasmineUnderTest.SpyRegistry({ currentSpies: function() { return spies; }, createSpy: createSpy }), originalReturn = 1, subjectParent = {}; Object.defineProperty(subjectParent, 'spiedProp', { get: function() { return originalReturn; }, configurable: true }); const subject = Object.create(subjectParent); expect(subject.hasOwnProperty('spiedProp')).toBe(false); spyRegistry.spyOnProperty(subject, 'spiedProp'); spyRegistry.clearSpies(); expect(subject.hasOwnProperty('spiedProp')).toBe(false); expect(subject.spiedProp).toBe(originalReturn); }); }); }); jasmine-4.5.0/spec/core/SpySpec.js000066400000000000000000000233151432731766000167710ustar00rootroot00000000000000describe('Spies', function() { let env; beforeEach(function() { env = new jasmineUnderTest.Env(); }); afterEach(function() { env.cleanup_(); }); describe('createSpy', function() { let TestClass; beforeEach(function() { TestClass = function() {}; TestClass.prototype.someFunction = function() {}; TestClass.prototype.someFunction.bob = 'test'; }); it('preserves the properties of the spied function', function() { const spy = env.createSpy( TestClass.prototype, TestClass.prototype.someFunction ); expect(spy.bob).toEqual('test'); }); it('should allow you to omit the name argument and only pass the originalFn argument', function() { const fn = function test() {}; const spy = env.createSpy(fn); expect(spy.and.identity).toEqual('test'); }); it('warns the user that we intend to overwrite an existing property', function() { TestClass.prototype.someFunction.and = 'turkey'; expect(function() { env.createSpy(TestClass.prototype, TestClass.prototype.someFunction); }).toThrowError( "Jasmine spies would overwrite the 'and' and 'calls' properties on the object being spied upon" ); }); it('adds a spyStrategy and callTracker to the spy', function() { const spy = env.createSpy( TestClass.prototype, TestClass.prototype.someFunction ); expect(spy.and).toEqual(jasmine.any(jasmineUnderTest.SpyStrategy)); expect(spy.calls).toEqual(jasmine.any(jasmineUnderTest.CallTracker)); }); it('tracks the argument of calls', function() { const spy = env.createSpy( TestClass.prototype, TestClass.prototype.someFunction ); const trackSpy = spyOn(spy.calls, 'track'); spy('arg'); expect(trackSpy.calls.mostRecent().args[0].args).toEqual(['arg']); }); it('tracks the context of calls', function() { const spy = env.createSpy( TestClass.prototype, TestClass.prototype.someFunction ); const trackSpy = spyOn(spy.calls, 'track'); const contextObject = { spyMethod: spy }; contextObject.spyMethod(); expect(trackSpy.calls.mostRecent().args[0].object).toEqual(contextObject); }); it('tracks the return value of calls', function() { const spy = env.createSpy( TestClass.prototype, TestClass.prototype.someFunction ); const trackSpy = spyOn(spy.calls, 'track'); spy.and.returnValue('return value'); spy(); expect(trackSpy.calls.mostRecent().args[0].returnValue).toEqual( 'return value' ); }); 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) {} ]; for (let arity = 0; arity < functions.length; arity++) { const someFunction = functions[arity], spy = env.createSpy(someFunction.name, someFunction); expect(spy.length).toEqual(arity); } }); }); describe('createSpyObj', function() { it('should create an object with spy methods and corresponding return values when you call jasmine.createSpyObj() with an object', function() { const spyObj = env.createSpyObj('BaseName', { method1: 42, method2: 'special sauce' }); expect(spyObj.method1()).toEqual(42); expect(spyObj.method1.and.identity).toEqual('BaseName.method1'); expect(spyObj.method2()).toEqual('special sauce'); expect(spyObj.method2.and.identity).toEqual('BaseName.method2'); }); it('should create an object with a bunch of spy methods when you call jasmine.createSpyObj()', function() { const spyObj = env.createSpyObj('BaseName', ['method1', 'method2']); expect(spyObj).toEqual({ method1: jasmine.any(Function), method2: jasmine.any(Function) }); expect(spyObj.method1.and.identity).toEqual('BaseName.method1'); expect(spyObj.method2.and.identity).toEqual('BaseName.method2'); }); it('should allow you to omit the baseName', function() { const spyObj = env.createSpyObj(['method1', 'method2']); expect(spyObj).toEqual({ method1: jasmine.any(Function), method2: jasmine.any(Function) }); expect(spyObj.method1.and.identity).toEqual('unknown.method1'); expect(spyObj.method2.and.identity).toEqual('unknown.method2'); }); it('should throw if you do not pass an array or object argument', function() { expect(function() { env.createSpyObj('BaseName'); }).toThrow( 'createSpyObj requires a non-empty array or object of method names to create spies for' ); }); it('should throw if you pass an empty array argument', function() { expect(function() { env.createSpyObj('BaseName', []); }).toThrow( 'createSpyObj requires a non-empty array or object of method names to create spies for' ); }); it('should throw if you pass an empty object argument', function() { expect(function() { env.createSpyObj('BaseName', {}); }).toThrow( 'createSpyObj requires a non-empty array or object of method names to create spies for' ); }); it('creates an object with spy properties if a second list is passed', function() { const spyObj = env.createSpyObj('base', ['method1'], ['prop1']); expect(spyObj).toEqual({ method1: jasmine.any(Function), prop1: undefined }); const descriptor = Object.getOwnPropertyDescriptor(spyObj, 'prop1'); expect(descriptor.get.and.identity).toEqual('base.prop1.get'); expect(descriptor.set.and.identity).toEqual('base.prop1.set'); }); it('creates an object with property names and return values if second object is passed', function() { const spyObj = env.createSpyObj('base', ['method1'], { prop1: 'foo', prop2: 37 }); expect(spyObj).toEqual({ method1: jasmine.any(Function), prop1: 'foo', prop2: 37 }); expect(spyObj.prop1).toEqual('foo'); expect(spyObj.prop2).toEqual(37); spyObj.prop2 = 4; expect(spyObj.prop2).toEqual(37); expect( Object.getOwnPropertyDescriptor(spyObj, 'prop2').set.calls.count() ).toBe(1); }); it('allows base name to be omitted when assigning methods and properties', function() { const spyObj = env.createSpyObj({ m: 3 }, { p: 4 }); expect(spyObj.m()).toEqual(3); expect(spyObj.p).toEqual(4); expect( Object.getOwnPropertyDescriptor(spyObj, 'p').get.and.identity ).toEqual('unknown.p.get'); }); }); it('can use different strategies for different arguments', function() { const spy = env.createSpy('foo'); spy.and.returnValue(42); spy.withArgs('baz', 'grault').and.returnValue(-1); spy.withArgs('thud').and.returnValue('bob'); expect(spy('foo')).toEqual(42); expect(spy('baz', 'grault')).toEqual(-1); expect(spy('thud')).toEqual('bob'); expect(spy('baz', 'grault', 'waldo')).toEqual(42); }); it('uses asymmetric equality testers when selecting a strategy', function() { const spy = env.createSpy('foo'); spy.and.returnValue(42); spy.withArgs(jasmineUnderTest.any(String)).and.returnValue(-1); expect(spy('foo')).toEqual(-1); expect(spy({})).toEqual(42); }); it('uses the provided matchersUtil selecting a strategy', function() { const matchersUtil = new jasmineUnderTest.MatchersUtil({ customTesters: [ function(a, b) { if ((a === 'bar' && b === 'baz') || (a === 'baz' && b === 'bar')) { return true; } } ] }); const spy = new jasmineUnderTest.Spy('aSpy', matchersUtil); spy.and.returnValue('default strategy return value'); spy.withArgs('bar').and.returnValue('custom strategy return value'); expect(spy('foo')).toEqual('default strategy return value'); expect(spy('baz')).toEqual('custom strategy return value'); }); it('can reconfigure an argument-specific strategy', function() { const spy = env.createSpy('foo'); spy.withArgs('foo').and.returnValue(42); spy.withArgs('foo').and.returnValue(17); expect(spy('foo')).toEqual(17); }); describe('any promise-based strategy', function() { it('works with global Promise library', function(done) { const spy = env.createSpy('foo').and.resolveTo(42); spy() .then(function(result) { expect(result).toEqual(42); done(); }) .catch(done.fail); }); }); describe('when withArgs is used without a base strategy', function() { it('uses the matching strategy', function() { const spy = env.createSpy('foo'); spy.withArgs('baz').and.returnValue(-1); expect(spy('baz')).toEqual(-1); }); it("throws if the args don't match", function() { const spy = env.createSpy('foo'); spy.withArgs('bar').and.returnValue(-1); expect(function() { spy('baz', { qux: 42 }); }).toThrowError( "Spy 'foo' received a call with arguments [ 'baz', Object({ qux: 42 }) ] but all configured strategies specify other arguments." ); }); }); }); jasmine-4.5.0/spec/core/SpyStrategySpec.js000066400000000000000000000236721432731766000205220ustar00rootroot00000000000000describe('SpyStrategy', function() { it('defaults its name to unknown', function() { const spyStrategy = new jasmineUnderTest.SpyStrategy(); expect(spyStrategy.identity).toEqual('unknown'); }); it('takes a name', function() { const spyStrategy = new jasmineUnderTest.SpyStrategy({ name: 'foo' }); expect(spyStrategy.identity).toEqual('foo'); }); it('stubs an original function, if provided', function() { const originalFn = jasmine.createSpy('original'), spyStrategy = new jasmineUnderTest.SpyStrategy({ fn: originalFn }); spyStrategy.exec(); expect(originalFn).not.toHaveBeenCalled(); }); it("allows an original function to be called, passed through the params and returns it's value", function() { const originalFn = jasmine.createSpy('original').and.returnValue(42), spyStrategy = new jasmineUnderTest.SpyStrategy({ fn: originalFn }); spyStrategy.callThrough(); const returnValue = spyStrategy.exec(null, ['foo']); expect(originalFn).toHaveBeenCalled(); expect(originalFn.calls.mostRecent().args).toEqual(['foo']); expect(returnValue).toEqual(42); }); it('can return a specified value when executed', function() { const originalFn = jasmine.createSpy('original'), spyStrategy = new jasmineUnderTest.SpyStrategy({ fn: originalFn }); spyStrategy.returnValue(17); const returnValue = spyStrategy.exec(); expect(originalFn).not.toHaveBeenCalled(); expect(returnValue).toEqual(17); }); it('can return specified values in order specified when executed', function() { const originalFn = jasmine.createSpy('original'), spyStrategy = new jasmineUnderTest.SpyStrategy({ fn: originalFn }); spyStrategy.returnValues('value1', 'value2', 'value3'); expect(spyStrategy.exec()).toEqual('value1'); expect(spyStrategy.exec()).toEqual('value2'); expect(spyStrategy.exec()).toBe('value3'); expect(spyStrategy.exec()).toBeUndefined(); expect(originalFn).not.toHaveBeenCalled(); }); it('allows an exception to be thrown when executed', function() { const originalFn = jasmine.createSpy('original'), spyStrategy = new jasmineUnderTest.SpyStrategy({ fn: originalFn }); spyStrategy.throwError(new TypeError('bar')); expect(function() { spyStrategy.exec(); }).toThrowError(TypeError, 'bar'); expect(originalFn).not.toHaveBeenCalled(); }); it('allows a string to be thrown, wrapping it into an exception when executed', function() { const originalFn = jasmine.createSpy('original'), spyStrategy = new jasmineUnderTest.SpyStrategy({ fn: originalFn }); spyStrategy.throwError('bar'); expect(function() { spyStrategy.exec(); }).toThrowError(Error, 'bar'); expect(originalFn).not.toHaveBeenCalled(); }); it('allows a non-Error to be thrown when executed', function() { const originalFn = jasmine.createSpy('original'), spyStrategy = new jasmineUnderTest.SpyStrategy({ fn: originalFn }); spyStrategy.throwError({ code: 'ESRCH' }); expect(function() { spyStrategy.exec(); }).toThrow(jasmine.objectContaining({ code: 'ESRCH' })); expect(originalFn).not.toHaveBeenCalled(); }); it('allows a fake function to be called instead', function() { const originalFn = jasmine.createSpy('original'), fakeFn = jasmine.createSpy('fake').and.returnValue(67), spyStrategy = new jasmineUnderTest.SpyStrategy({ fn: originalFn }); spyStrategy.callFake(fakeFn); const returnValue = spyStrategy.exec(); expect(originalFn).not.toHaveBeenCalled(); expect(returnValue).toEqual(67); }); it('allows a fake async function to be called instead', function(done) { const originalFn = jasmine.createSpy('original'), fakeFn = jasmine.createSpy('fake').and.callFake(async () => { return 67; }), spyStrategy = new jasmineUnderTest.SpyStrategy({ fn: originalFn }); spyStrategy.callFake(fakeFn); spyStrategy .exec() .then(function(returnValue) { expect(originalFn).not.toHaveBeenCalled(); expect(fakeFn).toHaveBeenCalled(); expect(returnValue).toEqual(67); done(); }) .catch(function(err) { done.fail(err); }); }); describe('#resolveTo', function() { it('allows a resolved promise to be returned', function(done) { const originalFn = jasmine.createSpy('original'), spyStrategy = new jasmineUnderTest.SpyStrategy({ fn: originalFn }); spyStrategy.resolveTo(37); spyStrategy .exec() .then(function(returnValue) { expect(returnValue).toEqual(37); done(); }) .catch(done.fail); }); it('allows an empty resolved promise to be returned', function(done) { const originalFn = jasmine.createSpy('original'), spyStrategy = new jasmineUnderTest.SpyStrategy({ fn: originalFn }); spyStrategy.resolveTo(); spyStrategy .exec() .then(function(returnValue) { expect(returnValue).toBe(); done(); }) .catch(done.fail); }); }); describe('#rejectWith', function() { it('allows a rejected promise to be returned', function(done) { const originalFn = jasmine.createSpy('original'), spyStrategy = new jasmineUnderTest.SpyStrategy({ fn: originalFn }); spyStrategy.rejectWith(new Error('oops')); spyStrategy .exec() .then(done.fail) .catch(function(error) { expect(error).toEqual(new Error('oops')); done(); }) .catch(done.fail); }); it('allows an empty rejected promise to be returned', function(done) { const originalFn = jasmine.createSpy('original'), spyStrategy = new jasmineUnderTest.SpyStrategy({ fn: originalFn }); spyStrategy.rejectWith(); spyStrategy .exec() .then(done.fail) .catch(function(error) { expect(error).toBe(); done(); }) .catch(done.fail); }); it('allows a non-Error to be rejected', function(done) { const originalFn = jasmine.createSpy('original'), spyStrategy = new jasmineUnderTest.SpyStrategy({ fn: originalFn }); spyStrategy.rejectWith('oops'); spyStrategy .exec() .then(done.fail) .catch(function(error) { expect(error).toEqual('oops'); done(); }) .catch(done.fail); }); }); it('allows a custom strategy to be used', function() { const plan = jasmine .createSpy('custom strategy') .and.returnValue('custom strategy result'), customStrategy = jasmine .createSpy('custom strategy') .and.returnValue(plan), originalFn = jasmine.createSpy('original'), spyStrategy = new jasmineUnderTest.SpyStrategy({ fn: originalFn, customStrategies: { doSomething: customStrategy } }); spyStrategy.doSomething(1, 2, 3); expect(customStrategy).toHaveBeenCalledWith(1, 2, 3); expect(spyStrategy.exec(null, ['some', 'args'])).toEqual( 'custom strategy result' ); expect(plan).toHaveBeenCalledWith('some', 'args'); }); it("throws an error if a custom strategy doesn't return a function", function() { const originalFn = jasmine.createSpy('original'), spyStrategy = new jasmineUnderTest.SpyStrategy({ fn: originalFn, customStrategies: { doSomething: function() { return 'not a function'; } } }); expect(function() { spyStrategy.doSomething(1, 2, 3); }).toThrowError('Spy strategy must return a function'); }); it('does not allow custom strategies to overwrite existing methods', function() { const spyStrategy = new jasmineUnderTest.SpyStrategy({ fn: function() {}, customStrategies: { exec: function() {} } }); expect(spyStrategy.exec).toBe(jasmineUnderTest.SpyStrategy.prototype.exec); }); it('throws an error when a non-function is passed to callFake strategy', function() { const originalFn = jasmine.createSpy('original'), spyStrategy = new jasmineUnderTest.SpyStrategy({ fn: originalFn }); spyOn(jasmineUnderTest, 'isFunction_').and.returnValue(false); spyOn(jasmineUnderTest, 'isAsyncFunction_').and.returnValue(false); expect(function() { spyStrategy.callFake(function() {}); }).toThrowError(/^Argument passed to callFake should be a function, got/); expect(function() { spyStrategy.callFake(function() {}); }).toThrowError(/^Argument passed to callFake should be a function, got/); }); it('allows generator functions to be passed to callFake strategy', function() { const generator = function*() { yield 'ok'; }, spyStrategy = new jasmineUnderTest.SpyStrategy({ fn: function() {} }); spyStrategy.callFake(generator); expect(spyStrategy.exec().next().value).toEqual('ok'); }); it('allows a return to plan stubbing after another strategy', function() { const originalFn = jasmine.createSpy('original'), fakeFn = jasmine.createSpy('fake').and.returnValue(67), spyStrategy = new jasmineUnderTest.SpyStrategy({ fn: originalFn }); spyStrategy.callFake(fakeFn); let returnValue = spyStrategy.exec(); expect(originalFn).not.toHaveBeenCalled(); expect(returnValue).toEqual(67); spyStrategy.stub(); returnValue = spyStrategy.exec(); expect(returnValue).toEqual(void 0); }); it('returns the spy after changing the strategy', function() { const spy = {}, spyFn = jasmine.createSpy('spyFn').and.returnValue(spy), spyStrategy = new jasmineUnderTest.SpyStrategy({ getSpy: spyFn }); expect(spyStrategy.callThrough()).toBe(spy); expect(spyStrategy.returnValue()).toBe(spy); expect(spyStrategy.throwError()).toBe(spy); expect(spyStrategy.callFake(function() {})).toBe(spy); expect(spyStrategy.stub()).toBe(spy); }); }); jasmine-4.5.0/spec/core/StackTraceSpec.js000066400000000000000000000225221432731766000202410ustar00rootroot00000000000000describe('StackTrace', function() { it('understands Chrome/Edge style traces', function() { const error = { message: 'nope', stack: 'Error: nope\n' + ' at UserContext. (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: nope'); expect(result.style).toEqual('v8'); expect(result.frames).toEqual([ { raw: ' at UserContext. (http://localhost:8888/__spec__/core/UtilSpec.js:115:19)', func: 'UserContext.', 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('understands Chrome/Edge style traces with multiline messages', function() { const error = { message: 'line 1\nline 2', stack: 'Error: line 1\nline 2\n' + ' at UserContext. (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\nline 2'); const rawFrames = result.frames.map(function(f) { return f.raw; }); expect(rawFrames).toEqual([ ' at UserContext. (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', stack: 'Error\n' + ' at /somewhere/jasmine/lib/jasmine-core/jasmine.js:4255:9\n' + ' at QueueRunner.complete [as onComplete] (/somewhere/jasmine/lib/jasmine-core/jasmine.js:579:9)\n' + ' at Immediate. (/somewhere/jasmine/lib/jasmine-core/jasmine.js:4314:12)\n' + ' at runCallback (timers.js:672:20)' }; const result = new jasmineUnderTest.StackTrace(error); expect(result.message).toEqual('Error'); expect(result.style).toEqual('v8'); expect(result.frames).toEqual([ { raw: ' at /somewhere/jasmine/lib/jasmine-core/jasmine.js:4255:9', func: undefined, file: '/somewhere/jasmine/lib/jasmine-core/jasmine.js', line: 4255 }, { raw: ' at QueueRunner.complete [as onComplete] (/somewhere/jasmine/lib/jasmine-core/jasmine.js:579:9)', func: 'QueueRunner.complete [as onComplete]', file: '/somewhere/jasmine/lib/jasmine-core/jasmine.js', line: 579 }, { raw: ' at Immediate. (/somewhere/jasmine/lib/jasmine-core/jasmine.js:4314:12)', func: 'Immediate.', file: '/somewhere/jasmine/lib/jasmine-core/jasmine.js', line: 4314 }, { raw: ' at runCallback (timers.js:672:20)', func: 'runCallback', file: 'timers.js', line: 672 } ]); }); it('understands Safari <=14/Firefox/Phantom-OS X style traces', function() { const error = { message: 'nope', stack: 'http://localhost:8888/__spec__/core/UtilSpec.js:115:28\n' + 'run@http://localhost:8888/__jasmine__/jasmine.js:4320:27' }; const result = new jasmineUnderTest.StackTrace(error); expect(result.message).toBeFalsy(); expect(result.style).toEqual('webkit'); expect(result.frames).toEqual([ { raw: 'http://localhost:8888/__spec__/core/UtilSpec.js:115:28', func: undefined, file: 'http://localhost:8888/__spec__/core/UtilSpec.js', line: 115 }, { raw: 'run@http://localhost:8888/__jasmine__/jasmine.js:4320:27', func: 'run', file: 'http://localhost:8888/__jasmine__/jasmine.js', line: 4320 } ]); }); it('understands Safari 15 style traces', function() { const error = { message: 'nope', stack: '@http://localhost:8888/__spec__/core/FooSpec.js:164:24\n' + 'attempt@http://localhost:8888/__jasmine__/jasmine.js:8074:44\n' }; const result = new jasmineUnderTest.StackTrace(error); expect(result.message).toBeFalsy(); expect(result.style).toEqual('webkit'); expect(result.frames).toEqual([ { raw: '@http://localhost:8888/__spec__/core/FooSpec.js:164:24', func: undefined, file: 'http://localhost:8888/__spec__/core/FooSpec.js', line: 164 }, { raw: 'attempt@http://localhost:8888/__jasmine__/jasmine.js:8074:44', func: 'attempt', file: 'http://localhost:8888/__jasmine__/jasmine.js', line: 8074 } ]); }); it('does not mistake gibberish for Safari/Firefox/Phantom-OS X style traces', function() { const error = { message: 'nope', stack: 'randomcharsnotincludingwhitespace' }; const result = new jasmineUnderTest.StackTrace(error); expect(result.style).toBeNull(); expect(result.frames).toEqual([{ raw: error.stack }]); }); it('understands Phantom-Linux style traces', function() { const error = { message: 'nope', stack: ' at UserContext. (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. (http://localhost:8888/__spec__/core/UtilSpec.js:115:19)', func: 'UserContext.', 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', stack: ' at UserContext. (http://localhost:8888/__spec__/core/UtilSpec.js:115:19)\n' }; const result = new jasmineUnderTest.StackTrace(error); expect(result.frames).toEqual([ { raw: ' at UserContext. (http://localhost:8888/__spec__/core/UtilSpec.js:115:19)', func: 'UserContext.', file: 'http://localhost:8888/__spec__/core/UtilSpec.js', line: 115 } ]); }); it("omits properties except 'raw' for frames that are not understood", function() { const error = { message: 'nope', stack: ' at UserContext. (http://localhost:8888/__spec__/core/UtilSpec.js:115:19)\n' + ' but this is quite unexpected\n' + ' at QueueRunner.run (http://localhost:8888/__jasmine__/jasmine.js:4320:20)' }; const result = new jasmineUnderTest.StackTrace(error); expect(result.style).toEqual('v8'); expect(result.frames).toEqual([ { raw: ' at UserContext. (http://localhost:8888/__spec__/core/UtilSpec.js:115:19)', func: 'UserContext.', file: 'http://localhost:8888/__spec__/core/UtilSpec.js', line: 115 }, { raw: ' but this is quite unexpected' }, { 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('consideres different types of errors', function() { const error = { message: 'nope', stack: 'TypeError: nope\n' + ' at UserContext. (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('TypeError: nope'); expect(result.frames).toEqual([ { raw: ' at UserContext. (http://localhost:8888/__spec__/core/UtilSpec.js:115:19)', func: 'UserContext.', 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 } ]); const no_error = { message: 'nope', stack: 'Type Error: nope\n' + ' at UserContext. (http://localhost:8888/__spec__/core/UtilSpec.js:115:19)\n' + ' at QueueRunner.run (http://localhost:8888/__jasmine__/jasmine.js:4320:20)' }; const result_no_error = new jasmineUnderTest.StackTrace(no_error); expect(result_no_error.message).not.toEqual(jasmine.anything()); }); }); jasmine-4.5.0/spec/core/SuiteBuilderSpec.js000066400000000000000000000123041432731766000206120ustar00rootroot00000000000000describe('SuiteBuilder', function() { beforeEach(function() { // Rethrow exceptions to ease debugging spyOn(jasmineUnderTest.Suite.prototype, 'handleException').and.callFake( function(e) { throw e; } ); spyOn(jasmineUnderTest.Spec.prototype, 'handleException').and.callFake( function(e) { throw e; } ); }); it('creates the top suite', function() { const env = { configuration: () => ({}) }; const suiteBuilder = new jasmineUnderTest.SuiteBuilder({ env }); expect(suiteBuilder.topSuite).toBeInstanceOf(jasmineUnderTest.Suite); expect(suiteBuilder.topSuite.description).toEqual( 'Jasmine__TopLevel__Suite' ); expect(suiteBuilder.topSuite.parentSuite).toBeUndefined(); }); describe('#describe', function() { definesSuites('describe'); }); describe('#fdescribe', function() { definesSuites('fdescribe'); it('focuses the suite', function() { const env = { configuration: () => ({}) }; const suiteBuilder = new jasmineUnderTest.SuiteBuilder({ env }); const suite = suiteBuilder.fdescribe('a suite', function() { suiteBuilder.it('a spec'); }); expect(suite.isFocused).toBeTrue(); expect(suiteBuilder.focusedRunables).toEqual([suite.id]); }); it('unfocuses any focused ancestor suite', function() { const env = { configuration: () => ({}) }; const suiteBuilder = new jasmineUnderTest.SuiteBuilder({ env }); const grandparent = suiteBuilder.fdescribe('a suite', function() { suiteBuilder.describe('another suite', function() { suiteBuilder.fdescribe('the focused suite', function() { suiteBuilder.it('a spec'); }); }); }); expect(suiteBuilder.focusedRunables).not.toContain(grandparent.id); }); }); describe('#xdescribe', function() { definesSuites('xdescribe'); it('excludes the suite', function() { const env = { configuration: () => ({}) }; const suiteBuilder = new jasmineUnderTest.SuiteBuilder({ env }); const suite = suiteBuilder.xdescribe('a suite', function() { suiteBuilder.it('a spec'); }); expect(suite.markedExcluding).toBeTrue(); }); it('causes child suites to be marked excluded', function() { const env = { configuration: () => ({}) }; const suiteBuilder = new jasmineUnderTest.SuiteBuilder({ env }); let suite; suiteBuilder.xdescribe('a suite', function() { suite = suiteBuilder.describe('another suite', function() { suiteBuilder.it('a spec'); }); }); expect(suite.markedExcluding).toBeTrue(); }); }); describe('#it', function() { definesSpecs('it'); }); describe('#fit', function() { definesSpecs('fit'); }); describe('#xit', function() { definesSpecs('xit'); }); function definesSuites(fnName) { it('links suites to their parents and children', function() { const env = { configuration: () => ({}) }; const suiteBuilder = new jasmineUnderTest.SuiteBuilder({ env }); let child; const parent = suiteBuilder[fnName]('parent', function() { child = suiteBuilder[fnName]('child', function() { suiteBuilder.it('a spec'); }); }); expect(suiteBuilder.topSuite.children).toEqual([sameInstanceAs(parent)]); expect(parent.children).toEqual([sameInstanceAs(child)]); expect(child.parentSuite).toBe(parent); expect(parent.parentSuite).toBe(suiteBuilder.topSuite); }); it('gives each suite a unique ID', function() { const env = { configuration: () => ({}) }; const suiteBuilder = new jasmineUnderTest.SuiteBuilder({ env }); let child; const parent = suiteBuilder[fnName]('parent', function() { child = suiteBuilder[fnName]('child', function() { suiteBuilder.it('a spec'); }); }); const ids = [suiteBuilder.topSuite.id, parent.id, child.id]; for (const id of ids) { expect(id).toMatch(/^suite[0-9]$/); } expect(new Set(ids).size).toEqual(3); }); } function definesSpecs(fnName) { it('adds the spec to its suite', function() { const env = { configuration: () => ({}) }; const suiteBuilder = new jasmineUnderTest.SuiteBuilder({ env }); let spec; const suite = suiteBuilder.describe('a suite', function() { spec = suiteBuilder[fnName]('a spec', function() {}); }); expect(suite.children).toEqual([sameInstanceAs(spec)]); }); it('gives each spec a unique ID', function() { const env = { configuration: () => ({}) }; const suiteBuilder = new jasmineUnderTest.SuiteBuilder({ env }); const spec1 = suiteBuilder[fnName]('a spec', function() {}); const spec2 = suiteBuilder[fnName]('another spec', function() {}); expect(spec1.id).toMatch(/^spec[0-9]+$/); expect(spec2.id).toMatch(/^spec[0-9]+$/); expect(spec1.id).not.toEqual(spec2.id); }); } function sameInstanceAs(expected) { return { asymmetricMatch: function(actual) { return actual === expected; }, jasmineToString: function(pp) { return ''; } }; } }); jasmine-4.5.0/spec/core/SuiteSpec.js000066400000000000000000000265151432731766000173140ustar00rootroot00000000000000describe('Suite', function() { let env; beforeEach(function() { env = new jasmineUnderTest.Env(); }); afterEach(function() { env.cleanup_(); }); it('keeps its id', function() { const suite = new jasmineUnderTest.Suite({ env: env, id: 456, description: 'I am a suite' }); expect(suite.id).toEqual(456); }); it('returns blank full name for top level suite', function() { const suite = new jasmineUnderTest.Suite({ env: env, description: 'I am a suite' }); expect(suite.getFullName()).toEqual(''); }); it('returns its full name when it has parent suites', function() { const parentSuite = new jasmineUnderTest.Suite({ env: env, description: 'I am a parent suite', parentSuite: jasmine.createSpy('pretend top level suite') }), suite = new jasmineUnderTest.Suite({ env: env, description: 'I am a suite', parentSuite: parentSuite }); expect(suite.getFullName()).toEqual('I am a parent suite I am a suite'); }); it('adds beforeEach functions in order of needed execution', function() { const suite = new jasmineUnderTest.Suite({ env: env, description: 'I am a suite' }), outerBefore = { fn: 'outerBeforeEach' }, innerBefore = { fn: 'insideBeforeEach' }; suite.beforeEach(outerBefore); suite.beforeEach(innerBefore); expect(suite.beforeFns).toEqual([ { fn: innerBefore.fn, suite }, { fn: outerBefore.fn, suite } ]); }); it('adds beforeAll functions in order of needed execution', function() { const suite = new jasmineUnderTest.Suite({ env: env, description: 'I am a suite' }), outerBefore = { fn: 'outerBeforeAll' }, innerBefore = { fn: 'insideBeforeAll' }; suite.beforeAll(outerBefore); suite.beforeAll(innerBefore); expect(suite.beforeAllFns).toEqual([ { fn: outerBefore.fn, type: 'beforeAll', suite: jasmine.is(suite) }, { fn: innerBefore.fn, type: 'beforeAll', suite: jasmine.is(suite) } ]); }); it('adds afterEach functions in order of needed execution', function() { const suite = new jasmineUnderTest.Suite({ env: env, description: 'I am a suite' }), outerAfter = { fn: 'outerAfterEach' }, innerAfter = { fn: 'insideAfterEach' }; suite.afterEach(outerAfter); suite.afterEach(innerAfter); expect(suite.afterFns).toEqual([ { fn: innerAfter.fn, suite, type: 'afterEach' }, { fn: outerAfter.fn, suite, type: 'afterEach' } ]); }); it('adds afterAll functions in order of needed execution', function() { const suite = new jasmineUnderTest.Suite({ env: env, description: 'I am a suite' }), outerAfter = { fn: 'outerAfterAll' }, innerAfter = { fn: 'insideAfterAl' }; suite.afterAll(outerAfter); suite.afterAll(innerAfter); expect(suite.afterAllFns).toEqual([ { fn: innerAfter.fn, type: 'afterAll' }, { fn: outerAfter.fn, type: 'afterAll' } ]); }); it('has a status of failed if any expectations have failed', function() { const suite = new jasmineUnderTest.Suite({}); suite.addExpectationResult(false, {}); expect(suite.status()).toBe('failed'); }); it('retrieves a result with updated status', function() { const suite = new jasmineUnderTest.Suite({}); expect(suite.getResult().status).toBe('passed'); }); it('retrieves a result with pending status', function() { const suite = new jasmineUnderTest.Suite({}); suite.pend(); expect(suite.getResult().status).toBe('pending'); }); it('throws an ExpectationFailed when receiving a failed expectation when throwOnExpectationFailure is set', function() { const suite = new jasmineUnderTest.Suite({ throwOnExpectationFailure: true }); expect(function() { suite.addExpectationResult(false, { message: 'failed' }); }).toThrowError(jasmineUnderTest.errors.ExpectationFailed); expect(suite.status()).toBe('failed'); expect(suite.result.failedExpectations).toEqual([ jasmine.objectContaining({ message: 'failed' }) ]); }); it('does not add an additional failure when an expectation fails', function() { const suite = new jasmineUnderTest.Suite({}); suite.handleException(new jasmineUnderTest.errors.ExpectationFailed()); expect(suite.getResult().failedExpectations).toEqual([]); }); it('forwards late expectation failures to onLateError', function() { const onLateError = jasmine.createSpy('onLateError'); const suite = new jasmineUnderTest.Suite({ onLateError }); const data = { matcherName: '', passed: false, expected: '', actual: '', error: new Error('nope') }; suite.reportedDone = true; suite.addExpectationResult(false, data, true); expect(onLateError).toHaveBeenCalledWith( jasmine.objectContaining({ message: jasmine.stringMatching(/^Error: nope/) }) ); expect(suite.result.failedExpectations).toEqual([]); }); it('does not forward non-late expectation failures to onLateError', function() { const onLateError = jasmine.createSpy('onLateError'); const suite = new jasmineUnderTest.Suite({ onLateError }); const data = { matcherName: '', passed: false, expected: '', actual: '', error: new Error('nope') }; suite.addExpectationResult(false, data, true); expect(onLateError).not.toHaveBeenCalled(); expect(suite.result.failedExpectations.length).toEqual(1); }); it('forwards late handleException calls to onLateError', function() { const onLateError = jasmine.createSpy('onLateError'); const suite = new jasmineUnderTest.Suite({ onLateError }); const error = new Error('oops'); suite.reportedDone = true; suite.handleException(error); expect(onLateError).toHaveBeenCalledWith( jasmine.objectContaining({ message: jasmine.stringMatching(/^Error: oops/) }) ); expect(suite.result.failedExpectations).toEqual([]); }); it('does not forward non-late handleException calls to onLateError', function() { const onLateError = jasmine.createSpy('onLateError'); const suite = new jasmineUnderTest.Suite({ onLateError }); const error = new Error('oops'); suite.handleException(error); expect(onLateError).not.toHaveBeenCalled(); expect(suite.result.failedExpectations.length).toEqual(1); }); it('clears the reportedDone flag when reset', function() { const suite = new jasmineUnderTest.Suite({ queueableFn: { fn: function() {} } }); suite.reportedDone = true; suite.reset(); expect(suite.reportedDone).toBeFalse(); }); it('calls timer to compute duration', function() { const suite = new jasmineUnderTest.Suite({ env: env, id: 456, description: 'I am a suite', timer: jasmine.createSpyObj('timer', { start: null, elapsed: 77000 }) }); suite.startTimer(); suite.endTimer(); expect(suite.getResult().duration).toEqual(77000); }); describe('#sharedUserContext', function() { beforeEach(function() { this.suite = new jasmineUnderTest.Suite({}); }); it('returns a UserContext', function() { expect(this.suite.sharedUserContext().constructor).toBe( jasmineUnderTest.UserContext ); }); }); describe('attr.autoCleanClosures', function() { function arrangeSuite(attrs) { const suite = new jasmineUnderTest.Suite(attrs); suite.beforeAll(function() {}); suite.beforeEach(function() {}); suite.afterEach(function() {}); suite.afterAll(function() {}); return suite; } it('should clean closures when "attr.autoCleanClosures" is missing', function() { const suite = arrangeSuite({}); suite.cleanupBeforeAfter(); expect(suite.beforeAllFns[0].fn).toBe(null); expect(suite.beforeFns[0].fn).toBe(null); expect(suite.afterFns[0].fn).toBe(null); expect(suite.afterAllFns[0].fn).toBe(null); }); it('should clean closures when "attr.autoCleanClosures" is true', function() { const suite = arrangeSuite({ autoCleanClosures: true }); suite.cleanupBeforeAfter(); expect(suite.beforeAllFns[0].fn).toBe(null); expect(suite.beforeFns[0].fn).toBe(null); expect(suite.afterFns[0].fn).toBe(null); expect(suite.afterAllFns[0].fn).toBe(null); }); it('should NOT clean closures when "attr.autoCleanClosures" is false', function() { const suite = arrangeSuite({ autoCleanClosures: false }); suite.cleanupBeforeAfter(); expect(suite.beforeAllFns[0].fn).not.toBe(null); expect(suite.beforeFns[0].fn).not.toBe(null); expect(suite.afterFns[0].fn).not.toBe(null); expect(suite.afterAllFns[0].fn).not.toBe(null); }); }); describe('#reset', function() { it('should reset the "pending" status', function() { const suite = new jasmineUnderTest.Suite({}); suite.pend(); suite.reset(); expect(suite.getResult().status).toBe('passed'); }); it('should not reset the "pending" status when the suite was excluded', function() { const suite = new jasmineUnderTest.Suite({}); suite.exclude(); suite.reset(); expect(suite.getResult().status).toBe('pending'); }); it('should also reset the children', function() { const suite = new jasmineUnderTest.Suite({}); const child1 = jasmine.createSpyObj(['reset']); const child2 = jasmine.createSpyObj(['reset']); suite.addChild(child1); suite.addChild(child2); suite.reset(); expect(child1.reset).toHaveBeenCalled(); expect(child2.reset).toHaveBeenCalled(); }); it('should reset the failedExpectations', function() { const suite = new jasmineUnderTest.Suite({}); suite.handleException(new Error()); suite.reset(); const result = suite.getResult(); expect(result.status).toBe('passed'); expect(result.failedExpectations).toHaveSize(0); }); }); describe('#onMultipleDone', function() { it('reports a special error when it is the top suite', function() { const onLateError = jasmine.createSpy('onLateError'); const suite = new jasmineUnderTest.Suite({ onLateError, parentSuite: null }); suite.onMultipleDone(); expect(onLateError).toHaveBeenCalledTimes(1); expect(onLateError.calls.argsFor(0)[0]).toBeInstanceOf(Error); expect(onLateError.calls.argsFor(0)[0].message).toEqual( 'A top-level beforeAll or afterAll function called its ' + "'done' callback more than once." ); }); it('reports an error including the suite name when it is a normal suite', function() { const onLateError = jasmine.createSpy('onLateError'); const suite = new jasmineUnderTest.Suite({ onLateError, description: 'the suite', parentSuite: { description: 'the parent suite', parentSuite: {} } }); suite.onMultipleDone(); expect(onLateError).toHaveBeenCalledTimes(1); expect(onLateError.calls.argsFor(0)[0]).toBeInstanceOf(Error); expect(onLateError.calls.argsFor(0)[0].message).toEqual( "An asynchronous beforeAll or afterAll function called its 'done' " + 'callback more than once.\n(in suite: the parent suite the suite)' ); }); }); }); jasmine-4.5.0/spec/core/TimerSpec.js000066400000000000000000000016411432731766000172740ustar00rootroot00000000000000describe('Timer', function() { it('reports the time elapsed', function() { const fakeNow = jasmine.createSpy('fake Date.now'), timer = new jasmineUnderTest.Timer({ now: fakeNow }); fakeNow.and.returnValue(100); timer.start(); fakeNow.and.returnValue(200); expect(timer.elapsed()).toEqual(100); }); describe('when date is stubbed, perhaps by other testing helpers', function() { const origDate = Date; beforeEach(function() { // eslint-disable-next-line no-implicit-globals Date = jasmine.createSpy('date spy'); }); afterEach(function() { // eslint-disable-next-line no-implicit-globals Date = origDate; }); it('does not throw even though Date was taken away', function() { const timer = new jasmineUnderTest.Timer(); expect(timer.start).not.toThrow(); expect(timer.elapsed()).toEqual(jasmine.any(Number)); }); }); }); jasmine-4.5.0/spec/core/TreeProcessorSpec.js000066400000000000000000000731351432731766000210220ustar00rootroot00000000000000describe('TreeProcessor', function() { let nodeNumber = 0, leafNumber = 0; function Node(attrs) { attrs = attrs || {}; this.id = 'node' + nodeNumber++; this.children = attrs.children || []; this.canBeReentered = function() { return !attrs.noReenter; }; this.markedPending = attrs.markedPending || false; this.sharedUserContext = function() { return attrs.userContext || {}; }; this.getResult = jasmine.createSpy(this.id + '#execute'); this.beforeAllFns = attrs.beforeAllFns || []; this.afterAllFns = attrs.afterAllFns || []; this.cleanupBeforeAfter = function() {}; } function Leaf(attrs) { attrs = attrs || {}; this.id = 'leaf' + leafNumber++; this.markedPending = attrs.markedPending || false; this.execute = jasmine.createSpy(this.id + '#execute'); } it('processes a single leaf', function() { const leaf = new Leaf(), processor = new jasmineUnderTest.TreeProcessor({ tree: leaf, runnableIds: [leaf.id] }), result = processor.processTree(); expect(result.valid).toBe(true); expect(result[leaf.id]).toEqual({ excluded: false, willExecute: true, segments: jasmine.any(Array) }); }); it('processes a single pending leaf', function() { const leaf = new Leaf({ markedPending: true }), processor = new jasmineUnderTest.TreeProcessor({ tree: leaf, runnableIds: [leaf.id] }), result = processor.processTree(); expect(result.valid).toBe(true); expect(result[leaf.id]).toEqual({ excluded: false, willExecute: false, segments: jasmine.any(Array) }); }); it('processes a single non-specified leaf', function() { const leaf = new Leaf(), processor = new jasmineUnderTest.TreeProcessor({ tree: leaf, runnableIds: [] }), result = processor.processTree(); expect(result.valid).toBe(true); expect(result[leaf.id]).toEqual({ excluded: true, willExecute: false, segments: jasmine.any(Array) }); }); it('processes a single excluded leaf', function() { const leaf = new Leaf(), processor = new jasmineUnderTest.TreeProcessor({ tree: leaf, runnableIds: [leaf.id], excludeNode: function() { return true; } }), result = processor.processTree(); expect(result.valid).toBe(true); expect(result[leaf.id]).toEqual({ excluded: true, willExecute: false, segments: jasmine.any(Array) }); }); it('processes a tree with a single leaf with the root specified', function() { const leaf = new Leaf(), parent = new Node({ children: [leaf] }), processor = new jasmineUnderTest.TreeProcessor({ tree: parent, runnableIds: [parent.id] }), result = processor.processTree(); expect(result.valid).toBe(true); expect(result[parent.id]).toEqual({ excluded: false, willExecute: true, segments: jasmine.any(Array) }); expect(result[leaf.id]).toEqual({ excluded: false, willExecute: true, segments: jasmine.any(Array) }); }); it('processes a tree with a single pending leaf, with the root specified', function() { const leaf = new Leaf({ markedPending: true }), parent = new Node({ children: [leaf] }), processor = new jasmineUnderTest.TreeProcessor({ tree: parent, runnableIds: [parent.id] }), result = processor.processTree(); expect(result.valid).toBe(true); expect(result[parent.id]).toEqual({ excluded: false, willExecute: false, segments: jasmine.any(Array) }); expect(result[leaf.id]).toEqual({ excluded: false, willExecute: false, segments: jasmine.any(Array) }); }); it('processes a complicated tree with the root specified', function() { const pendingLeaf = new Leaf({ markedPending: true }), executableLeaf = new Leaf({ markedPending: false }), parent = new Node({ children: [pendingLeaf, executableLeaf] }), childless = new Node(), childOfPending = new Leaf({ markedPending: true }), pendingNode = new Node({ markedPending: true, children: [childOfPending] }), parentOfPendings = new Node({ markedPending: false, children: [childless, pendingNode] }), root = new Node({ children: [parent, parentOfPendings] }), processor = new jasmineUnderTest.TreeProcessor({ tree: root, runnableIds: [root.id] }), result = processor.processTree(); expect(result.valid).toBe(true); expect(result[root.id]).toEqual({ excluded: false, willExecute: true, segments: jasmine.any(Array) }); expect(result[parentOfPendings.id]).toEqual({ excluded: false, willExecute: false, segments: jasmine.any(Array) }); expect(result[childless.id]).toEqual({ excluded: false, willExecute: false, segments: jasmine.any(Array) }); expect(result[pendingLeaf.id]).toEqual({ excluded: false, willExecute: false, segments: jasmine.any(Array) }); expect(result[executableLeaf.id]).toEqual({ excluded: false, willExecute: true, segments: jasmine.any(Array) }); expect(result[parent.id]).toEqual({ excluded: false, willExecute: true, segments: jasmine.any(Array) }); expect(result[pendingNode.id]).toEqual({ excluded: false, willExecute: false, segments: jasmine.any(Array) }); expect(result[childOfPending.id]).toEqual({ excluded: false, willExecute: false, segments: jasmine.any(Array) }); }); it('marks the run order invalid if it would re-enter a node that does not allow re-entry', function() { const leaf1 = new Leaf(), leaf2 = new Leaf(), leaf3 = new Leaf(), reentered = new Node({ noReenter: true, children: [leaf1, leaf2] }), root = new Node({ children: [reentered, leaf3] }), processor = new jasmineUnderTest.TreeProcessor({ tree: root, runnableIds: [leaf1.id, leaf3.id, leaf2.id] }), result = processor.processTree(); expect(result).toEqual({ valid: false }); }); it('marks the run order valid if a node being re-entered allows re-entry', function() { const leaf1 = new Leaf(), leaf2 = new Leaf(), leaf3 = new Leaf(), reentered = new Node({ children: [leaf1, leaf2] }), root = new Node({ children: [reentered, leaf3] }), processor = new jasmineUnderTest.TreeProcessor({ tree: root, runnableIds: [leaf1.id, leaf3.id, leaf2.id] }), result = processor.processTree(); expect(result.valid).toBe(true); }); it("marks the run order valid if a node which can't be re-entered is only entered once", function() { const leaf1 = new Leaf(), leaf2 = new Leaf(), leaf3 = new Leaf(), noReentry = new Node({ noReenter: true }), root = new Node({ children: [noReentry] }), processor = new jasmineUnderTest.TreeProcessor({ tree: root, runnableIds: [leaf2.id, leaf1.id, leaf3.id] }), result = processor.processTree(); expect(result.valid).toBe(true); }); it("marks the run order valid if a node which can't be re-entered is run directly", function() { const noReentry = new Node({ noReenter: true }), root = new Node({ children: [noReentry] }), processor = new jasmineUnderTest.TreeProcessor({ tree: root, runnableIds: [root.id] }), result = processor.processTree(); expect(result.valid).toBe(true); }); it('runs a single leaf', async function() { const leaf = new Leaf(), node = new Node({ children: [leaf], userContext: { root: 'context' } }), queueRunner = jasmine.createSpy('queueRunner'), processor = new jasmineUnderTest.TreeProcessor({ tree: node, runnableIds: [leaf.id], queueRunnerFactory: queueRunner }); const promise = processor.execute(); expect(queueRunner).toHaveBeenCalledWith({ onComplete: jasmine.any(Function), onException: jasmine.any(Function), userContext: { root: 'context' }, queueableFns: [{ fn: jasmine.any(Function) }], onMultipleDone: null }); const queueRunnerArgs = queueRunner.calls.mostRecent().args[0]; queueRunnerArgs.queueableFns[0].fn('foo'); expect(leaf.execute).toHaveBeenCalledWith(queueRunner, 'foo', false, false); queueRunnerArgs.onComplete(); await expectAsync(promise).toBeResolvedTo(undefined); }); it('runs a node with no children', async function() { const node = new Node({ userContext: { node: 'context' } }), root = new Node({ children: [node], userContext: { root: 'context' } }), nodeStart = jasmine.createSpy('nodeStart'), nodeComplete = jasmine.createSpy('nodeComplete'), queueRunner = jasmine.createSpy('queueRunner'), processor = new jasmineUnderTest.TreeProcessor({ tree: root, runnableIds: [node.id], nodeStart: nodeStart, nodeComplete: nodeComplete, queueRunnerFactory: queueRunner }), nodeDone = jasmine.createSpy('nodeDone'); const promise = processor.execute(); expect(queueRunner).toHaveBeenCalledWith({ onComplete: jasmine.any(Function), onException: jasmine.any(Function), userContext: { root: 'context' }, queueableFns: [{ fn: jasmine.any(Function) }], onMultipleDone: null }); const queueRunnerArgs = queueRunner.calls.mostRecent().args[0]; queueRunnerArgs.queueableFns[0].fn(nodeDone); expect(queueRunner).toHaveBeenCalledWith({ onComplete: jasmine.any(Function), onMultipleDone: null, queueableFns: [{ fn: jasmine.any(Function) }], userContext: { node: 'context' }, onException: jasmine.any(Function), onMultipleDone: null }); queueRunner.calls.mostRecent().args[0].queueableFns[0].fn('foo'); expect(nodeStart).toHaveBeenCalledWith(node, 'foo'); node.getResult.and.returnValue({ my: 'result' }); queueRunner.calls.mostRecent().args[0].onComplete(); expect(nodeComplete).toHaveBeenCalledWith( node, { my: 'result' }, jasmine.any(Function) ); queueRunnerArgs.onComplete(); await expectAsync(promise).toBeResolvedTo(undefined); }); it('runs a node with children', function() { const leaf1 = new Leaf(), leaf2 = new Leaf(), node = new Node({ children: [leaf1, leaf2] }), root = new Node({ children: [node] }), queueRunner = jasmine.createSpy('queueRunner'), processor = new jasmineUnderTest.TreeProcessor({ tree: root, runnableIds: [node.id], queueRunnerFactory: queueRunner }), treeComplete = jasmine.createSpy('treeComplete'), nodeDone = jasmine.createSpy('nodeDone'); processor.execute(treeComplete); let queueableFns = queueRunner.calls.mostRecent().args[0].queueableFns; queueableFns[0].fn(nodeDone); queueableFns = queueRunner.calls.mostRecent().args[0].queueableFns; expect(queueableFns.length).toBe(3); queueableFns[1].fn('foo'); expect(leaf1.execute).toHaveBeenCalledWith( queueRunner, 'foo', false, false ); queueableFns[2].fn('bar'); expect(leaf2.execute).toHaveBeenCalledWith( queueRunner, 'bar', false, false ); }); it('cascades errors up the tree', function() { const leaf = new Leaf(), node = new Node({ children: [leaf] }), root = new Node({ children: [node] }), queueRunner = jasmine.createSpy('queueRunner'), nodeComplete = jasmine.createSpy('nodeComplete'), processor = new jasmineUnderTest.TreeProcessor({ tree: root, runnableIds: [node.id], nodeComplete: nodeComplete, queueRunnerFactory: queueRunner }), treeComplete = jasmine.createSpy('treeComplete'), nodeDone = jasmine.createSpy('nodeDone'); processor.execute(treeComplete); let queueableFns = queueRunner.calls.mostRecent().args[0].queueableFns; queueableFns[0].fn(nodeDone); queueableFns = queueRunner.calls.mostRecent().args[0].queueableFns; expect(queueableFns.length).toBe(2); queueableFns[1].fn('foo'); expect(leaf.execute).toHaveBeenCalledWith(queueRunner, 'foo', false, false); queueRunner.calls.mostRecent().args[0].onComplete('things'); expect(nodeComplete).toHaveBeenCalled(); nodeComplete.calls.mostRecent().args[2](); expect(nodeDone).toHaveBeenCalledWith('things'); }); it('runs an excluded node with leaf', function() { const leaf1 = new Leaf(), node = new Node({ children: [leaf1] }), root = new Node({ children: [node] }), queueRunner = jasmine.createSpy('queueRunner'), nodeStart = jasmine.createSpy('nodeStart'), nodeComplete = jasmine.createSpy('nodeComplete'), processor = new jasmineUnderTest.TreeProcessor({ tree: root, runnableIds: [], queueRunnerFactory: queueRunner, nodeStart: nodeStart, nodeComplete: nodeComplete }), treeComplete = jasmine.createSpy('treeComplete'), nodeDone = jasmine.createSpy('nodeDone'); processor.execute(treeComplete); let queueableFns = queueRunner.calls.mostRecent().args[0].queueableFns; queueableFns[0].fn(nodeDone); queueableFns = queueRunner.calls.mostRecent().args[0].queueableFns; expect(queueableFns.length).toBe(2); queueableFns[0].fn('bar'); expect(nodeStart).toHaveBeenCalledWith(node, 'bar'); queueableFns[1].fn('foo'); expect(leaf1.execute).toHaveBeenCalledWith(queueRunner, 'foo', true, false); node.getResult.and.returnValue({ im: 'disabled' }); queueRunner.calls.mostRecent().args[0].onComplete(); expect(nodeComplete).toHaveBeenCalledWith( node, { im: 'disabled' }, jasmine.any(Function) ); }); it('should execute node with correct arguments when failSpecWithNoExpectations option is set', function() { const leaf = new Leaf(), node = new Node({ children: [leaf] }), root = new Node({ children: [node] }), queueRunner = jasmine.createSpy('queueRunner'), nodeStart = jasmine.createSpy('nodeStart'), nodeComplete = jasmine.createSpy('nodeComplete'), processor = new jasmineUnderTest.TreeProcessor({ tree: root, runnableIds: [], queueRunnerFactory: queueRunner, nodeStart: nodeStart, nodeComplete: nodeComplete, failSpecWithNoExpectations: true }), treeComplete = jasmine.createSpy('treeComplete'), nodeDone = jasmine.createSpy('nodeDone'); processor.execute(treeComplete); let queueableFns = queueRunner.calls.mostRecent().args[0].queueableFns; queueableFns[0].fn(nodeDone); queueableFns = queueRunner.calls.mostRecent().args[0].queueableFns; expect(queueableFns.length).toBe(2); queueableFns[1].fn('foo'); expect(leaf.execute).toHaveBeenCalledWith(queueRunner, 'foo', true, true); }); it('runs beforeAlls for a node with children', function() { const leaf = new Leaf(), node = new Node({ children: [leaf], beforeAllFns: [ { fn: 'beforeAll1', timeout: 1 }, { fn: 'beforeAll2', timeout: 2 } ] }), root = new Node({ children: [node] }), queueRunner = jasmine.createSpy('queueRunner'), processor = new jasmineUnderTest.TreeProcessor({ tree: root, runnableIds: [node.id], queueRunnerFactory: queueRunner }), treeComplete = jasmine.createSpy('treeComplete'), nodeDone = jasmine.createSpy('nodeDone'); processor.execute(treeComplete); let queueableFns = queueRunner.calls.mostRecent().args[0].queueableFns; queueableFns[0].fn(nodeDone); queueableFns = queueRunner.calls.mostRecent().args[0].queueableFns; expect(queueableFns).toEqual([ { fn: jasmine.any(Function) }, { fn: 'beforeAll1', timeout: 1 }, { fn: 'beforeAll2', timeout: 2 }, { fn: jasmine.any(Function) } ]); }); it('runs afterAlls for a node with children', function() { const leaf = new Leaf(), afterAllFns = [{ fn: 'afterAll1' }, { fn: 'afterAll2' }], node = new Node({ children: [leaf], afterAllFns }), root = new Node({ children: [node] }), queueRunner = jasmine.createSpy('queueRunner'), processor = new jasmineUnderTest.TreeProcessor({ tree: root, runnableIds: [node.id], queueRunnerFactory: queueRunner }), treeComplete = jasmine.createSpy('treeComplete'), nodeDone = jasmine.createSpy('nodeDone'); processor.execute(treeComplete); let queueableFns = queueRunner.calls.mostRecent().args[0].queueableFns; queueableFns[0].fn(nodeDone); queueableFns = queueRunner.calls.mostRecent().args[0].queueableFns; expect(queueableFns).toEqual([ { fn: jasmine.any(Function) }, { fn: jasmine.any(Function) }, afterAllFns[0], afterAllFns[1] ]); }); it('does not run beforeAlls or afterAlls for a node with no children', function() { const node = new Node({ beforeAllFns: [{ fn: 'before' }], afterAllFns: [{ fn: 'after' }] }), root = new Node({ children: [node] }), queueRunner = jasmine.createSpy('queueRunner'), processor = new jasmineUnderTest.TreeProcessor({ tree: root, runnableIds: [node.id], queueRunnerFactory: queueRunner }), treeComplete = jasmine.createSpy('treeComplete'), nodeDone = jasmine.createSpy('nodeDone'); processor.execute(treeComplete); let queueableFns = queueRunner.calls.mostRecent().args[0].queueableFns; queueableFns[0].fn(nodeDone); queueableFns = queueRunner.calls.mostRecent().args[0].queueableFns; expect(queueableFns).toEqual([{ fn: jasmine.any(Function) }]); }); it('does not run beforeAlls or afterAlls for a node with only pending children', function() { const leaf = new Leaf({ markedPending: true }), node = new Node({ children: [leaf], beforeAllFns: [{ fn: 'before' }], afterAllFns: [{ fn: 'after' }], markedPending: false }), root = new Node({ children: [node] }), queueRunner = jasmine.createSpy('queueRunner'), processor = new jasmineUnderTest.TreeProcessor({ tree: root, runnableIds: [node.id], queueRunnerFactory: queueRunner }), treeComplete = jasmine.createSpy('treeComplete'), nodeDone = jasmine.createSpy('nodeDone'); processor.execute(treeComplete); let queueableFns = queueRunner.calls.mostRecent().args[0].queueableFns; queueableFns[0].fn(nodeDone); queueableFns = queueRunner.calls.mostRecent().args[0].queueableFns; expect(queueableFns).toEqual([ { fn: jasmine.any(Function) }, { fn: jasmine.any(Function) } ]); }); it('runs leaves in the order specified', function() { const leaf1 = new Leaf(), leaf2 = new Leaf(), root = new Node({ children: [leaf1, leaf2] }), queueRunner = jasmine.createSpy('queueRunner'), processor = new jasmineUnderTest.TreeProcessor({ tree: root, runnableIds: [leaf2.id, leaf1.id], queueRunnerFactory: queueRunner }), treeComplete = jasmine.createSpy('treeComplete'); processor.execute(treeComplete); const queueableFns = queueRunner.calls.mostRecent().args[0].queueableFns; queueableFns[0].fn(); expect(leaf1.execute).not.toHaveBeenCalled(); expect(leaf2.execute).toHaveBeenCalled(); queueableFns[1].fn(); expect(leaf1.execute).toHaveBeenCalled(); }); it('runs specified leaves before non-specified leaves within a parent node', function() { const specified = new Leaf(), nonSpecified = new Leaf(), root = new Node({ children: [nonSpecified, specified] }), queueRunner = jasmine.createSpy('queueRunner'), processor = new jasmineUnderTest.TreeProcessor({ tree: root, runnableIds: [specified.id], queueRunnerFactory: queueRunner }), treeComplete = jasmine.createSpy('treeComplete'); processor.execute(treeComplete); const queueableFns = queueRunner.calls.mostRecent().args[0].queueableFns; queueableFns[0].fn(); expect(nonSpecified.execute).not.toHaveBeenCalled(); expect(specified.execute).toHaveBeenCalledWith( queueRunner, undefined, false, false ); queueableFns[1].fn(); expect(nonSpecified.execute).toHaveBeenCalledWith( queueRunner, undefined, true, false ); }); it('runs nodes and leaves with a specified order', function() { const specifiedLeaf = new Leaf(), childLeaf = new Leaf(), specifiedNode = new Node({ children: [childLeaf] }), root = new Node({ children: [specifiedLeaf, specifiedNode] }), queueRunner = jasmine.createSpy('queueRunner'), processor = new jasmineUnderTest.TreeProcessor({ tree: root, runnableIds: [specifiedNode.id, specifiedLeaf.id], queueRunnerFactory: queueRunner }); processor.execute(); const queueableFns = queueRunner.calls.mostRecent().args[0].queueableFns; queueableFns[0].fn(); expect(specifiedLeaf.execute).not.toHaveBeenCalled(); const nodeQueueableFns = queueRunner.calls.mostRecent().args[0] .queueableFns; nodeQueueableFns[1].fn(); expect(childLeaf.execute).toHaveBeenCalled(); queueableFns[1].fn(); expect(specifiedLeaf.execute).toHaveBeenCalled(); }); it('runs a node multiple times if the order specified leaves and re-enters it', function() { const leaf1 = new Leaf(), leaf2 = new Leaf(), leaf3 = new Leaf(), leaf4 = new Leaf(), leaf5 = new Leaf(), reentered = new Node({ children: [leaf1, leaf2, leaf3] }), root = new Node({ children: [reentered, leaf4, leaf5] }), queueRunner = jasmine.createSpy('queueRunner'), processor = new jasmineUnderTest.TreeProcessor({ tree: root, runnableIds: [leaf1.id, leaf4.id, leaf2.id, leaf5.id, leaf3.id], queueRunnerFactory: queueRunner }); processor.execute(); const queueableFns = queueRunner.calls.mostRecent().args[0].queueableFns; expect(queueableFns.length).toBe(5); queueableFns[0].fn(); expect(queueRunner.calls.mostRecent().args[0].queueableFns.length).toBe(2); queueRunner.calls.mostRecent().args[0].queueableFns[1].fn(); expect(leaf1.execute).toHaveBeenCalled(); queueableFns[1].fn(); expect(leaf4.execute).toHaveBeenCalled(); queueableFns[2].fn(); expect(queueRunner.calls.count()).toBe(3); expect(queueRunner.calls.mostRecent().args[0].queueableFns.length).toBe(2); queueRunner.calls.mostRecent().args[0].queueableFns[1].fn(); expect(leaf2.execute).toHaveBeenCalled(); queueableFns[3].fn(); expect(leaf5.execute).toHaveBeenCalled(); queueableFns[4].fn(); expect(queueRunner.calls.count()).toBe(4); expect(queueRunner.calls.mostRecent().args[0].queueableFns.length).toBe(2); queueRunner.calls.mostRecent().args[0].queueableFns[1].fn(); expect(leaf3.execute).toHaveBeenCalled(); }); it('runs a parent of a node with segments correctly', function() { const leaf1 = new Leaf(), leaf2 = new Leaf(), leaf3 = new Leaf(), leaf4 = new Leaf(), leaf5 = new Leaf(), parent = new Node({ children: [leaf1, leaf2, leaf3] }), grandparent = new Node({ children: [parent] }), root = new Node({ children: [grandparent, leaf4, leaf5] }), queueRunner = jasmine.createSpy('queueRunner'), processor = new jasmineUnderTest.TreeProcessor({ tree: root, runnableIds: [leaf1.id, leaf4.id, leaf2.id, leaf5.id, leaf3.id], queueRunnerFactory: queueRunner }); processor.execute(); const queueableFns = queueRunner.calls.mostRecent().args[0].queueableFns; expect(queueableFns.length).toBe(5); queueableFns[0].fn(); expect(queueRunner.calls.count()).toBe(2); expect(queueRunner.calls.mostRecent().args[0].queueableFns.length).toBe(2); queueRunner.calls.mostRecent().args[0].queueableFns[1].fn(); expect(queueRunner.calls.count()).toBe(3); queueRunner.calls.mostRecent().args[0].queueableFns[1].fn(); expect(leaf1.execute).toHaveBeenCalled(); queueableFns[1].fn(); expect(leaf4.execute).toHaveBeenCalled(); queueableFns[2].fn(); expect(queueRunner.calls.count()).toBe(4); expect(queueRunner.calls.mostRecent().args[0].queueableFns.length).toBe(2); queueRunner.calls.mostRecent().args[0].queueableFns[1].fn(); expect(queueRunner.calls.count()).toBe(5); queueRunner.calls.mostRecent().args[0].queueableFns[1].fn(); expect(leaf2.execute).toHaveBeenCalled(); queueableFns[3].fn(); expect(leaf5.execute).toHaveBeenCalled(); queueableFns[4].fn(); expect(queueRunner.calls.count()).toBe(6); expect(queueRunner.calls.mostRecent().args[0].queueableFns.length).toBe(2); queueRunner.calls.mostRecent().args[0].queueableFns[1].fn(); expect(queueRunner.calls.count()).toBe(7); queueRunner.calls.mostRecent().args[0].queueableFns[1].fn(); expect(leaf3.execute).toHaveBeenCalled(); }); it('runs nodes in the order they were declared', function() { const leaf1 = new Leaf(), leaf2 = new Leaf(), leaf3 = new Leaf(), parent = new Node({ children: [leaf2, leaf3] }), root = new Node({ children: [leaf1, parent] }), queueRunner = jasmine.createSpy('queueRunner'), processor = new jasmineUnderTest.TreeProcessor({ tree: root, runnableIds: [root.id], queueRunnerFactory: queueRunner }); processor.execute(); const queueableFns = queueRunner.calls.mostRecent().args[0].queueableFns; expect(queueableFns.length).toBe(2); queueableFns[0].fn(); expect(leaf1.execute).toHaveBeenCalled(); queueableFns[1].fn(); const childFns = queueRunner.calls.mostRecent().args[0].queueableFns; expect(childFns.length).toBe(3); childFns[1].fn(); expect(leaf2.execute).toHaveBeenCalled(); childFns[2].fn(); expect(leaf3.execute).toHaveBeenCalled(); }); it('runs large segments of nodes in the order they were declared', function() { const leaf1 = new Leaf(), leaf2 = new Leaf(), leaf3 = new Leaf(), leaf4 = new Leaf(), leaf5 = new Leaf(), leaf6 = new Leaf(), leaf7 = new Leaf(), leaf8 = new Leaf(), leaf9 = new Leaf(), leaf10 = new Leaf(), leaf11 = new Leaf(), root = new Node({ children: [ leaf1, leaf2, leaf3, leaf4, leaf5, leaf6, leaf7, leaf8, leaf9, leaf10, leaf11 ] }), queueRunner = jasmine.createSpy('queueRunner'), processor = new jasmineUnderTest.TreeProcessor({ tree: root, runnableIds: [root.id], queueRunnerFactory: queueRunner }); processor.execute(); const queueableFns = queueRunner.calls.mostRecent().args[0].queueableFns; expect(queueableFns.length).toBe(11); queueableFns[0].fn(); expect(leaf1.execute).toHaveBeenCalled(); queueableFns[1].fn(); expect(leaf2.execute).toHaveBeenCalled(); queueableFns[2].fn(); expect(leaf3.execute).toHaveBeenCalled(); queueableFns[3].fn(); expect(leaf4.execute).toHaveBeenCalled(); queueableFns[4].fn(); expect(leaf5.execute).toHaveBeenCalled(); queueableFns[5].fn(); expect(leaf6.execute).toHaveBeenCalled(); queueableFns[6].fn(); expect(leaf7.execute).toHaveBeenCalled(); queueableFns[7].fn(); expect(leaf8.execute).toHaveBeenCalled(); queueableFns[8].fn(); expect(leaf9.execute).toHaveBeenCalled(); queueableFns[9].fn(); expect(leaf10.execute).toHaveBeenCalled(); queueableFns[10].fn(); expect(leaf11.execute).toHaveBeenCalled(); }); it('runs nodes in a custom order when orderChildren is overridden', function() { const leaf1 = new Leaf(), leaf2 = new Leaf(), leaf3 = new Leaf(), leaf4 = new Leaf(), leaf5 = new Leaf(), leaf6 = new Leaf(), leaf7 = new Leaf(), leaf8 = new Leaf(), leaf9 = new Leaf(), leaf10 = new Leaf(), leaf11 = new Leaf(), root = new Node({ children: [ leaf1, leaf2, leaf3, leaf4, leaf5, leaf6, leaf7, leaf8, leaf9, leaf10, leaf11 ] }), queueRunner = jasmine.createSpy('queueRunner'), processor = new jasmineUnderTest.TreeProcessor({ tree: root, runnableIds: [root.id], queueRunnerFactory: queueRunner, orderChildren: function(node) { const children = node.children.slice(); return children.reverse(); } }); processor.execute(); const queueableFns = queueRunner.calls.mostRecent().args[0].queueableFns; expect(queueableFns.length).toBe(11); queueableFns[0].fn(); expect(leaf11.execute).toHaveBeenCalled(); queueableFns[1].fn(); expect(leaf10.execute).toHaveBeenCalled(); queueableFns[2].fn(); expect(leaf9.execute).toHaveBeenCalled(); queueableFns[3].fn(); expect(leaf8.execute).toHaveBeenCalled(); queueableFns[4].fn(); expect(leaf7.execute).toHaveBeenCalled(); queueableFns[5].fn(); expect(leaf6.execute).toHaveBeenCalled(); queueableFns[6].fn(); expect(leaf5.execute).toHaveBeenCalled(); queueableFns[7].fn(); expect(leaf4.execute).toHaveBeenCalled(); queueableFns[8].fn(); expect(leaf3.execute).toHaveBeenCalled(); queueableFns[9].fn(); expect(leaf2.execute).toHaveBeenCalled(); queueableFns[10].fn(); expect(leaf1.execute).toHaveBeenCalled(); }); }); jasmine-4.5.0/spec/core/UserContextSpec.js000066400000000000000000000030701432731766000204750ustar00rootroot00000000000000describe('UserContext', function() { it('Behaves just like an plain object', function() { const context = new jasmineUnderTest.UserContext(), properties = []; for (const prop in context) { if (obj.hasOwnProperty(prop)) { properties.push(prop); } } expect(properties).toEqual([]); }); describe('.fromExisting', function() { describe('when using an already built context as model', function() { beforeEach(function() { this.context = new jasmineUnderTest.UserContext(); this.context.key = 'value'; this.cloned = jasmineUnderTest.UserContext.fromExisting(this.context); }); it('returns a cloned object', function() { expect(this.cloned).toEqual(this.context); }); it('does not return the same object', function() { expect(this.cloned).not.toBe(this.context); }); }); describe('when using a regular object as parameter', function() { beforeEach(function() { this.context = {}; this.value = 'value'; this.context.key = this.value; this.cloned = jasmineUnderTest.UserContext.fromExisting(this.context); }); it('returns an object with the same attributes', function() { expect(this.cloned.key).toEqual(this.value); }); it('does not return the same object', function() { expect(this.cloned).not.toBe(this.context); }); it('returns an UserContext', function() { expect(this.cloned.constructor).toBe(jasmineUnderTest.UserContext); }); }); }); }); jasmine-4.5.0/spec/core/UtilSpec.js000066400000000000000000000152561432731766000171400ustar00rootroot00000000000000describe('util', function() { describe('isArray_', function() { it('should return true if the argument is an array', function() { expect(jasmineUnderTest.isArray_([])).toBe(true); expect(jasmineUnderTest.isArray_(['a'])).toBe(true); }); it('should return false if the argument is not an array', function() { expect(jasmineUnderTest.isArray_(undefined)).toBe(false); expect(jasmineUnderTest.isArray_({})).toBe(false); expect(jasmineUnderTest.isArray_(function() {})).toBe(false); expect(jasmineUnderTest.isArray_('foo')).toBe(false); expect(jasmineUnderTest.isArray_(5)).toBe(false); expect(jasmineUnderTest.isArray_(null)).toBe(false); }); }); describe('isObject_', function() { it('should return true if the argument is an object', function() { expect(jasmineUnderTest.isObject_({})).toBe(true); expect(jasmineUnderTest.isObject_({ an: 'object' })).toBe(true); }); it('should return false if the argument is not an object', function() { expect(jasmineUnderTest.isObject_(undefined)).toBe(false); expect(jasmineUnderTest.isObject_([])).toBe(false); expect(jasmineUnderTest.isObject_(function() {})).toBe(false); expect(jasmineUnderTest.isObject_('foo')).toBe(false); expect(jasmineUnderTest.isObject_(5)).toBe(false); expect(jasmineUnderTest.isObject_(null)).toBe(false); }); }); describe('promise utils', function() { let mockNativePromise, mockPromiseLikeObject; const mockPromiseLike = function() { this.then = function() {}; }; beforeEach(function() { mockNativePromise = new Promise(function() {}); mockPromiseLikeObject = new mockPromiseLike(); }); describe('isPromise', function() { it('should return true when passed a native promise', function() { expect(jasmineUnderTest.isPromise(mockNativePromise)).toBe(true); }); it('should return false for promise like objects', function() { expect(jasmineUnderTest.isPromise(mockPromiseLikeObject)).toBe(false); }); it('should return false for strings', function() { expect(jasmineUnderTest.isPromise('hello')).toBe(false); }); it('should return false for numbers', function() { expect(jasmineUnderTest.isPromise(3)).toBe(false); }); it('should return false for null', function() { expect(jasmineUnderTest.isPromise(null)).toBe(false); }); it('should return false for undefined', function() { expect(jasmineUnderTest.isPromise(undefined)).toBe(false); }); it('should return false for arrays', function() { expect(jasmineUnderTest.isPromise([])).toBe(false); }); it('should return false for objects', function() { expect(jasmineUnderTest.isPromise({})).toBe(false); }); it('should return false for boolean values', function() { expect(jasmineUnderTest.isPromise(true)).toBe(false); }); }); describe('isPromiseLike', function() { it('should return true when passed a native promise', function() { expect(jasmineUnderTest.isPromiseLike(mockNativePromise)).toBe(true); }); it('should return true for promise like objects', function() { expect(jasmineUnderTest.isPromiseLike(mockPromiseLikeObject)).toBe( true ); }); it('should return false if then is not a function', function() { expect( jasmineUnderTest.isPromiseLike({ then: { its: 'Not a function :O' } }) ).toBe(false); }); it('should return false for strings', function() { expect(jasmineUnderTest.isPromiseLike('hello')).toBe(false); }); it('should return false for numbers', function() { expect(jasmineUnderTest.isPromiseLike(3)).toBe(false); }); it('should return false for null', function() { expect(jasmineUnderTest.isPromiseLike(null)).toBe(false); }); it('should return false for undefined', function() { expect(jasmineUnderTest.isPromiseLike(undefined)).toBe(false); }); it('should return false for arrays', function() { expect(jasmineUnderTest.isPromiseLike([])).toBe(false); }); it('should return false for objects', function() { expect(jasmineUnderTest.isPromiseLike({})).toBe(false); }); it('should return false for boolean values', function() { expect(jasmineUnderTest.isPromiseLike(true)).toBe(false); }); }); }); 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([ true, false ]); expect(jasmineUnderTest.util.cloneArgs([0, 1])).toEqual([0, 1]); expect(jasmineUnderTest.util.cloneArgs(['str'])).toEqual(['str']); }); it('clones Regexp objects as-is', function() { const regex = /match/; expect(jasmineUnderTest.util.cloneArgs([regex])).toEqual([regex]); }); it('clones Date objects as-is', function() { const date = new Date(2022, 1, 1); expect(jasmineUnderTest.util.cloneArgs([date])).toEqual([date]); }); it('clones null and undefined', function() { expect(jasmineUnderTest.util.cloneArgs([null])).toEqual([null]); expect(jasmineUnderTest.util.cloneArgs([undefined])).toEqual([undefined]); }); }); describe('getPropertyDescriptor', function() { it('get property descriptor from object', function() { const obj = { prop: 1 }, actual = jasmineUnderTest.util.getPropertyDescriptor(obj, 'prop'), expected = Object.getOwnPropertyDescriptor(obj, 'prop'); expect(actual).toEqual(expected); }); it('get property descriptor from object property', function() { const proto = { prop: 1 }, actual = jasmineUnderTest.util.getPropertyDescriptor(proto, 'prop'), expected = Object.getOwnPropertyDescriptor(proto, 'prop'); expect(actual).toEqual(expected); }); }); describe('jasmineFile', function() { it('returns the file containing jasmine.util', function() { // Chrome sometimes reports foo.js as foo.js/, so tolerate // a trailing slash if present. expect(jasmineUnderTest.util.jasmineFile()).toMatch(/util.js\/?$/); expect(jasmine.util.jasmineFile()).toMatch(/jasmine.js\/?$/); }); }); }); jasmine-4.5.0/spec/core/asymmetric_equality/000077500000000000000000000000001432731766000211335ustar00rootroot00000000000000jasmine-4.5.0/spec/core/asymmetric_equality/AnySpec.js000066400000000000000000000043331432731766000230360ustar00rootroot00000000000000describe('Any', function() { it('matches a string', function() { const any = new jasmineUnderTest.Any(String); expect(any.asymmetricMatch('foo')).toBe(true); }); it('matches a number', function() { const any = new jasmineUnderTest.Any(Number); expect(any.asymmetricMatch(1)).toBe(true); }); it('matches a function', function() { const any = new jasmineUnderTest.Any(Function); expect(any.asymmetricMatch(function() {})).toBe(true); }); it('matches an Object', function() { const any = new jasmineUnderTest.Any(Object); expect(any.asymmetricMatch({})).toBe(true); }); it('matches a Boolean', function() { const any = new jasmineUnderTest.Any(Boolean); expect(any.asymmetricMatch(true)).toBe(true); }); it('matches a Map', function() { const any = new jasmineUnderTest.Any(Map); expect(any.asymmetricMatch(new Map())).toBe(true); }); it('matches a Set', function() { const any = new jasmineUnderTest.Any(Set); expect(any.asymmetricMatch(new Set())).toBe(true); }); it('matches a TypedArray', function() { const any = new jasmineUnderTest.Any(Uint32Array); expect(any.asymmetricMatch(new Uint32Array([]))).toBe(true); }); it('matches a Symbol', function() { const any = new jasmineUnderTest.Any(Symbol); expect(any.asymmetricMatch(Symbol())).toBe(true); }); it('matches another constructed object', function() { const Thing = function() {}, any = new jasmineUnderTest.Any(Thing); expect(any.asymmetricMatch(new Thing())).toBe(true); }); it('does not treat null as an Object', function() { const any = new jasmineUnderTest.Any(Object); expect(any.asymmetricMatch(null)).toBe(false); }); it("jasmineToString's itself", function() { const any = new jasmineUnderTest.Any(Number); expect(any.jasmineToString()).toEqual(''); expect(any.jasmineToString()).toEqual(''); }); describe('when called without an argument', function() { it('tells the user to pass a constructor or use jasmine.anything()', function() { expect(function() { new jasmineUnderTest.Any(); }).toThrowError(TypeError, /constructor.*anything/); }); }); }); jasmine-4.5.0/spec/core/asymmetric_equality/AnythingSpec.js000066400000000000000000000035231432731766000240700ustar00rootroot00000000000000describe('Anything', function() { it('matches a string', function() { const anything = new jasmineUnderTest.Anything(); expect(anything.asymmetricMatch('foo')).toBe(true); }); it('matches a number', function() { const anything = new jasmineUnderTest.Anything(); expect(anything.asymmetricMatch(42)).toBe(true); }); it('matches an object', function() { const anything = new jasmineUnderTest.Anything(); expect(anything.asymmetricMatch({ foo: 'bar' })).toBe(true); }); it('matches an array', function() { const anything = new jasmineUnderTest.Anything(); expect(anything.asymmetricMatch([1, 2, 3])).toBe(true); }); it('matches a Map', function() { const anything = new jasmineUnderTest.Anything(); expect(anything.asymmetricMatch(new Map())).toBe(true); }); it('matches a Set', function() { const anything = new jasmineUnderTest.Anything(); expect(anything.asymmetricMatch(new Set())).toBe(true); }); it('matches a TypedArray', function() { const anything = new jasmineUnderTest.Anything(); expect(anything.asymmetricMatch(new Uint32Array([]))).toBe(true); }); it('matches a Symbol', function() { const anything = new jasmineUnderTest.Anything(); expect(anything.asymmetricMatch(Symbol())).toBe(true); }); it("doesn't match undefined", function() { const anything = new jasmineUnderTest.Anything(); expect(anything.asymmetricMatch()).toBe(false); expect(anything.asymmetricMatch(undefined)).toBe(false); }); it("doesn't match null", function() { const anything = new jasmineUnderTest.Anything(); expect(anything.asymmetricMatch(null)).toBe(false); }); it("jasmineToString's itself", function() { const anything = new jasmineUnderTest.Anything(); expect(anything.jasmineToString()).toEqual(''); }); }); jasmine-4.5.0/spec/core/asymmetric_equality/ArrayContainingSpec.js000066400000000000000000000047221432731766000254010ustar00rootroot00000000000000describe('ArrayContaining', function() { it('matches any actual to an empty array', function() { const containing = new jasmineUnderTest.ArrayContaining([]); expect(containing.asymmetricMatch('foo')).toBe(true); }); it('does not work when not passed an array', function() { const containing = new jasmineUnderTest.ArrayContaining('foo'); expect(function() { containing.asymmetricMatch([]); }).toThrowError(/not 'foo'/); }); it('matches when the item is in the actual', function() { const containing = new jasmineUnderTest.ArrayContaining(['foo']); const matchersUtil = new jasmineUnderTest.MatchersUtil(); expect(containing.asymmetricMatch(['foo'], matchersUtil)).toBe(true); }); it('matches when additional items are in the actual', function() { const containing = new jasmineUnderTest.ArrayContaining(['foo']); const matchersUtil = new jasmineUnderTest.MatchersUtil(); expect(containing.asymmetricMatch(['foo', 'bar'], matchersUtil)).toBe(true); }); it('does not match when the item is not in the actual', function() { const containing = new jasmineUnderTest.ArrayContaining(['foo']); const matchersUtil = new jasmineUnderTest.MatchersUtil(); expect(containing.asymmetricMatch(['bar'], matchersUtil)).toBe(false); }); it('does not match when the actual is not an array', function() { const containing = new jasmineUnderTest.ArrayContaining(['foo']); const matchersUtil = new jasmineUnderTest.MatchersUtil(); expect(containing.asymmetricMatch('foo', matchersUtil)).toBe(false); }); it('jasmineToStrings itself', function() { const sample = [], matcher = new jasmineUnderTest.ArrayContaining(sample), pp = jasmine.createSpy('pp').and.returnValue('sample'); expect(matcher.jasmineToString(pp)).toEqual( '' ); expect(pp).toHaveBeenCalledWith(sample); }); it('uses custom equality testers', function() { const tester = function(a, b) { // All "foo*" strings match each other. if ( typeof a == 'string' && typeof b == 'string' && a.slice(0, 3) == 'foo' && b.slice(0, 3) == 'foo' ) { return true; } }; const containing = new jasmineUnderTest.ArrayContaining(['fooVal']); const matchersUtil = new jasmineUnderTest.MatchersUtil({ customTesters: [tester] }); expect(containing.asymmetricMatch(['fooBar'], matchersUtil)).toBe(true); }); }); jasmine-4.5.0/spec/core/asymmetric_equality/ArrayWithExactContentsSpec.js000066400000000000000000000042051432731766000267220ustar00rootroot00000000000000describe('ArrayWithExactContents', function() { it('matches an array with the same items in a different order', function() { const matcher = new jasmineUnderTest.ArrayWithExactContents(['a', 2, /a/]); const matchersUtil = new jasmineUnderTest.MatchersUtil(); expect(matcher.asymmetricMatch([2, 'a', /a/], matchersUtil)).toBe(true); }); it('does not work when not passed an array', function() { const matcher = new jasmineUnderTest.ArrayWithExactContents('foo'); expect(function() { matcher.asymmetricMatch([]); }).toThrowError(/not 'foo'/); }); it('does not match when an item is missing', function() { const matcher = new jasmineUnderTest.ArrayWithExactContents(['a', 2, /a/]); const matchersUtil = new jasmineUnderTest.MatchersUtil(); expect(matcher.asymmetricMatch(['a', 2], matchersUtil)).toBe(false); expect(matcher.asymmetricMatch(['a', 2, undefined], matchersUtil)).toBe( false ); }); it('does not match when there is an extra item', function() { const matcher = new jasmineUnderTest.ArrayWithExactContents(['a']); const matchersUtil = new jasmineUnderTest.MatchersUtil(); expect(matcher.asymmetricMatch(['a', 2], matchersUtil)).toBe(false); }); it('jasmineToStrings itself', function() { const sample = [], matcher = new jasmineUnderTest.ArrayWithExactContents(sample), pp = jasmine.createSpy('pp').and.returnValue('sample'); expect(matcher.jasmineToString(pp)).toEqual( '' ); expect(pp).toHaveBeenCalledWith(sample); }); it('uses custom equality testers', function() { const tester = function(a, b) { // All "foo*" strings match each other. if ( typeof a == 'string' && typeof b == 'string' && a.slice(0, 3) == 'foo' && b.slice(0, 3) == 'foo' ) { return true; } }; const matcher = new jasmineUnderTest.ArrayWithExactContents(['fooVal']); const matchersUtil = new jasmineUnderTest.MatchersUtil({ customTesters: [tester] }); expect(matcher.asymmetricMatch(['fooBar'], matchersUtil)).toBe(true); }); }); jasmine-4.5.0/spec/core/asymmetric_equality/EmptySpec.js000066400000000000000000000027261432731766000234110ustar00rootroot00000000000000describe('Empty', function() { it('matches an empty object', function() { const empty = new jasmineUnderTest.Empty(); expect(empty.asymmetricMatch({})).toBe(true); expect(empty.asymmetricMatch({ undefined: false })).toBe(false); }); it('matches an empty array', function() { const empty = new jasmineUnderTest.Empty(); expect(empty.asymmetricMatch([])).toBe(true); expect(empty.asymmetricMatch([1, 12, 3])).toBe(false); }); it('matches an empty string', function() { const empty = new jasmineUnderTest.Empty(); expect(empty.asymmetricMatch('')).toBe(true); expect(empty.asymmetricMatch('')).toBe(true); expect(empty.asymmetricMatch('12312')).toBe(false); }); it('matches an empty map', function() { const empty = new jasmineUnderTest.Empty(); const fullMap = new Map(); fullMap.set('thing', 2); expect(empty.asymmetricMatch(new Map())).toBe(true); expect(empty.asymmetricMatch(fullMap)).toBe(false); }); it('matches an empty set', function() { const empty = new jasmineUnderTest.Empty(); const fullSet = new Set(); fullSet.add(3); expect(empty.asymmetricMatch(new Set())).toBe(true); expect(empty.asymmetricMatch(fullSet)).toBe(false); }); it('matches an empty typed array', function() { const empty = new jasmineUnderTest.Empty(); expect(empty.asymmetricMatch(new Int16Array())).toBe(true); expect(empty.asymmetricMatch(new Int16Array([1, 2]))).toBe(false); }); }); jasmine-4.5.0/spec/core/asymmetric_equality/FalsySpec.js000066400000000000000000000022731432731766000233660ustar00rootroot00000000000000describe('Falsy', function() { it('is true for an empty string', function() { const falsy = new jasmineUnderTest.Falsy(); expect(falsy.asymmetricMatch('')).toBe(true); expect(falsy.asymmetricMatch('')).toBe(true); expect(falsy.asymmetricMatch('asdasdad')).toBe(false); }); it('is false for a number that is 0', function() { const falsy = new jasmineUnderTest.Falsy(Number); expect(falsy.asymmetricMatch(1)).toBe(false); expect(falsy.asymmetricMatch(0)).toBe(true); expect(falsy.asymmetricMatch(-23)).toBe(false); expect(falsy.asymmetricMatch(-3.1)).toBe(false); }); it('is true for a null or undefined', function() { const falsy = new jasmineUnderTest.Falsy(Function); expect(falsy.asymmetricMatch(null)).toBe(true); expect(falsy.asymmetricMatch(undefined)).toBe(true); }); it('is true for NaN', function() { const falsy = new jasmineUnderTest.Falsy(Object); expect(falsy.asymmetricMatch(NaN)).toBe(true); }); it('is true for a false Boolean', function() { const falsy = new jasmineUnderTest.Falsy(Boolean); expect(falsy.asymmetricMatch(false)).toBe(true); expect(falsy.asymmetricMatch(true)).toBe(false); }); }); jasmine-4.5.0/spec/core/asymmetric_equality/IsSpec.js000066400000000000000000000020411432731766000226540ustar00rootroot00000000000000describe('Is', function() { it('passes for primitives that are ===', function() { const exactly = new jasmineUnderTest.Is(17); expect(exactly.asymmetricMatch(17)).toBeTrue(); }); it('fails for primitives that are not ===', function() { const exactly = new jasmineUnderTest.Is(42); expect(exactly.asymmetricMatch('42')).toBeFalse(); }); it('passes for the same object instance', function() { const obj = {}; const exactly = new jasmineUnderTest.Is(obj); expect(exactly.asymmetricMatch(obj)).toBeTrue(); }); it('fails for different object instances, even if they are deep value equal', function() { const exactly = new jasmineUnderTest.Is({}); expect(exactly.asymmetricMatch({})).toBeFalse(); }); it('describes itself for use in diffs and pretty printing', function() { const exactly = new jasmineUnderTest.Is({ foo: ['bar'] }); const pp = jasmineUnderTest.basicPrettyPrinter_; expect(exactly.jasmineToString(pp)).toEqual( "" ); }); }); jasmine-4.5.0/spec/core/asymmetric_equality/MapContainingSpec.js000066400000000000000000000131111432731766000250300ustar00rootroot00000000000000describe('MapContaining', function() { it('matches any actual map to an empty map', function() { const actualMap = new Map([['foo', 'bar']]); const containing = new jasmineUnderTest.MapContaining(new Map()); expect(containing.asymmetricMatch(actualMap)).toBe(true); }); it('matches when all the key/value pairs in sample have matches in actual', function() { const actualMap = new Map([ ['foo', [1, 2, 3]], [{ foo: 'bar' }, 'baz'], ['other', 'any'] ]); const containingMap = new Map([ [{ foo: 'bar' }, 'baz'], ['foo', [1, 2, 3]] ]); const containing = new jasmineUnderTest.MapContaining(containingMap); const matchersUtil = new jasmineUnderTest.MatchersUtil(); expect(containing.asymmetricMatch(actualMap, matchersUtil)).toBe(true); }); it('does not match when a key is not in actual', function() { const actualMap = new Map([ ['foo', [1, 2, 3]], [{ foo: 'not a bar' }, 'baz'] ]); const containingMap = new Map([ [{ foo: 'bar' }, 'baz'], ['foo', [1, 2, 3]] ]); const containing = new jasmineUnderTest.MapContaining(containingMap); const matchersUtil = new jasmineUnderTest.MatchersUtil(); expect(containing.asymmetricMatch(actualMap, matchersUtil)).toBe(false); }); it('does not match when a value is not in actual', function() { const actualMap = new Map([['foo', [1, 2, 3]], [{ foo: 'bar' }, 'baz']]); const containingMap = new Map([[{ foo: 'bar' }, 'baz'], ['foo', [1, 2]]]); const containing = new jasmineUnderTest.MapContaining(containingMap); const matchersUtil = new jasmineUnderTest.MatchersUtil(); expect(containing.asymmetricMatch(actualMap, matchersUtil)).toBe(false); }); it('matches when all the key/value pairs in sample have asymmetric matches in actual', function() { const actualMap = new Map([ ['foo1', 'not a bar'], ['foo2', 'bar'], ['baz', [1, 2, 3, 4]] ]); const containingMap = new Map([ [jasmineUnderTest.stringMatching(/^foo\d/), 'bar'], ['baz', jasmineUnderTest.arrayContaining([2, 3])] ]); const containing = new jasmineUnderTest.MapContaining(containingMap); const matchersUtil = new jasmineUnderTest.MatchersUtil(); expect(containing.asymmetricMatch(actualMap, matchersUtil)).toBe(true); }); it('does not match when a key in sample has no asymmetric matches in actual', function() { const actualMap = new Map([['a-foo1', 'bar'], ['baz', [1, 2, 3, 4]]]); const containingMap = new Map([ [jasmineUnderTest.stringMatching(/^foo\d/), 'bar'], ['baz', jasmineUnderTest.arrayContaining([2, 3])] ]); const containing = new jasmineUnderTest.MapContaining(containingMap); const matchersUtil = new jasmineUnderTest.MatchersUtil(); expect(containing.asymmetricMatch(actualMap, matchersUtil)).toBe(false); }); it('does not match when a value in sample has no asymmetric matches in actual', function() { const actualMap = new Map([['foo1', 'bar'], ['baz', [1, 2, 3, 4]]]); const containingMap = new Map([ [jasmineUnderTest.stringMatching(/^foo\d/), 'bar'], ['baz', jasmineUnderTest.arrayContaining([4, 5])] ]); const containing = new jasmineUnderTest.MapContaining(containingMap); const matchersUtil = new jasmineUnderTest.MatchersUtil(); expect(containing.asymmetricMatch(actualMap, matchersUtil)).toBe(false); }); it('matches recursively', function() { const actualMap = new Map([ ['foo', new Map([['foo1', 1], ['foo2', 2]])], [new Map([[1, 'bar1'], [2, 'bar2']]), 'bar'], ['other', 'any'] ]); const containingMap = new Map([ ['foo', new jasmineUnderTest.MapContaining(new Map([['foo1', 1]]))], [new jasmineUnderTest.MapContaining(new Map([[2, 'bar2']])), 'bar'] ]); const containing = new jasmineUnderTest.MapContaining(containingMap); const matchersUtil = new jasmineUnderTest.MatchersUtil(); expect(containing.asymmetricMatch(actualMap, matchersUtil)).toBe(true); }); it('uses custom equality testers', function() { function tester(a, b) { // treat all negative numbers as equal return typeof a == 'number' && typeof b == 'number' ? a < 0 && b < 0 : a === b; } const actualMap = new Map([['foo', -1]]); const containing = new jasmineUnderTest.MapContaining( new Map([['foo', -2]]) ); const matchersUtil = new jasmineUnderTest.MatchersUtil({ customTesters: [tester] }); expect(containing.asymmetricMatch(actualMap, matchersUtil)).toBe(true); }); it('does not match when actual is not a map', function() { const containingMap = new Map([['foo', 'bar']]); expect( new jasmineUnderTest.MapContaining(containingMap).asymmetricMatch('foo') ).toBe(false); expect( new jasmineUnderTest.MapContaining(containingMap).asymmetricMatch(-1) ).toBe(false); expect( new jasmineUnderTest.MapContaining(containingMap).asymmetricMatch({ foo: 'bar' }) ).toBe(false); }); it('throws an error when sample is not a map', function() { expect(function() { new jasmineUnderTest.MapContaining({ foo: 'bar' }).asymmetricMatch( new Map() ); }).toThrowError(/You must provide a map/); }); it('defines a `jasmineToString` method', function() { const sample = new Map(), containing = new jasmineUnderTest.MapContaining(sample), pp = jasmine.createSpy('pp').and.returnValue('sample'); expect(containing.jasmineToString(pp)).toEqual( '' ); expect(pp).toHaveBeenCalledWith(sample); }); }); jasmine-4.5.0/spec/core/asymmetric_equality/NotEmptySpec.js000066400000000000000000000031741432731766000240700ustar00rootroot00000000000000describe('NotEmpty', function() { it('matches a non empty object', function() { const notEmpty = new jasmineUnderTest.NotEmpty(); expect(notEmpty.asymmetricMatch({ undefined: false })).toBe(true); expect(notEmpty.asymmetricMatch({})).toBe(false); }); it('matches a non empty array', function() { const notEmpty = new jasmineUnderTest.NotEmpty(); expect(notEmpty.asymmetricMatch([1, 12, 3])).toBe(true); expect(notEmpty.asymmetricMatch([])).toBe(false); }); it('matches a non empty string', function() { const notEmpty = new jasmineUnderTest.NotEmpty(); expect(notEmpty.asymmetricMatch('12312')).toBe(true); expect(notEmpty.asymmetricMatch('')).toBe(false); expect(notEmpty.asymmetricMatch('')).toBe(false); }); it('matches a non empty map', function() { const notEmpty = new jasmineUnderTest.NotEmpty(); const fullMap = new Map(); fullMap.set('one', 1); const emptyMap = new Map(); expect(notEmpty.asymmetricMatch(fullMap)).toBe(true); expect(notEmpty.asymmetricMatch(emptyMap)).toBe(false); }); it('matches a non empty set', function() { const notEmpty = new jasmineUnderTest.NotEmpty(); const filledSet = new Set(); filledSet.add(1); const emptySet = new Set(); expect(notEmpty.asymmetricMatch(filledSet)).toBe(true); expect(notEmpty.asymmetricMatch(emptySet)).toBe(false); }); it('matches a non empty typed array', function() { const notEmpty = new jasmineUnderTest.NotEmpty(); expect(notEmpty.asymmetricMatch(new Int16Array([1, 2, 3]))).toBe(true); expect(notEmpty.asymmetricMatch(new Int16Array())).toBe(false); }); }); jasmine-4.5.0/spec/core/asymmetric_equality/ObjectContainingSpec.js000066400000000000000000000151601432731766000255270ustar00rootroot00000000000000describe('ObjectContaining', function() { it('matches any object actual to an empty object', function() { const containing = new jasmineUnderTest.ObjectContaining({}); const matchersUtil = new jasmineUnderTest.MatchersUtil(); expect(containing.asymmetricMatch({ foo: 1 }, matchersUtil)).toBe(true); }); it('does not match when the actual is not an object', function() { const containing = new jasmineUnderTest.ObjectContaining({}); [1, true, undefined, 'a string'].forEach(function(actual) { expect(containing.asymmetricMatch(actual)).toBe(false); }); }); it('does not match an empty object actual', function() { const containing = new jasmineUnderTest.ObjectContaining('foo'); expect(function() { containing.asymmetricMatch({}); }).toThrowError(/not 'foo'/); }); it('matches when the key/value pair is present in the actual', function() { const containing = new jasmineUnderTest.ObjectContaining({ foo: 'fooVal' }); const matchersUtil = new jasmineUnderTest.MatchersUtil(); expect( containing.asymmetricMatch({ foo: 'fooVal', bar: 'barVal' }, matchersUtil) ).toBe(true); }); it('does not match when the key/value pair is not present in the actual', function() { const containing = new jasmineUnderTest.ObjectContaining({ foo: 'fooVal' }); const matchersUtil = new jasmineUnderTest.MatchersUtil(); expect( containing.asymmetricMatch( { bar: 'barVal', quux: 'quuxVal' }, matchersUtil ) ).toBe(false); }); it('does not match when the key is present but the value is different in the actual', function() { const containing = new jasmineUnderTest.ObjectContaining({ foo: 'other' }); const matchersUtil = new jasmineUnderTest.MatchersUtil(); expect( containing.asymmetricMatch({ foo: 'fooVal', bar: 'barVal' }, matchersUtil) ).toBe(false); }); it("jasmineToString's itself", function() { const sample = {}, matcher = new jasmineUnderTest.ObjectContaining(sample), pp = jasmine.createSpy('pp').and.returnValue('sample'); expect(matcher.jasmineToString(pp)).toEqual( '' ); expect(pp).toHaveBeenCalledWith(sample); }); it('matches recursively', function() { const containing = new jasmineUnderTest.ObjectContaining({ one: new jasmineUnderTest.ObjectContaining({ two: {} }) }); const matchersUtil = new jasmineUnderTest.MatchersUtil(); expect(containing.asymmetricMatch({ one: { two: {} } }, matchersUtil)).toBe( true ); }); it('matches when key is present with undefined value', function() { const containing = new jasmineUnderTest.ObjectContaining({ one: undefined }); const matchersUtil = new jasmineUnderTest.MatchersUtil(); expect(containing.asymmetricMatch({ one: undefined }, matchersUtil)).toBe( true ); }); it('does not match when key with undefined value is not present', function() { const containing = new jasmineUnderTest.ObjectContaining({ one: undefined }); const matchersUtil = new jasmineUnderTest.MatchersUtil(); expect(containing.asymmetricMatch({}, matchersUtil)).toBe(false); }); it('matches defined properties', function() { const containing = new jasmineUnderTest.ObjectContaining({ foo: 'fooVal' }); const matchersUtil = new jasmineUnderTest.MatchersUtil(); const definedPropertyObject = {}; Object.defineProperty(definedPropertyObject, 'foo', { get: function() { return 'fooVal'; } }); expect( containing.asymmetricMatch(definedPropertyObject, matchersUtil) ).toBe(true); }); it('matches prototype properties', function() { const containing = new jasmineUnderTest.ObjectContaining({ foo: 'fooVal' }); const matchersUtil = new jasmineUnderTest.MatchersUtil(); const prototypeObject = { foo: 'fooVal' }; const obj = Object.create(prototypeObject); expect(containing.asymmetricMatch(obj, matchersUtil)).toBe(true); }); it('uses custom equality testers', function() { const tester = function(a, b) { // All "foo*" strings match each other. if ( typeof a == 'string' && typeof b == 'string' && a.slice(0, 3) == 'foo' && b.slice(0, 3) == 'foo' ) { return true; } }; const containing = new jasmineUnderTest.ObjectContaining({ foo: 'fooVal' }); const matchersUtil = new jasmineUnderTest.MatchersUtil({ customTesters: [tester] }); expect(containing.asymmetricMatch({ foo: 'fooBar' }, matchersUtil)).toBe( true ); }); describe('valuesForDiff_', function() { describe('when other is not an object', function() { it('sets self to jasmineToString()', function() { const containing = new jasmineUnderTest.ObjectContaining({}), pp = jasmineUnderTest.makePrettyPrinter(), result = containing.valuesForDiff_('a', pp); expect(result).toEqual({ self: '', other: 'a' }); }); }); describe('when other is an object', function() { it('includes keys that are present in both other and sample', function() { const sample = { a: 1, b: 2 }, other = { a: 3, b: 4 }, containing = new jasmineUnderTest.ObjectContaining(sample), result = containing.valuesForDiff_(other); expect(result.self).not.toBeInstanceOf( jasmineUnderTest.ObjectContaining ); expect(result).toEqual({ self: sample, other: other }); }); it('includes keys that are present only in sample', function() { const sample = { a: 1, b: 2 }, other = { a: 3 }, containing = new jasmineUnderTest.ObjectContaining(sample), result = containing.valuesForDiff_(other); expect(result.self).not.toBeInstanceOf( jasmineUnderTest.ObjectContaining ); expect(containing.valuesForDiff_(other)).toEqual({ self: sample, other: { a: 3, b: undefined } }); }); it('omits keys that are present only in other', function() { const sample = { a: 1, b: 2 }, other = { a: 3, b: 4, c: 5 }, containing = new jasmineUnderTest.ObjectContaining(sample), result = containing.valuesForDiff_(other); expect(result.self).not.toBeInstanceOf( jasmineUnderTest.ObjectContaining ); expect(result).toEqual({ self: sample, other: { a: 3, b: 4 } }); }); }); }); }); jasmine-4.5.0/spec/core/asymmetric_equality/SetContainingSpec.js000066400000000000000000000101441432731766000250510ustar00rootroot00000000000000describe('SetContaining', function() { it('matches any actual set to an empty set', function() { const actualSet = new Set(['foo', 'bar']); const containing = new jasmineUnderTest.SetContaining(new Set()); expect(containing.asymmetricMatch(actualSet)).toBe(true); }); it('matches when all the values in sample have matches in actual', function() { const actualSet = new Set([{ foo: 'bar' }, 'baz', [1, 2, 3]]); const containingSet = new Set([[1, 2, 3], { foo: 'bar' }]); const containing = new jasmineUnderTest.SetContaining(containingSet); const matchersUtil = new jasmineUnderTest.MatchersUtil(); expect(containing.asymmetricMatch(actualSet, matchersUtil)).toBe(true); }); it('does not match when a value is not in actual', function() { const actualSet = new Set([{ foo: 'bar' }, 'baz', [1, 2, 3]]); const containingSet = new Set([[1, 2], { foo: 'bar' }]); const containing = new jasmineUnderTest.SetContaining(containingSet); const matchersUtil = new jasmineUnderTest.MatchersUtil(); expect(containing.asymmetricMatch(actualSet, matchersUtil)).toBe(false); }); it('matches when all the values in sample have asymmetric matches in actual', function() { const actualSet = new Set([[1, 2, 3, 4], 'other', 'foo1']); const containingSet = new Set([ jasmineUnderTest.stringMatching(/^foo\d/), jasmineUnderTest.arrayContaining([2, 3]) ]); const containing = new jasmineUnderTest.SetContaining(containingSet); const matchersUtil = new jasmineUnderTest.MatchersUtil(); expect(containing.asymmetricMatch(actualSet, matchersUtil)).toBe(true); }); it('does not match when a value in sample has no asymmetric matches in actual', function() { const actualSet = new Set(['a-foo1', [1, 2, 3, 4], 'other']); const containingSet = new Set([ jasmine.stringMatching(/^foo\d/), jasmine.arrayContaining([2, 3]) ]); const containing = new jasmineUnderTest.SetContaining(containingSet); const matchersUtil = new jasmineUnderTest.MatchersUtil(); expect(containing.asymmetricMatch(actualSet, matchersUtil)).toBe(false); }); it('matches recursively', function() { const actualSet = new Set(['foo', new Set([1, 'bar', 2]), 'other']); const containingSet = new Set([ new jasmineUnderTest.SetContaining(new Set(['bar'])), 'foo' ]); const containing = new jasmineUnderTest.SetContaining(containingSet); const matchersUtil = new jasmineUnderTest.MatchersUtil(); expect(containing.asymmetricMatch(actualSet, matchersUtil)).toBe(true); }); it('uses custom equality testers', function() { function tester(a, b) { // treat all negative numbers as equal return typeof a == 'number' && typeof b == 'number' ? a < 0 && b < 0 : a === b; } const actualSet = new Set(['foo', -1]); const containing = new jasmineUnderTest.SetContaining(new Set([-2, 'foo'])); const matchersUtil = new jasmineUnderTest.MatchersUtil({ customTesters: [tester] }); expect(containing.asymmetricMatch(actualSet, matchersUtil)).toBe(true); }); it('does not match when actual is not a set', function() { const containingSet = new Set(['foo']); expect( new jasmineUnderTest.SetContaining(containingSet).asymmetricMatch('foo') ).toBe(false); expect( new jasmineUnderTest.SetContaining(containingSet).asymmetricMatch(1) ).toBe(false); expect( new jasmineUnderTest.SetContaining(containingSet).asymmetricMatch(['foo']) ).toBe(false); }); it('throws an error when sample is not a set', function() { expect(function() { new jasmineUnderTest.SetContaining({ foo: 'bar' }).asymmetricMatch( new Set() ); }).toThrowError(/You must provide a set/); }); it('defines a `jasmineToString` method', function() { const sample = new Set(), containing = new jasmineUnderTest.SetContaining(sample), pp = jasmine.createSpy('pp').and.returnValue('sample'); expect(containing.jasmineToString(pp)).toEqual( '' ); expect(pp).toHaveBeenCalledWith(sample); }); }); jasmine-4.5.0/spec/core/asymmetric_equality/StringContainingSpec.js000066400000000000000000000016271432731766000255720ustar00rootroot00000000000000describe('StringContaining', function() { it('searches for a provided substring when the expected is a String', function() { const matcher = new jasmineUnderTest.StringContaining('foo'); expect(matcher.asymmetricMatch('barfoobaz')).toBe(true); expect(matcher.asymmetricMatch('barbaz')).toBe(false); }); it('raises an Error when the expected is not a String', function() { expect(function() { new jasmineUnderTest.StringContaining(/foo/); }).toThrowError(/not a String/); }); it('fails when the actual is not a String', function() { const matcher = new jasmineUnderTest.StringContaining('x'); expect(matcher.asymmetricMatch(['x'])).toBe(false); }); it("jasmineToString's itself", function() { const matching = new jasmineUnderTest.StringContaining('foo'); expect(matching.jasmineToString()).toEqual( '' ); }); }); jasmine-4.5.0/spec/core/asymmetric_equality/StringMatchingSpec.js000066400000000000000000000017211432731766000252260ustar00rootroot00000000000000describe('StringMatching', function() { it('matches a string against a provided regexp', function() { const matcher = new jasmineUnderTest.StringMatching(/foo/); expect(matcher.asymmetricMatch('barfoobaz')).toBe(true); expect(matcher.asymmetricMatch('barbaz')).toBe(false); }); it('matches a string against provided string', function() { const matcher = new jasmineUnderTest.StringMatching('foo'); expect(matcher.asymmetricMatch('barfoobaz')).toBe(true); expect(matcher.asymmetricMatch('barbaz')).toBe(false); }); it('raises an Error when the expected is not a String or RegExp', function() { expect(function() { new jasmineUnderTest.StringMatching({}); }).toThrowError(/not a String or a RegExp/); }); it("jasmineToString's itself", function() { const matching = new jasmineUnderTest.StringMatching(/^foo/); expect(matching.jasmineToString()).toEqual( '' ); }); }); jasmine-4.5.0/spec/core/asymmetric_equality/TruthySpec.js000066400000000000000000000034021432731766000236020ustar00rootroot00000000000000describe('Truthy', function() { it('is true for a non empty string', function() { const truthy = new jasmineUnderTest.Truthy(); expect(truthy.asymmetricMatch('foo')).toBe(true); expect(truthy.asymmetricMatch('')).toBe(false); }); it('is true for a number that is not 0', function() { const truthy = new jasmineUnderTest.Truthy(); expect(truthy.asymmetricMatch(1)).toBe(true); expect(truthy.asymmetricMatch(0)).toBe(false); expect(truthy.asymmetricMatch(-23)).toBe(true); expect(truthy.asymmetricMatch(-3.1)).toBe(true); }); it('is true for a function', function() { const truthy = new jasmineUnderTest.Truthy(); expect(truthy.asymmetricMatch(function() {})).toBe(true); }); it('is true for an Object', function() { const truthy = new jasmineUnderTest.Truthy(); expect(truthy.asymmetricMatch({})).toBe(true); }); it('is true for a truthful Boolean', function() { const truthy = new jasmineUnderTest.Truthy(); expect(truthy.asymmetricMatch(true)).toBe(true); expect(truthy.asymmetricMatch(false)).toBe(false); }); it('is true for an empty object', function() { const truthy = new jasmineUnderTest.Truthy(); expect(truthy.asymmetricMatch({})).toBe(true); }); it('is true for an empty array', function() { const truthy = new jasmineUnderTest.Truthy(); expect(truthy.asymmetricMatch([])).toBe(true); }); it('is true for a date', function() { const truthy = new jasmineUnderTest.Truthy(); expect(truthy.asymmetricMatch(new Date())).toBe(true); }); it('is true for a infiniti', function() { const truthy = new jasmineUnderTest.Truthy(); expect(truthy.asymmetricMatch(Infinity)).toBe(true); expect(truthy.asymmetricMatch(-Infinity)).toBe(true); }); }); jasmine-4.5.0/spec/core/baseSpec.js000066400000000000000000000150471432731766000171330ustar00rootroot00000000000000describe('base helpers', function() { describe('isError_', function() { it('correctly handles WebSocket events', function(done) { if (typeof jasmine.getGlobal().WebSocket === 'undefined') { done(); return; } const obj = (function() { const sock = new WebSocket('ws://localhost'); let event; sock.onerror = function(e) { event = e; }; return function() { return event; }; })(); let left = 20; const int = setInterval(function() { if (obj() || left === 0) { const result = jasmineUnderTest.isError_(obj()); expect(result).toBe(false); clearInterval(int); done(); } else { left--; } }, 100); }); it('returns true for an Error subclass', function() { function MyError() {} MyError.prototype = new Error(); expect(jasmineUnderTest.isError_(new MyError())).toBe(true); }); it('returns true for an un-thrown Error with no message in this environment', function() { expect(jasmineUnderTest.isError_(new Error())).toBe(true); }); it('returns true for an Error that originated from another frame', function() { if (typeof window === 'undefined') { pending('This test only runs in browsers.'); } const iframe = document.createElement('iframe'); iframe.style.display = 'none'; document.body.appendChild(iframe); try { const error = iframe.contentWindow.eval('new Error()'); expect(jasmineUnderTest.isError_(error)).toBe(true); } finally { document.body.removeChild(iframe); } }); it('returns false for a falsy value', function() { expect(jasmineUnderTest.isError_(undefined)).toBe(false); }); it('returns false for a non-Error object', function() { expect(jasmineUnderTest.isError_({})).toBe(false); }); }); describe('isAsymmetricEqualityTester_', function() { it('returns false when the argument is falsy', function() { expect(jasmineUnderTest.isAsymmetricEqualityTester_(null)).toBe(false); }); it('returns false when the argument does not have a asymmetricMatch property', function() { const obj = {}; expect(jasmineUnderTest.isAsymmetricEqualityTester_(obj)).toBe(false); }); it("returns false when the argument's asymmetricMatch is not a function", function() { const obj = { asymmetricMatch: 'yes' }; expect(jasmineUnderTest.isAsymmetricEqualityTester_(obj)).toBe(false); }); it("returns true when the argument's asymmetricMatch is a function", function() { const obj = { asymmetricMatch: function() {} }; expect(jasmineUnderTest.isAsymmetricEqualityTester_(obj)).toBe(true); }); }); describe('isSet', function() { it('returns true when the object is a Set', function() { expect(jasmineUnderTest.isSet(new Set())).toBe(true); }); it('returns false when the object is not a Set', function() { expect(jasmineUnderTest.isSet({})).toBe(false); }); }); describe('isURL', function() { it('returns true when the object is a URL', function() { expect(jasmineUnderTest.isURL(new URL('http://localhost/'))).toBe(true); }); it('returns false when the object is not a URL', function() { expect(jasmineUnderTest.isURL({})).toBe(false); }); }); describe('isIterable_', function() { it('returns true when the object is an Array', function() { expect(jasmineUnderTest.isIterable_([])).toBe(true); }); it('returns true when the object is a Set', function() { expect(jasmineUnderTest.isIterable_(new Set())).toBe(true); }); it('returns true when the object is a Map', function() { expect(jasmineUnderTest.isIterable_(new Map())).toBe(true); }); it('returns true when the object implements @@iterator', function() { const myIterable = { [Symbol.iterator]: function() {} }; expect(jasmineUnderTest.isIterable_(myIterable)).toBe(true); }); it('returns false when the object does not implement @@iterator', function() { expect(jasmineUnderTest.isIterable_({})).toBe(false); }); }); describe('isPending_', function() { it('returns a promise that resolves to true when the promise is pending', function() { const promise = new Promise(function() {}); return expectAsync(jasmineUnderTest.isPending_(promise)).toBeResolvedTo( true ); }); it('returns a promise that resolves to false when the promise is resolved', function() { const promise = Promise.resolve(); return expectAsync(jasmineUnderTest.isPending_(promise)).toBeResolvedTo( false ); }); it('returns a promise that resolves to false when the promise is rejected', function() { const promise = Promise.reject(); return expectAsync(jasmineUnderTest.isPending_(promise)).toBeResolvedTo( false ); }); }); describe('DEFAULT_TIMEOUT_INTERVAL setter', function() { const max = 2147483647; beforeEach(function() { this.initialValue = jasmineUnderTest.DEFAULT_TIMEOUT_INTERVAL; }); afterEach(function() { jasmineUnderTest.DEFAULT_TIMEOUT_INTERVAL = this.initialValue; }); it('accepts only values <= ' + max, function() { expect(function() { jasmineUnderTest.DEFAULT_TIMEOUT_INTERVAL = max + 1; }).toThrowError( 'jasmine.DEFAULT_TIMEOUT_INTERVAL cannot be greater than ' + max ); jasmineUnderTest.DEFAULT_TIMEOUT_INTERVAL = max; expect(jasmineUnderTest.DEFAULT_TIMEOUT_INTERVAL).toEqual(max); }); it('is consistent with setTimeout in this environment', function(done) { const f1 = jasmine.createSpy('setTimeout callback for ' + max), f2 = jasmine.createSpy('setTimeout callback for ' + (max + 1)); // Suppress printing of TimeoutOverflowWarning in node spyOn(console, 'error'); let id = setTimeout(f1, max); setTimeout(function() { clearTimeout(id); expect(f1).not.toHaveBeenCalled(); id = setTimeout(f2, max + 1); setTimeout(function() { clearTimeout(id); expect(f2).toHaveBeenCalled(); done(); }); }); }); }); describe('debugLog', function() { it("forwards to the current env's debugLog function", function() { spyOn(jasmineUnderTest.getEnv(), 'debugLog'); jasmineUnderTest.debugLog('a message'); expect(jasmineUnderTest.getEnv().debugLog).toHaveBeenCalledWith( 'a message' ); }); }); }); jasmine-4.5.0/spec/core/buildExpectationResultSpec.js000066400000000000000000000063651432731766000227260ustar00rootroot00000000000000describe('buildExpectationResult', function() { it('defaults to passed', function() { const result = jasmineUnderTest.buildExpectationResult({ passed: 'some-value' }); expect(result.passed).toBe('some-value'); }); it('message defaults to Passed for passing specs', function() { const result = jasmineUnderTest.buildExpectationResult({ passed: true, message: 'some-value' }); expect(result.message).toBe('Passed.'); }); it('message returns the message for failing expectations', function() { const result = jasmineUnderTest.buildExpectationResult({ passed: false, message: 'some-value' }); expect(result.message).toBe('some-value'); }); describe('When the error property is provided', function() { it('sets the message to the formatted error', function() { const result = jasmineUnderTest.buildExpectationResult({ passed: false, error: { message: 'foo', fileName: 'somefile.js' } }); expect(result.message).toEqual('foo in somefile.js'); }); it('delegates stack formatting to the provided formatter', function() { const result = jasmineUnderTest.buildExpectationResult({ passed: false, error: { stack: 'foo', extra: 'wombat' } }); expect(result.stack).toEqual( "error properties: Object({ extra: 'wombat' })\nfoo" ); }); }); describe('When the errorForStack property is provided', function() { it('builds the stack trace using errorForStack instead of Error', function() { const result = jasmineUnderTest.buildExpectationResult({ passed: false, errorForStack: { stack: 'foo' }, error: { stack: 'bar' } }); expect(result.stack).toEqual('bar'); }); }); it('matcherName returns passed matcherName', function() { const result = jasmineUnderTest.buildExpectationResult({ matcherName: 'some-value' }); expect(result.matcherName).toBe('some-value'); }); it('expected returns passed expected', function() { const result = jasmineUnderTest.buildExpectationResult({ expected: 'some-value' }); expect(result.expected).toBe('some-value'); }); it('actual returns passed actual', function() { const result = jasmineUnderTest.buildExpectationResult({ actual: 'some-value' }); expect(result.actual).toBe('some-value'); }); it('handles nodejs assertions', function() { if (typeof require === 'undefined') { return; } const assert = require('assert'); const value = 8421; const expectedValue = 'JasmineExpectationTestValue'; let error; try { assert.equal(value, expectedValue); } catch (e) { error = e; } expect(error.code).toEqual('ERR_ASSERTION'); expect(error.actual).toEqual(value); expect(error.expected).toEqual(expectedValue); expect(error.operator).toEqual('=='); const result = jasmineUnderTest.buildExpectationResult({ passed: false, matcherName: '', expected: '', actual: '', error: error }); expect(result.code).toEqual('ERR_ASSERTION'); expect(result.actual).toEqual(value); expect(result.expected).toEqual(expectedValue); expect(result.matcherName).toEqual('assert =='); }); }); jasmine-4.5.0/spec/core/formatErrorMsgSpec.js000066400000000000000000000011131432731766000211570ustar00rootroot00000000000000describe('formatErrorMsg', function() { it('should format an error with a domain', function() { const formator = jasmineUnderTest.formatErrorMsg('api'); expect(formator('message')).toBe('api : message'); expect(formator('message2')).toBe('api : message2'); }); it('should format an error with a domain and usage', function() { const formator = jasmineUnderTest.formatErrorMsg('api', 'with a param'); expect(formator('message')).toBe('api : message\nUsage: with a param'); expect(formator('message2')).toBe('api : message2\nUsage: with a param'); }); }); jasmine-4.5.0/spec/core/integration/000077500000000000000000000000001432731766000173645ustar00rootroot00000000000000jasmine-4.5.0/spec/core/integration/AsymmetricEqualityTestersSpec.js000066400000000000000000000140671432731766000257520ustar00rootroot00000000000000describe('Asymmetric equality testers (Integration)', function() { function verifyPasses(expectations) { it('passes', async function() { const env = new jasmineUnderTest.Env(); env.it('a spec', function() { expectations(env); }); const reporter = jasmine.createSpyObj('reporter', ['specDone']); env.addReporter(reporter); await env.execute(); expect(reporter.specDone).toHaveBeenCalledTimes(1); const result = reporter.specDone.calls.argsFor(0)[0]; expect(result.status).toEqual('passed'); expect(result.passedExpectations.length) .withContext('Number of passed expectations') .toEqual(1); expect(result.failedExpectations.length) .withContext('Number of failed expectations') .toEqual(0); expect( result.failedExpectations[0] && result.failedExpectations[0].message ) .withContext('Failure message') .toBeUndefined(); }); } function verifyFails(expectations) { it('fails', async function() { const env = new jasmineUnderTest.Env(); env.it('a spec', function() { expectations(env); }); const reporter = jasmine.createSpyObj('reporter', ['specDone']); env.addReporter(reporter); await env.execute(); expect(reporter.specDone).toHaveBeenCalledTimes(1); const result = reporter.specDone.calls.argsFor(0)[0]; expect(result.status).toEqual('failed'); expect(result.failedExpectations.length) .withContext('Number of failed expectations') .toEqual(1); expect(result.failedExpectations[0].message) .withContext('Failed with a thrown error rather than a matcher failure') .not.toMatch(/^Error: /); expect(result.failedExpectations[0].matcherName) .withContext('Matcher name') .not.toEqual(''); }); } describe('any', function() { verifyPasses(function(env) { env.expect(5).toEqual(jasmineUnderTest.any(Number)); }); verifyFails(function(env) { env.expect('five').toEqual(jasmineUnderTest.any(Number)); }); }); describe('anything', function() { verifyPasses(function(env) { env.expect('').toEqual(jasmineUnderTest.anything()); }); verifyFails(function(env) { env.expect(null).toEqual(jasmineUnderTest.anything()); }); }); describe('arrayContaining', function() { verifyPasses(function(env) { env.addCustomEqualityTester(function(a, b) { return a.toString() === b.toString(); }); env.expect([1, 2, 3]).toEqual(jasmineUnderTest.arrayContaining(['2'])); }); verifyFails(function(env) { env.expect(null).toEqual(jasmineUnderTest.arrayContaining([2])); }); }); describe('arrayWithExactContents', function() { verifyPasses(function(env) { env.addCustomEqualityTester(function(a, b) { return a.toString() === b.toString(); }); env .expect([1, 2]) .toEqual(jasmineUnderTest.arrayWithExactContents(['2', '1'])); }); verifyFails(function(env) { env.expect([]).toEqual(jasmineUnderTest.arrayWithExactContents([2])); }); }); describe('empty', function() { verifyPasses(function(env) { env.expect([]).toEqual(jasmineUnderTest.empty()); }); verifyFails(function(env) { env.expect([1]).toEqual(jasmineUnderTest.empty()); }); }); describe('falsy', function() { verifyPasses(function(env) { env.expect(false).toEqual(jasmineUnderTest.falsy()); }); verifyFails(function(env) { env.expect(true).toEqual(jasmineUnderTest.falsy()); }); }); describe('mapContaining', function() { verifyPasses(function(env) { const actual = new Map(); actual.set('a', '2'); const expected = new Map(); expected.set('a', 2); env.addCustomEqualityTester(function(a, b) { return a.toString() === b.toString(); }); env.expect(actual).toEqual(jasmineUnderTest.mapContaining(expected)); }); verifyFails(function(env) { env .expect('something') .toEqual(jasmineUnderTest.mapContaining(new Map())); }); }); describe('notEmpty', function() { verifyPasses(function(env) { env.expect([1]).toEqual(jasmineUnderTest.notEmpty()); }); verifyFails(function(env) { env.expect([]).toEqual(jasmineUnderTest.notEmpty()); }); }); describe('objectContaining', function() { verifyPasses(function(env) { env.addCustomEqualityTester(function(a, b) { return a.toString() === b.toString(); }); env .expect({ a: 1, b: 2 }) .toEqual(jasmineUnderTest.objectContaining({ a: '1' })); }); verifyFails(function(env) { env.expect({}).toEqual(jasmineUnderTest.objectContaining({ a: '1' })); }); }); describe('setContaining', function() { verifyPasses(function(env) { const actual = new Set(); actual.add('1'); const expected = new Set(); actual.add(1); env.addCustomEqualityTester(function(a, b) { return a.toString() === b.toString(); }); env.expect(actual).toEqual(jasmineUnderTest.setContaining(expected)); }); verifyFails(function(env) { env .expect('something') .toEqual(jasmineUnderTest.setContaining(new Set())); }); }); describe('stringMatching', function() { verifyPasses(function(env) { env.expect('foo').toEqual(jasmineUnderTest.stringMatching(/o/)); }); verifyFails(function(env) { env.expect('bar').toEqual(jasmineUnderTest.stringMatching(/o/)); }); }); describe('stringContaining', function() { verifyPasses(function(env) { env.expect('foo').toEqual(jasmineUnderTest.stringContaining('o')); }); verifyFails(function(env) { env.expect('bar').toEqual(jasmineUnderTest.stringContaining('o')); }); }); describe('truthy', function() { verifyPasses(function(env) { env.expect(true).toEqual(jasmineUnderTest.truthy()); }); verifyFails(function(env) { env.expect(false).toEqual(jasmineUnderTest.truthy()); }); }); }); jasmine-4.5.0/spec/core/integration/CustomAsyncMatchersSpec.js000066400000000000000000000077771432731766000245160ustar00rootroot00000000000000describe('Custom Async Matchers (Integration)', function() { let env; beforeEach(function() { env = new jasmineUnderTest.Env(); env.configure({ random: false }); }); afterEach(function() { env.cleanup_(); }); it('passes the spec if the custom async matcher passes', async function() { env.it('spec using custom async matcher', function() { env.addAsyncMatchers({ toBeReal: function() { return { compare: function() { return Promise.resolve({ pass: true }); } }; } }); return env.expectAsync(true).toBeReal(); }); const specExpectations = function(result) { expect(result.status).toEqual('passed'); }; env.addReporter({ specDone: specExpectations }); await env.execute(); }); it('uses the negative compare function for a negative comparison, if provided', async function() { env.it('spec with custom negative comparison matcher', function() { env.addAsyncMatchers({ toBeReal: function() { return { compare: function() { return Promise.resolve({ pass: true }); }, negativeCompare: function() { return Promise.resolve({ pass: true }); } }; } }); return env.expectAsync(true).not.toBeReal(); }); const specExpectations = function(result) { expect(result.status).toEqual('passed'); }; env.addReporter({ specDone: specExpectations }); await env.execute(); }); it('generates messages with the same rules as built in matchers absent a custom message', async function() { env.it('spec with an expectation', function() { env.addAsyncMatchers({ toBeReal: function() { return { compare: function() { return Promise.resolve({ pass: false }); } }; } }); return env.expectAsync('a').toBeReal(); }); const specExpectations = function(result) { expect(result.failedExpectations[0].message).toEqual( "Expected 'a' to be real." ); }; env.addReporter({ specDone: specExpectations }); await env.execute(); }); it('passes the jasmine utility to the matcher factory', async function() { const matcherFactory = function() { return { compare: function() { return Promise.resolve({ pass: true }); } }; }, matcherFactorySpy = jasmine.createSpy( 'matcherFactorySpy', matcherFactory ); env.it('spec with expectation', function() { env.addAsyncMatchers({ toBeReal: matcherFactorySpy }); return env.expectAsync(true).toBeReal(); }); const specExpectations = function() { expect(matcherFactorySpy).toHaveBeenCalledWith( jasmine.any(jasmineUnderTest.MatchersUtil) ); }; env.addReporter({ specDone: specExpectations }); await env.execute(); }); it('provides custom equality testers to the matcher factory via matchersUtil', async function() { const matcherFactory = function(matchersUtil) { return { compare: function(actual, expected) { return Promise.resolve({ pass: matchersUtil.equals(actual[0], expected) }); } }; }, customEqualityFn = jasmine .createSpy('customEqualityFn') .and.callFake(function(a, b) { return a.toString() === b; }); env.it('spec with expectation', function() { env.addCustomEqualityTester(customEqualityFn); env.addAsyncMatchers({ toBeArrayWithFirstElement: matcherFactory }); return env.expectAsync([1, 2]).toBeArrayWithFirstElement('1'); }); const specExpectations = function(result) { expect(customEqualityFn).toHaveBeenCalledWith(1, '1'); expect(result.failedExpectations).toEqual([]); }; env.addReporter({ specDone: specExpectations }); await env.execute(); }); }); jasmine-4.5.0/spec/core/integration/CustomMatchersSpec.js000066400000000000000000000175711432731766000235110ustar00rootroot00000000000000describe('Custom Matchers (Integration)', function() { let env; beforeEach(function() { env = new jasmineUnderTest.Env(); env.configure({ random: false }); }); afterEach(function() { env.cleanup_(); }); it('allows adding more matchers local to a spec', async function() { env.it('spec defining a custom matcher', function() { env.addMatchers({ matcherForSpec: function() { return { compare: function(actual, expected) { return { pass: false, message: 'matcherForSpec: actual: ' + actual + '; expected: ' + expected }; } }; } }); env.expect('zzz').matcherForSpec('yyy'); }); env.it('spec without custom matcher defined', function() { expect(env.expect('zzz').matcherForSpec).toBeUndefined(); }); const specDoneSpy = jasmine.createSpy('specDoneSpy'); env.addReporter({ specDone: specDoneSpy }); await env.execute(); const firstSpecResult = specDoneSpy.calls.first().args[0]; expect(firstSpecResult.status).toEqual('failed'); expect(firstSpecResult.failedExpectations[0].message).toEqual( 'matcherForSpec: actual: zzz; expected: yyy' ); }); it('passes the spec if the custom matcher passes', async function() { env.it('spec using custom matcher', function() { env.addMatchers({ toBeReal: function() { return { compare: function() { return { pass: true }; } }; } }); env.expect(true).toBeReal(); }); const reporter = jasmine.createSpyObj('reporter', ['specDone']); env.addReporter(reporter); await env.execute(); expect(reporter.specDone).toHaveBeenCalledTimes(1); const result = reporter.specDone.calls.argsFor(0)[0]; expect(result.status).toEqual('passed'); }); it('passes the spec if the custom equality matcher passes for types nested inside asymmetric equality testers', async function() { env.it('spec using custom equality matcher', function() { const customEqualityFn = function(a, b) { // All "foo*" strings match each other. if ( typeof a == 'string' && typeof b == 'string' && a.slice(0, 3) == 'foo' && b.slice(0, 3) == 'foo' ) { return true; } }; env.addCustomEqualityTester(customEqualityFn); env .expect({ foo: 'fooValue' }) .toEqual(jasmineUnderTest.objectContaining({ foo: 'fooBar' })); env .expect(['fooValue', 'things']) .toEqual(jasmineUnderTest.arrayContaining(['fooBar'])); env .expect(['fooValue']) .toEqual(jasmineUnderTest.arrayWithExactContents(['fooBar'])); }); const reporter = jasmine.createSpyObj('reporter', ['specDone']); env.addReporter(reporter); await env.execute(); expect(reporter.specDone).toHaveBeenCalledTimes(1); const result = reporter.specDone.calls.argsFor(0)[0]; expect(result.status).toEqual('passed'); }); it('displays an appropriate failure message if a custom equality matcher fails', async function() { env.it('spec using custom equality matcher', function() { const customEqualityFn = function(a, b) { // "foo" is not equal to anything if (a === 'foo' || b === 'foo') { return false; } }; env.addCustomEqualityTester(customEqualityFn); env.expect({ foo: 'foo' }).toEqual({ foo: 'foo' }); }); const reporter = jasmine.createSpyObj('reporter', ['specDone']); env.addReporter(reporter); await env.execute(); expect(reporter.specDone).toHaveBeenCalledTimes(1); const result = reporter.specDone.calls.argsFor(0)[0]; expect(result.status).toEqual('failed'); expect(result.failedExpectations[0].message).toEqual( "Expected $.foo = 'foo' to equal 'foo'." ); }); it('uses the negative compare function for a negative comparison, if provided', async function() { env.it('spec with custom negative comparison matcher', function() { env.addMatchers({ toBeReal: function() { return { compare: function() { return { pass: true }; }, negativeCompare: function() { return { pass: true }; } }; } }); env.expect(true).not.toBeReal(); }); const reporter = jasmine.createSpyObj('reporter', ['specDone']); env.addReporter(reporter); await env.execute(); expect(reporter.specDone).toHaveBeenCalledTimes(1); const result = reporter.specDone.calls.argsFor(0)[0]; expect(result.status).toEqual('passed'); }); it('generates messages with the same rules as built in matchers absent a custom message', async function() { env.it('spec with an expectation', function() { env.addMatchers({ toBeReal: function() { return { compare: function() { return { pass: false }; } }; } }); env.expect('a').toBeReal(); }); const reporter = jasmine.createSpyObj('reporter', ['specDone']); env.addReporter(reporter); await env.execute(); expect(reporter.specDone).toHaveBeenCalledTimes(1); const result = reporter.specDone.calls.argsFor(0)[0]; expect(result.failedExpectations[0].message).toEqual( "Expected 'a' to be real." ); }); it('passes the expected and actual arguments to the comparison function', async function() { const argumentSpy = jasmine .createSpy('argument spy') .and.returnValue({ pass: true }); env.it('spec with an expectation', function() { env.addMatchers({ toBeReal: function() { return { compare: argumentSpy }; } }); env.expect(true).toBeReal(); env.expect(true).toBeReal('arg'); env.expect(true).toBeReal('arg1', 'arg2'); }); await env.execute(); expect(argumentSpy).toHaveBeenCalledWith(true); expect(argumentSpy).toHaveBeenCalledWith(true, 'arg'); expect(argumentSpy).toHaveBeenCalledWith(true, 'arg1', 'arg2'); }); it('passes the jasmine utility to the matcher factory', async function() { const matcherFactory = function() { return { compare: function() { return { pass: true }; } }; }, matcherFactorySpy = jasmine .createSpy('matcherFactorySpy') .and.callFake(matcherFactory); env.it('spec with expectation', function() { env.addMatchers({ toBeReal: matcherFactorySpy }); env.expect(true).toBeReal(); }); await env.execute(); expect(matcherFactorySpy).toHaveBeenCalledWith( jasmine.any(jasmineUnderTest.MatchersUtil) ); }); it('provides custom equality testers to the matcher factory via matchersUtil', async function() { const matcherFactory = function(matchersUtil) { return { compare: function(actual, expected) { return { pass: matchersUtil.equals(actual[0], expected) }; } }; }, customEqualityFn = jasmine .createSpy('customEqualityFn') .and.callFake(function(a, b) { return a.toString() === b; }); env.it('spec with expectation', function() { env.addCustomEqualityTester(customEqualityFn); env.addMatchers({ toBeArrayWithFirstElement: matcherFactory }); env.expect([1, 2]).toBeArrayWithFirstElement('1'); }); const reporter = jasmine.createSpyObj('reporter', ['specDone']); env.addReporter(reporter); await env.execute(); expect(reporter.specDone).toHaveBeenCalledTimes(1); const result = reporter.specDone.calls.argsFor(0)[0]; expect(customEqualityFn).toHaveBeenCalledWith(1, '1'); expect(result.failedExpectations).toEqual([]); }); }); jasmine-4.5.0/spec/core/integration/CustomObjectFormatterSpec.js000066400000000000000000000041651432731766000250300ustar00rootroot00000000000000describe('Custom object formatters', function() { let env; beforeEach(function() { env = new jasmineUnderTest.Env(); env.configure({ random: false }); }); it('scopes custom object formatters to a spec', async function() { env.it('a spec with custom pretty-printer', function() { env.addCustomObjectFormatter(function(obj) { return 'custom(' + obj + ')'; }); env.expect(42).toBeUndefined(); }); env.it('a spec without custom pretty-printer', function() { env.expect(42).toBeUndefined(); }); const specResults = []; const specDone = function(result) { specResults.push(result); }; env.addReporter({ specDone: specDone }); await env.execute(); expect(specResults[0].failedExpectations[0].message).toEqual( 'Expected custom(42) to be undefined.' ); expect(specResults[1].failedExpectations[0].message).toEqual( 'Expected 42 to be undefined.' ); }); it('scopes custom object formatters to a suite', async function() { env.it('a spec without custom pretty-printer', function() { env.expect(42).toBeUndefined(); }); env.describe('with custom pretty-printer', function() { env.beforeAll(function() { env.addCustomObjectFormatter(function(obj) { return 'custom(' + obj + ')'; }); }); env.it('a spec', function() { env.expect(42).toBeUndefined(); }); }); const specResults = []; const specDone = function(result) { specResults.push(result); }; env.addReporter({ specDone: specDone }); await env.execute(); expect(specResults[0].failedExpectations[0].message).toEqual( 'Expected 42 to be undefined.' ); expect(specResults[1].failedExpectations[0].message).toEqual( 'Expected custom(42) to be undefined.' ); }); it('throws an exception if you try to add a custom object formatter outside a runable', function() { expect(function() { env.addCustomObjectFormatter(function() {}); }).toThrowError( 'Custom object formatters must be added in a before function or a spec' ); }); }); jasmine-4.5.0/spec/core/integration/CustomSpyStrategiesSpec.js000066400000000000000000000101211432731766000245310ustar00rootroot00000000000000describe('Custom Spy Strategies (Integration)', function() { let env; beforeEach(function() { env = new jasmineUnderTest.Env(); env.configure({ random: false }); }); afterEach(function() { env.cleanup_(); }); it('allows adding more strategies local to a suite', async function() { const plan = jasmine.createSpy('custom strategy plan').and.returnValue(42); const strategy = jasmine.createSpy('custom strategy').and.returnValue(plan); env.describe('suite defining a custom spy strategy', function() { env.beforeAll(function() { env.addSpyStrategy('frobnicate', strategy); }); env.it('spec in the suite', function() { const spy = env.createSpy('something').and.frobnicate(17); expect(spy(1, 2, 3)).toEqual(42); expect(strategy).toHaveBeenCalledWith(17); expect(plan).toHaveBeenCalledWith(1, 2, 3); }); }); env.it('spec without custom strategy defined', function() { expect(env.createSpy('something').and.frobnicate).toBeUndefined(); }); const result = await env.execute(); expect(result.overallStatus).toEqual('passed'); }); it('allows adding more strategies local to a spec', async function() { const plan = jasmine.createSpy('custom strategy plan').and.returnValue(42); const strategy = jasmine.createSpy('custom strategy').and.returnValue(plan); env.it('spec defining a custom spy strategy', function() { env.addSpyStrategy('frobnicate', strategy); const spy = env.createSpy('something').and.frobnicate(17); expect(spy(1, 2, 3)).toEqual(42); expect(strategy).toHaveBeenCalledWith(17); expect(plan).toHaveBeenCalledWith(1, 2, 3); }); env.it('spec without custom strategy defined', function() { expect(env.createSpy('something').and.frobnicate).toBeUndefined(); }); const result = await env.execute(); expect(result.overallStatus).toEqual('passed'); }); it('allows using custom strategies on a per-argument basis', async function() { const plan = jasmine.createSpy('custom strategy plan').and.returnValue(42); const strategy = jasmine.createSpy('custom strategy').and.returnValue(plan); env.it('spec defining a custom spy strategy', function() { env.addSpyStrategy('frobnicate', strategy); const spy = env .createSpy('something') .and.returnValue('no args return') .withArgs(1, 2, 3) .and.frobnicate(17); expect(spy()).toEqual('no args return'); expect(plan).not.toHaveBeenCalled(); expect(spy(1, 2, 3)).toEqual(42); expect(plan).toHaveBeenCalledWith(1, 2, 3); }); env.it('spec without custom strategy defined', function() { expect(env.createSpy('something').and.frobnicate).toBeUndefined(); }); const result = await env.execute(); expect(result.overallStatus).toEqual('passed'); }); it('allows multiple custom strategies to be used', async function() { const plan1 = jasmine.createSpy('plan 1').and.returnValue(42), strategy1 = jasmine.createSpy('strat 1').and.returnValue(plan1), plan2 = jasmine.createSpy('plan 2').and.returnValue(24), strategy2 = jasmine.createSpy('strat 2').and.returnValue(plan2), specDone = jasmine.createSpy('specDone'); env.beforeEach(function() { env.addSpyStrategy('frobnicate', strategy1); env.addSpyStrategy('jiggle', strategy2); }); env.it('frobnicates', function() { plan1.calls.reset(); plan2.calls.reset(); const spy = env.createSpy('spy').and.frobnicate(); expect(spy()).toEqual(42); expect(plan1).toHaveBeenCalled(); expect(plan2).not.toHaveBeenCalled(); }); env.it('jiggles', function() { plan1.calls.reset(); plan2.calls.reset(); const spy = env.createSpy('spy').and.jiggle(); expect(spy()).toEqual(24); expect(plan1).not.toHaveBeenCalled(); expect(plan2).toHaveBeenCalled(); }); env.addReporter({ specDone: specDone }); const result = await env.execute(); expect(result.overallStatus).toEqual('passed'); expect(specDone.calls.count()).toBe(2); }); }); jasmine-4.5.0/spec/core/integration/DefaultSpyStrategySpec.js000066400000000000000000000071151432731766000243440ustar00rootroot00000000000000describe('Default Spy Strategy (Integration)', function() { let env; beforeEach(function() { env = new jasmineUnderTest.Env(); env.configure({ random: false }); }); afterEach(function() { env.cleanup_(); }); it('allows defining a default spy strategy', async function() { env.describe('suite with default strategy', function() { env.beforeEach(function() { env.setDefaultSpyStrategy(function(and) { and.returnValue(42); }); }); env.it('spec in suite', function() { const spy = env.createSpy('something'); expect(spy()).toBe(42); }); }); env.it('spec not in suite', function() { const spy = env.createSpy('something'); expect(spy()).toBeUndefined(); }); const result = await env.execute(); expect(result.overallStatus).toEqual('passed'); }); it('inherits the default spy strategy set in a parent suite', async function() { env.describe('suite with default strategy', function() { env.beforeAll(function() { env.setDefaultSpyStrategy(function(and) { and.returnValue(42); }); }); env.describe('child suite', function() { env.it('spec in suite', function() { const spy = env.createSpy('something'); expect(spy()).toBe(42); }); }); }); let overallStatus; env.addReporter({ jasmineDone: r => (overallStatus = r.overallStatus) }); await env.execute(); expect(overallStatus).toEqual('passed'); }); it('restores the previous default strategy when exiting a runnable', async function() { env.describe('outer suite', function() { env.describe('inner suite', function() { env.beforeAll(function() { env.setDefaultSpyStrategy(function(and) { and.returnValue(42); }); }); env.it('spec in suite', function() { env.setDefaultSpyStrategy(function(and) { and.returnValue(17); }); const spy = env.createSpy('something'); expect(spy()).toBe(17); }); env.afterAll(function() { const spy = env.createSpy('something'); expect(spy()).toBe(42); }); }); env.afterAll(function() { const spy = env.createSpy('something'); expect(spy()).toBeUndefined(); }); }); let overallStatus; env.addReporter({ jasmineDone: r => (overallStatus = r.overallStatus) }); await env.execute(); expect(overallStatus).toEqual('passed'); }); it('uses the default spy strategy defined when the spy is created', async function() { env.it('spec', function() { const a = env.createSpy('a'); env.setDefaultSpyStrategy(function(and) { and.returnValue(42); }); const b = env.createSpy('b'); env.setDefaultSpyStrategy(function(and) { and.stub(); }); const c = env.createSpy('c'); env.setDefaultSpyStrategy(); const d = env.createSpy('d'); expect(a()).toBeUndefined(); expect(b()).toBe(42); expect(c()).toBeUndefined(); expect(d()).toBeUndefined(); // Check our assumptions about which spies are "configured" (this matters because // spies that use withArgs() behave differently if they are not configured). expect(a.and.isConfigured()).toBe(false); expect(b.and.isConfigured()).toBe(true); expect(c.and.isConfigured()).toBe(true); expect(d.and.isConfigured()).toBe(false); }); const result = await env.execute(); expect(result.overallStatus).toEqual('passed'); }); }); jasmine-4.5.0/spec/core/integration/DeprecationSpec.js000066400000000000000000000212051432731766000227720ustar00rootroot00000000000000/* eslint no-console: 0 */ describe('Deprecation (integration)', function() { let env; beforeEach(function() { env = new jasmineUnderTest.Env(); }); afterEach(function() { env.cleanup_(); }); it('reports a deprecation on the top suite', async function() { const reporter = jasmine.createSpyObj('reporter', ['jasmineDone']); env.addReporter(reporter); spyOn(console, 'error'); env.beforeAll(function() { env.deprecated('the message'); }); env.it('a spec', function() {}); await env.execute(); expect(reporter.jasmineDone).toHaveBeenCalledWith( jasmine.objectContaining({ deprecationWarnings: [ jasmine.objectContaining({ message: jasmine.stringMatching(/^the message/) }) ] }) ); expect(console.error).toHaveBeenCalledWith( jasmine.stringMatching(/^DEPRECATION: the message/) ); }); it('reports a deprecation on a descendent suite', async function() { const reporter = jasmine.createSpyObj('reporter', ['suiteDone']); env.addReporter(reporter); spyOn(console, 'error'); env.describe('a suite', function() { env.beforeAll(function() { env.deprecated('the message'); }); env.it('a spec', function() {}); }); await env.execute(); expect(reporter.suiteDone).toHaveBeenCalledWith( jasmine.objectContaining({ deprecationWarnings: [ jasmine.objectContaining({ message: jasmine.stringMatching(/^the message/) }) ] }) ); expect(console.error).toHaveBeenCalledWith( jasmine.stringMatching(/^DEPRECATION: the message \(in suite: a suite\)/) ); }); it('reports a deprecation on a spec', async function() { const reporter = jasmine.createSpyObj('reporter', ['specDone']); env.addReporter(reporter); spyOn(console, 'error'); env.describe('a suite', function() { env.it('a spec', function() { env.deprecated('the message'); }); }); await env.execute(); expect(reporter.specDone).toHaveBeenCalledWith( jasmine.objectContaining({ deprecationWarnings: [ jasmine.objectContaining({ message: jasmine.stringMatching(/^the message/) }) ] }) ); expect(console.error).toHaveBeenCalledWith( jasmine.stringMatching( /^DEPRECATION: the message \(in spec: a suite a spec\)/ ) ); }); it('omits the suite or spec context when ignoreRunnable is true', async function() { const reporter = jasmine.createSpyObj('reporter', ['jasmineDone']); env.addReporter(reporter); spyOn(console, 'error'); env.it('a spec', function() { env.deprecated('the message', { ignoreRunnable: true }); }); await env.execute(); expect(reporter.jasmineDone).toHaveBeenCalledWith( jasmine.objectContaining({ deprecationWarnings: [ jasmine.objectContaining({ message: jasmine.stringMatching(/^the message/) }) ] }) ); expect(console.error).toHaveBeenCalledWith( jasmine.stringMatching(/the message/) ); expect(console.error).not.toHaveBeenCalledWith( jasmine.stringMatching(/a spec/) ); }); it('includes the stack trace', async function() { const reporter = jasmine.createSpyObj('reporter', ['specDone']); env.addReporter(reporter); spyOn(console, 'error'); env.describe('a suite', function() { env.it('a spec', function() { env.deprecated('the message'); }); }); await env.execute(); expect(reporter.specDone).toHaveBeenCalledWith( jasmine.objectContaining({ deprecationWarnings: [ jasmine.objectContaining({ stack: jasmine.stringMatching(/DeprecationSpec.js/) }) ] }) ); expect(console.error).toHaveBeenCalled(); expect(console.error.calls.argsFor(0)[0].replace(/\n/g, 'NL')).toMatch( /^DEPRECATION: the message \(in spec: a suite a spec\)NL.*DeprecationSpec.js/ ); }); it('excludes the stack trace when omitStackTrace is true', async function() { const reporter = jasmine.createSpyObj('reporter', ['specDone']); env.addReporter(reporter); spyOn(console, 'error'); env.describe('a suite', function() { env.it('a spec', function() { env.deprecated('the message', { omitStackTrace: true }); }); }); await env.execute(); expect(reporter.specDone).toHaveBeenCalledWith( jasmine.objectContaining({ deprecationWarnings: [ jasmine.objectContaining({ stack: jasmine.falsy() }) ] }) ); expect(console.error).toHaveBeenCalled(); expect(console.error).not.toHaveBeenCalledWith( jasmine.stringMatching(/DeprecationSpec.js/) ); }); it('emits a given deprecation only once', async function() { const reporter = jasmine.createSpyObj('reporter', [ 'specDone', 'suiteDone' ]); env.addReporter(reporter); spyOn(console, 'error'); env.describe('a suite', function() { env.beforeAll(function() { env.deprecated('the message'); env.deprecated('the message'); }); env.it('a spec', function() { env.deprecated('the message'); env.deprecated('a different message'); }); }); await env.execute(); expect(reporter.suiteDone).toHaveBeenCalledWith( jasmine.objectContaining({ deprecationWarnings: [ // only one jasmine.objectContaining({ message: jasmine.stringMatching(/^the message/) }) ] }) ); expect(reporter.specDone).toHaveBeenCalledWith( jasmine.objectContaining({ deprecationWarnings: [ // only the other one jasmine.objectContaining({ message: jasmine.stringMatching(/^a different message/) }) ] }) ); expect(console.error).toHaveBeenCalledTimes(2); expect(console.error).toHaveBeenCalledWith( jasmine.stringMatching(/^DEPRECATION: the message \(in suite: a suite\)/) ); expect(console.error).toHaveBeenCalledWith( jasmine.stringMatching( /^DEPRECATION: a different message \(in spec: a suite a spec\)/ ) ); }); it('emits a given deprecation each time when config.verboseDeprecations is true', async function() { const reporter = jasmine.createSpyObj('reporter', [ 'specDone', 'suiteDone' ]); env.addReporter(reporter); spyOn(console, 'error'); env.configure({ verboseDeprecations: true }); env.describe('a suite', function() { env.beforeAll(function() { env.deprecated('the message'); env.deprecated('the message'); }); env.it('a spec', function() { env.deprecated('the message'); }); }); await env.execute(); expect(reporter.suiteDone).toHaveBeenCalledWith( jasmine.objectContaining({ deprecationWarnings: [ jasmine.objectContaining({ message: jasmine.stringMatching(/^the message/) }), jasmine.objectContaining({ message: jasmine.stringMatching(/^the message/) }) ] }) ); expect(reporter.specDone).toHaveBeenCalledWith( jasmine.objectContaining({ deprecationWarnings: [ jasmine.objectContaining({ message: jasmine.stringMatching(/^the message/) }) ] }) ); expect(console.error).toHaveBeenCalledTimes(3); expect(console.error.calls.argsFor(0)[0]).toMatch( /^DEPRECATION: the message \(in suite: a suite\)/ ); expect(console.error.calls.argsFor(1)[0]).toMatch( /^DEPRECATION: the message \(in suite: a suite\)/ ); expect(console.error.calls.argsFor(2)[0]).toMatch( /^DEPRECATION: the message \(in spec: a suite a spec\)/ ); expect(console.error.calls.argsFor(2)[0]).toMatch( /^DEPRECATION: the message \(in spec: a suite a spec\)/ ); }); it('handles deprecations that occur before execute() is called', async function() { const reporter = jasmine.createSpyObj('reporter', ['jasmineDone']); env.addReporter(reporter); spyOn(console, 'error'); env.deprecated('the message'); env.it('a spec', function() {}); await env.execute(); expect(reporter.jasmineDone).toHaveBeenCalledWith( jasmine.objectContaining({ deprecationWarnings: [ jasmine.objectContaining({ message: jasmine.stringMatching(/^the message/) }) ] }) ); expect(console.error).toHaveBeenCalledWith( jasmine.stringMatching(/^DEPRECATION: the message/) ); }); }); jasmine-4.5.0/spec/core/integration/EnvSpec.js000066400000000000000000003413601432731766000212740ustar00rootroot00000000000000describe('Env integration', function() { let env; beforeEach(function() { jasmine.getEnv().registerIntegrationMatchers(); env = new jasmineUnderTest.Env(); }); afterEach(function() { env.cleanup_(); }); it('Suites execute as expected (no nesting)', async function() { const calls = []; env.configure({ random: false }); env.describe('A Suite', function() { env.it('with a spec', function() { calls.push('with a spec'); }); env.it('and another spec', function() { calls.push('and another spec'); }); }); await env.execute(); expect(calls).toEqual(['with a spec', 'and another spec']); }); it('Nested Suites execute as expected', async function() { const calls = []; env.configure({ random: false }); env.describe('Outer suite', function() { env.it('an outer spec', function() { calls.push('an outer spec'); }); env.describe('Inner suite', function() { env.it('an inner spec', function() { calls.push('an inner spec'); }); env.it('another inner spec', function() { calls.push('another inner spec'); }); }); }); await env.execute(); expect(calls).toEqual([ 'an outer spec', 'an inner spec', 'another inner spec' ]); }); it('Multiple top-level Suites execute as expected', async function() { const calls = []; env.configure({ random: false }); env.describe('Outer suite', function() { env.it('an outer spec', function() { calls.push('an outer spec'); }); env.describe('Inner suite', function() { env.it('an inner spec', function() { calls.push('an inner spec'); }); env.it('another inner spec', function() { calls.push('another inner spec'); }); }); }); env.describe('Another outer suite', function() { env.it('a 2nd outer spec', function() { calls.push('a 2nd outer spec'); }); }); await env.execute(); expect(calls).toEqual([ 'an outer spec', 'an inner spec', 'another inner spec', 'a 2nd outer spec' ]); }); it('explicitly fails a spec', async function() { const specDone = jasmine.createSpy('specDone'); env.addReporter({ specDone: specDone }); env.describe('failing', function() { env.it('has a default message', function() { env.fail(); }); env.it('specifies a message', function() { env.fail('messy message'); }); env.it('has a message and stack trace from an Error', function() { env.fail(new Error('error message')); }); env.it('pretty prints objects', function() { env.fail({ prop: 'value', arr: ['works', true] }); }); }); await env.execute(); expect(specDone).toHaveBeenCalledWith( jasmine.objectContaining({ description: 'has a default message', failedExpectations: [ jasmine.objectContaining({ message: 'Failed' }) ] }) ); expect(specDone).toHaveBeenCalledWith( jasmine.objectContaining({ description: 'specifies a message', failedExpectations: [ jasmine.objectContaining({ message: 'Failed: messy message' }) ] }) ); expect(specDone).toHaveBeenCalledWith( jasmine.objectContaining({ description: 'has a message and stack trace from an Error', failedExpectations: [ jasmine.objectContaining({ message: 'Failed: error message', stack: { asymmetricMatch: function(other) { const split = other.split('\n'); let firstLine = split[0]; if (firstLine.indexOf('error message') >= 0) { // Chrome inserts the message and a newline before the first stacktrace line. firstLine = split[1]; } return firstLine.indexOf('EnvSpec') >= 0; } } }) ] }) ); expect(specDone).toHaveBeenCalledWith( jasmine.objectContaining({ description: 'pretty prints objects', failedExpectations: [ jasmine.objectContaining({ message: "Failed: Object({ prop: 'value', arr: [ 'works', true ] })" }) ] }) ); }); it("produces an understandable error message when 'fail' is used outside of a current spec", async function() { env.describe('A Suite', function() { env.it('an async spec that is actually synchronous', function( underTestCallback ) { underTestCallback(); }); expect(function() { env.fail(); }).toThrowError(/'fail' was used when there was no current spec/); }); await env.execute(); }); it("calls associated befores/specs/afters with the same 'this'", async function() { env.configure({ random: false }); env.describe('tests', function() { let firstTimeThrough = true; let firstSpecContext; let secondSpecContext; env.beforeEach(function() { if (firstTimeThrough) { firstSpecContext = this; } else { secondSpecContext = this; } expect(this).toEqual(new jasmineUnderTest.UserContext()); }); env.it('sync spec', function() { expect(this).toBe(firstSpecContext); }); env.it('another sync spec', function() { expect(this).toBe(secondSpecContext); }); env.afterEach(function() { if (firstTimeThrough) { expect(this).toBe(firstSpecContext); firstTimeThrough = false; } else { expect(this).toBe(secondSpecContext); } }); }); await env.execute(); }); it("calls associated befores/its/afters with the same 'this' for an async spec", async function() { env.describe('with an async spec', function() { let specContext; env.beforeEach(function() { specContext = this; expect(this).toEqual(new jasmineUnderTest.UserContext()); }); env.it('sync spec', function(underTestCallback) { expect(this).toBe(specContext); underTestCallback(); }); env.afterEach(function() { expect(this).toBe(specContext); }); }); await env.execute(); }); it('calls associated beforeAlls/afterAlls only once per suite', async function() { const before = jasmine.createSpy('beforeAll'), after = jasmine.createSpy('afterAll'); env.describe('with beforeAll and afterAll', function() { env.it('spec', function() { expect(before).toHaveBeenCalled(); expect(after).not.toHaveBeenCalled(); }); env.it('another spec', function() { expect(before).toHaveBeenCalled(); expect(after).not.toHaveBeenCalled(); }); env.beforeAll(before); env.afterAll(after); }); await env.execute(); expect(after).toHaveBeenCalled(); expect(after.calls.count()).toBe(1); expect(before.calls.count()).toBe(1); }); it('calls associated beforeAlls/afterAlls only once per suite for async', async function() { const before = jasmine.createSpy('beforeAll'), after = jasmine.createSpy('afterAll'); env.describe('with beforeAll and afterAll', function() { env.it('spec', function() { expect(before).toHaveBeenCalled(); expect(after).not.toHaveBeenCalled(); }); env.it('another spec', function() { expect(before).toHaveBeenCalled(); expect(after).not.toHaveBeenCalled(); }); env.beforeAll(function(beforeCallbackUnderTest) { before(); beforeCallbackUnderTest(); }); env.afterAll(function(afterCallbackUnderTest) { after(); afterCallbackUnderTest(); }); }); await env.execute(); expect(after).toHaveBeenCalled(); expect(after.calls.count()).toBe(1); expect(before.calls.count()).toBe(1); }); it("calls associated beforeAlls/afterAlls with the cascaded 'this'", async function() { env.describe('with beforeAll and afterAll', function() { env.beforeAll(function() { this.x = 1; }); env.it('has an x at the root', function() { expect(this.x).toBe(1); }); env.describe('child that deletes', function() { env.beforeAll(function() { expect(this.x).toBe(1); delete this.x; }); env.it('has no x', function() { expect(this.x).not.toBeDefined(); }); }); env.describe('child should still have x', function() { env.beforeAll(function(innerDone) { expect(this.x).toBe(1); innerDone(); }); env.it('has an x', function() { expect(this.x).toBe(1); delete this.x; }); env.it('still has an x', function() { expect(this.x).toBe(1); }); env.it('adds a y', function() { this.y = 2; expect(this.y).toBe(2); }); env.it("doesn't have y that was added in sibling", function() { expect(this.y).not.toBeDefined(); }); }); }); await env.execute(); }); it('tags top-level afterAll failures with a type', async function() { const jasmineDone = jasmine.createSpy('jasmineDone'); env.it('has a spec', function() {}); env.afterAll(function() { throw 'nope'; }); env.addReporter({ jasmineDone: jasmineDone }); await env.execute(); const result = jasmineDone.calls.argsFor(0)[0]; expect(result.failedExpectations[0].globalErrorType).toEqual('afterAll'); }); it('does not tag suite afterAll failures with a type', async function() { const reporter = { suiteDone: jasmine.createSpy('suiteDone').and.callFake(function(result) { expect(result.failedExpectations[0].globalErrorType).toBeFalsy(); }) }; env.addReporter(reporter); env.describe('a suite', function() { env.it('has a spec', function() {}); env.afterAll(function() { throw 'nope'; }); }); await env.execute(); expect(reporter.suiteDone).toHaveBeenCalled(); }); it('when the beforeAll fails, error at suite level', async function() { const reporter = jasmine.createSpyObj('fakeReporter', [ 'specDone', 'suiteDone' ]); env.addReporter(reporter); env.describe('A suite', function() { env.beforeAll(function() { env.expect(1).toBe(2); }); env.it('spec that will pass', function() {}); env.describe('nesting', function() { env.it('another spec to pass', function() {}); }); }); await env.execute(); expect(reporter.specDone.calls.count()).toEqual(2); expect(reporter.specDone).toHaveFailedExpectationsForRunnable( 'A suite spec that will pass', [] ); expect(reporter.specDone).toHaveFailedExpectationsForRunnable( 'A suite nesting another spec to pass', [] ); expect(reporter.suiteDone).toHaveFailedExpectationsForRunnable('A suite', [ 'Expected 1 to be 2.' ]); }); describe('Handling async errors', function() { it('routes async errors to a running spec', async function() { const global = { ...browserEventMethods(), setTimeout: function(fn, delay) { return setTimeout(fn, delay); }, clearTimeout: function(fn, delay) { clearTimeout(fn, delay); }, queueMicrotask: function(fn) { queueMicrotask(fn); } }; spyOn(jasmineUnderTest, 'getGlobal').and.returnValue(global); env.cleanup_(); env = new jasmineUnderTest.Env(); const reporter = jasmine.createSpyObj('fakeReporter', [ 'specDone', 'suiteDone' ]); env.addReporter(reporter); env.describe('A suite', function() { env.it('fails', function(specDone) { setTimeout(function() { global.onerror('fail'); specDone(); }); }); }); await env.execute(); expect(reporter.specDone).toHaveFailedExpectationsForRunnable( 'A suite fails', ['fail thrown'] ); }); describe('When the running spec has reported specDone', function() { it('routes async errors to an ancestor suite', async function() { const global = { ...browserEventMethods(), setTimeout: function(fn, delay) { return setTimeout(fn, delay); }, clearTimeout: function(fn) { clearTimeout(fn); }, queueMicrotask: function(fn) { queueMicrotask(fn); } }; spyOn(jasmineUnderTest, 'getGlobal').and.returnValue(global); const realClearStack = jasmineUnderTest.getClearStack(global); const clearStackCallbacks = {}; let clearStackCallCount = 0; spyOn(jasmineUnderTest, 'getClearStack').and.returnValue(function(fn) { clearStackCallCount++; if (clearStackCallbacks[clearStackCallCount]) { clearStackCallbacks[clearStackCallCount](); } realClearStack(fn); }); env.cleanup_(); env = new jasmineUnderTest.Env(); let suiteErrors = []; env.addReporter({ suiteDone: function(result) { const messages = result.failedExpectations.map(e => e.message); suiteErrors = suiteErrors.concat(messages); }, specDone: function() { clearStackCallbacks[clearStackCallCount + 1] = function() { global.onerror('fail at the end of the reporter queue'); }; clearStackCallbacks[clearStackCallCount + 2] = function() { global.onerror('fail at the end of the spec queue'); }; } }); env.describe('A suite', function() { env.it('is finishing when the failure occurs', function() {}); }); await env.execute(); expect(suiteErrors).toEqual([ 'fail at the end of the reporter queue thrown', 'fail at the end of the spec queue thrown' ]); }); }); it('routes async errors to a running suite', async function() { const global = { ...browserEventMethods(), setTimeout: function(fn, delay) { return setTimeout(fn, delay); }, clearTimeout: function(fn, delay) { clearTimeout(fn, delay); }, queueMicrotask: function(fn) { queueMicrotask(fn); } }; spyOn(jasmineUnderTest, 'getGlobal').and.returnValue(global); env.cleanup_(); env = new jasmineUnderTest.Env(); const reporter = jasmine.createSpyObj('fakeReporter', [ 'specDone', 'suiteDone' ]); env.addReporter(reporter); env.fdescribe('A suite', function() { env.it('fails', function(specDone) { setTimeout(function() { specDone(); queueMicrotask(function() { queueMicrotask(function() { global.onerror('fail'); }); }); }); }); }); env.describe('Ignored', function() { env.it('is not run', function() {}); }); await env.execute(); expect(reporter.specDone).not.toHaveFailedExpectationsForRunnable( 'A suite fails', ['fail thrown'] ); expect(reporter.suiteDone).toHaveFailedExpectationsForRunnable( 'A suite', ['fail thrown'] ); }); describe('When the running suite has reported suiteDone', function() { it('routes async errors to an ancestor suite', async function() { const global = { ...browserEventMethods(), setTimeout: function(fn, delay) { return setTimeout(fn, delay); }, clearTimeout: function(fn, delay) { clearTimeout(fn, delay); }, queueMicrotask: function(fn) { queueMicrotask(fn); } }; spyOn(jasmineUnderTest, 'getGlobal').and.returnValue(global); const realClearStack = jasmineUnderTest.getClearStack(global); const clearStackCallbacks = {}; let clearStackCallCount = 0; spyOn(jasmineUnderTest, 'getClearStack').and.returnValue(function(fn) { clearStackCallCount++; if (clearStackCallbacks[clearStackCallCount]) { clearStackCallbacks[clearStackCallCount](); } realClearStack(fn); }); env.cleanup_(); env = new jasmineUnderTest.Env(); let suiteErrors = []; env.addReporter({ suiteDone: function(result) { const messages = result.failedExpectations.map(e => e.message); suiteErrors = suiteErrors.concat(messages); if (result.description === 'A nested suite') { clearStackCallbacks[clearStackCallCount + 1] = function() { global.onerror('fail at the end of the reporter queue'); }; clearStackCallbacks[clearStackCallCount + 2] = function() { global.onerror('fail at the end of the suite queue'); }; } } }); env.describe('A suite', function() { env.describe('A nested suite', function() { env.it('a spec', function() {}); }); }); await env.execute(); expect(suiteErrors).toEqual([ 'fail at the end of the reporter queue thrown', 'fail at the end of the suite queue thrown' ]); }); }); describe('When the env has started reporting jasmineDone', function() { it('logs the error to the console', async function() { const global = { ...browserEventMethods(), setTimeout: function(fn, delay) { return setTimeout(fn, delay); }, clearTimeout: function(fn, delay) { clearTimeout(fn, delay); }, queueMicrotask: function(fn) { queueMicrotask(fn); } }; spyOn(jasmineUnderTest, 'getGlobal').and.returnValue(global); env.cleanup_(); env = new jasmineUnderTest.Env(); spyOn(console, 'error'); env.addReporter({ jasmineDone: function() { global.onerror('a very late error'); } }); env.it('a spec', function() {}); await env.execute(); /* eslint-disable-next-line no-console */ expect(console.error).toHaveBeenCalledWith( 'Jasmine received a result after the suite finished:' ); /* eslint-disable-next-line no-console */ expect(console.error).toHaveBeenCalledWith( jasmine.objectContaining({ message: 'a very late error thrown', globalErrorType: 'afterAll' }) ); }); }); it('routes all errors that occur during stack clearing somewhere', async function() { const global = { ...browserEventMethods(), setTimeout: function(fn, delay) { return setTimeout(fn, delay); }, clearTimeout: function(fn) { clearTimeout(fn); }, queueMicrotask: function(fn) { queueMicrotask(fn); } }; spyOn(jasmineUnderTest, 'getGlobal').and.returnValue(global); const realClearStack = jasmineUnderTest.getClearStack(global); let clearStackCallCount = 0; let jasmineDone = false; const expectedErrors = []; const expectedErrorsAfterJasmineDone = []; spyOn(jasmineUnderTest, 'getClearStack').and.returnValue(function(fn) { clearStackCallCount++; const msg = `Error in clearStack #${clearStackCallCount}`; if (jasmineDone) { expectedErrorsAfterJasmineDone.push(`${msg} thrown`); } else { expectedErrors.push(`${msg} thrown`); } global.onerror(msg); realClearStack(fn); }); spyOn(console, 'error'); env.cleanup_(); env = new jasmineUnderTest.Env(); const receivedErrors = []; function logErrors(event) { for (const failure of event.failedExpectations) { receivedErrors.push(failure.message); } } env.addReporter({ specDone: logErrors, suiteDone: logErrors, jasmineDone: function(event) { jasmineDone = true; logErrors(event); } }); env.describe('A suite', function() { env.it('is finishing when the failure occurs', function() {}); }); await env.execute(); expect(receivedErrors.length).toEqual(expectedErrors.length); for (const e of expectedErrors) { expect(receivedErrors).toContain(e); } for (const message of expectedErrorsAfterJasmineDone) { /* eslint-disable-next-line no-console */ expect(console.error).toHaveBeenCalledWith( jasmine.objectContaining({ message }) ); } }); }); it('reports multiple calls to done in the top suite as errors', async function() { const reporter = jasmine.createSpyObj('fakeReporter', ['jasmineDone']); const message = 'A top-level beforeAll or afterAll function called its ' + "'done' callback more than once."; env.addReporter(reporter); env.beforeAll(function(innerDone) { innerDone(); innerDone(); }); env.it('a spec, so the beforeAll runs', function() {}); env.afterAll(function(innerDone) { innerDone(); innerDone(); }); await env.execute(); expect(reporter.jasmineDone).toHaveBeenCalled(); const errors = reporter.jasmineDone.calls.argsFor(0)[0].failedExpectations; expect(errors.length).toEqual(2); expect(errors[0].message) .withContext('top beforeAll') .toContain(message); expect(errors[0].globalErrorType).toEqual('lateError'); expect(errors[1].message) .withContext('top afterAll') .toContain(message); expect(errors[1].globalErrorType).toEqual('lateError'); }); it('reports multiple calls to done in a non-top suite as errors', async function() { const reporter = jasmine.createSpyObj('fakeReporter', [ 'jasmineDone', 'suiteDone' ]); const message = "An asynchronous beforeAll or afterAll function called its 'done' " + 'callback more than once.\n(in suite: a suite)'; let lateDone; reporter.suiteDone.and.callFake(function() { lateDone(); }); env.addReporter(reporter); env.describe('a suite', function() { env.beforeAll(function(innerDone) { innerDone(); innerDone(); }); env.it('a spec, so that before/afters run', function() {}); env.afterAll(function(innerDone) { innerDone(); innerDone(); lateDone = innerDone; }); }); await env.execute(); expect(reporter.suiteDone).toHaveBeenCalled(); const suiteErrors = reporter.suiteDone.calls.argsFor(0)[0] .failedExpectations; expect(suiteErrors.length).toEqual(2); expect(suiteErrors[0].message) .withContext('suite beforeAll') .toContain(message); expect(suiteErrors[1].message) .withContext('suite afterAll') .toContain(message); expect(reporter.jasmineDone).toHaveBeenCalled(); const topErrors = reporter.jasmineDone.calls.argsFor(0)[0] .failedExpectations; expect(topErrors.length).toEqual(1); expect(topErrors[0].message) .withContext('late suite afterAll') .toContain(message); expect(topErrors[0].globalErrorType).toEqual('lateError'); expect(topErrors[0].globalErrorType).toEqual('lateError'); }); it('reports multiple calls to done in a spec as errors', async function() { const reporter = jasmine.createSpyObj('fakeReporter', [ 'specDone', 'suiteDone', 'jasmineDone' ]); const message = 'An asynchronous spec, beforeEach, or afterEach function called its ' + "'done' callback more than once.\n(in spec: a suite a spec)"; let lateDone; reporter.specDone.and.callFake(function() { lateDone(); }); reporter.suiteDone.and.callFake(function() { lateDone(); }); env.addReporter(reporter); env.describe('a suite', function() { env.beforeEach(function(innerDone) { innerDone(); innerDone(); }); env.it('a spec', function(innerDone) { innerDone(); innerDone(); }); env.afterEach(function(innerDone) { innerDone(); innerDone(); lateDone = innerDone; }); }); await env.execute(); expect(reporter.specDone).toHaveBeenCalled(); const specErrors = reporter.specDone.calls.argsFor(0)[0].failedExpectations; expect(specErrors.length).toEqual(3); expect(specErrors[0].message) .withContext('error caused by beforeEach') .toContain(message); expect(specErrors[1].message) .withContext('error caused by it') .toContain(message); expect(specErrors[2].message) .withContext('error caused by afterEach') .toContain(message); const suiteErrors = reporter.suiteDone.calls.argsFor(0)[0] .failedExpectations; expect(suiteErrors.length).toEqual(1); expect(suiteErrors[0].message) .withContext('late error caused by afterEach') .toContain(message); const topErrors = reporter.jasmineDone.calls.argsFor(0)[0] .failedExpectations; expect(topErrors.length).toEqual(1); expect(topErrors[0].message) .withContext('really late error caused by afterEach') .toContain(message); expect(topErrors[0].globalErrorType).toEqual('lateError'); }); it('reports multiple calls to done in reporters as errors', async function() { const message = "An asynchronous reporter callback called its 'done' callback more " + 'than once.'; const reporter = jasmine.createSpyObj('fakeReport', ['jasmineDone']); reporter.specDone = function(result, done) { done(); done(); }; env.addReporter(reporter); env.it('a spec', function() {}); await env.execute(); expect(reporter.jasmineDone).toHaveBeenCalled(); const errors = reporter.jasmineDone.calls.argsFor(0)[0].failedExpectations; expect(errors.length).toEqual(1); expect(errors[0].message).toContain(message); expect(errors[0].globalErrorType).toEqual('lateError'); }); it('does not report an error for a call to done that comes after a timeout', async function() { const reporter = jasmine.createSpyObj('fakeReporter', ['jasmineDone']); let firstSpecDone; reporter.specDone = function(result, reporterDone) { setTimeout(function() { firstSpecDone(); reporterDone(); }); }; env.addReporter(reporter); env.it( 'a spec', function(innerDone) { firstSpecDone = innerDone; }, 1 ); await env.execute(); expect(reporter.jasmineDone).toHaveBeenCalledWith( jasmine.objectContaining({ failedExpectations: [] }) ); }); describe('suiteDone reporting', function() { it('reports when an afterAll fails an expectation', async function() { const reporter = jasmine.createSpyObj('fakeReport', ['suiteDone']); env.addReporter(reporter); env.describe('my suite', function() { env.it('my spec', function() {}); env.afterAll(function() { env.expect(1).toEqual(2); env.expect(2).toEqual(3); }); }); await env.execute(); expect(reporter.suiteDone).toHaveFailedExpectationsForRunnable( 'my suite', ['Expected 1 to equal 2.', 'Expected 2 to equal 3.'] ); }); it('if there are no specs, it still reports correctly', async function() { const reporter = jasmine.createSpyObj('fakeReport', ['suiteDone']); env.addReporter(reporter); env.describe('outer suite', function() { env.describe('inner suite', function() { env.it('spec', function() {}); }); env.afterAll(function() { env.expect(1).toEqual(2); env.expect(2).toEqual(3); }); }); await env.execute(); expect(reporter.suiteDone).toHaveFailedExpectationsForRunnable( 'outer suite', ['Expected 1 to equal 2.', 'Expected 2 to equal 3.'] ); }); it('reports when afterAll throws an exception', async function() { const error = new Error('After All Exception'), reporter = jasmine.createSpyObj('fakeReport', ['suiteDone']); env.addReporter(reporter); env.describe('my suite', function() { env.it('my spec', function() {}); env.afterAll(function() { throw error; }); }); await env.execute(); expect(reporter.suiteDone).toHaveFailedExpectationsForRunnable( 'my suite', [/^Error: After All Exception/] ); }); it('reports when an async afterAll fails an expectation', async function() { const reporter = jasmine.createSpyObj('fakeReport', ['suiteDone']); env.addReporter(reporter); env.describe('my suite', function() { env.it('my spec', function() {}); env.afterAll(function(afterAllDone) { env.expect(1).toEqual(2); afterAllDone(); }); }); await env.execute(); expect(reporter.suiteDone).toHaveFailedExpectationsForRunnable( 'my suite', ['Expected 1 to equal 2.'] ); }); it('reports when an async afterAll throws an exception', async function() { const error = new Error('After All Exception'), reporter = jasmine.createSpyObj('fakeReport', ['suiteDone']); env.addReporter(reporter); env.describe('my suite', function() { env.it('my spec', function() {}); // eslint-disable-next-line no-unused-vars env.afterAll(function(afterAllDone) { throw error; }); }); await env.execute(); expect(reporter.suiteDone).toHaveFailedExpectationsForRunnable( 'my suite', [/^Error: After All Exception/] ); }); it('reports the duration of the suite', async function() { let duration; env.addReporter({ suiteDone: function(result) { expect(duration).toBeUndefined(); duration = result.duration; } }); env.describe('my suite', function() { env.it('takes time', function(done) { // We can't just use the mock clock here because the timer is designed // to record real time even when the mock clock is installed. setTimeout(done, 10); }); }); await env.execute(); // Expect > 0 to compensate for clock imprecision expect(duration).toBeGreaterThan(0); }); }); describe('specDone reporting', function() { it('reports the duration of the spec', async function() { let duration; env.addReporter({ specDone: function(result) { expect(duration).toBeUndefined(); duration = result.duration; } }); env.describe('my suite', function() { env.it('takes time', function(done) { // We can't just use the mock clock here because the timer is designed // to record real time even when the mock clock is installed. setTimeout(done, 10); }); }); await env.execute(); // Expect > 0 to compensate for clock imprecision expect(duration).toBeGreaterThan(0); }); }); it('reports expectation failures in global beforeAll', async function() { const reporter = jasmine.createSpyObj(['specDone', 'jasmineDone']); env.beforeAll(function() { env.expect(1).toBe(0); }); env.it('is a spec', function() { env.expect(true).toBe(true); }); env.addReporter(reporter); await env.execute(); const results = reporter.jasmineDone.calls.argsFor(0)[0]; expect(results.failedExpectations).toEqual([ jasmine.objectContaining({ message: 'Expected 1 to be 0.' }) ]); expect(reporter.specDone).toHaveFailedExpectationsForRunnable( 'is a spec', [] ); }); it('reports expectation failures in global afterAll', async function() { const reporter = jasmine.createSpyObj(['jasmineDone']); env.afterAll(function() { env.expect(1).toBe(0); }); env.it('is a spec', function() { env.expect(true).toBe(true); }); env.addReporter(reporter); await env.execute(); const results = reporter.jasmineDone.calls.argsFor(0)[0]; expect(results.failedExpectations).toEqual([ jasmine.objectContaining({ message: 'Expected 1 to be 0.' }) ]); }); it('Allows specifying which specs and suites to run', async function() { const calls = [], suiteCallback = jasmine.createSpy('suite callback'); let firstSpec; let secondSuite; env.addReporter({ suiteDone: suiteCallback }); env.describe('first suite', function() { firstSpec = env.it('first spec', function() { calls.push('first spec'); }); env.it('second spec', function() { calls.push('second spec'); }); }); secondSuite = env.describe('second suite', function() { env.it('third spec', function() { calls.push('third spec'); }); }); await env.execute([secondSuite.id, firstSpec.id]); expect(calls).toEqual(['third spec', 'first spec']); expect(suiteCallback).toHaveBeenCalled(); }); it('runs before and after all functions for runnables provided to .execute()', async function() { const calls = []; let first_spec; let second_spec; env.describe('first suite', function() { env.beforeAll(function() { calls.push('before'); }); env.afterAll(function() { calls.push('after'); }); first_spec = env.it('spec', function() { calls.push('first spec'); }); second_spec = env.it('spec 2', function() { calls.push('second spec'); }); }); await env.execute([first_spec.id, second_spec.id]); expect(calls).toEqual(['before', 'first spec', 'second spec', 'after']); }); it('Allows filtering out specs and suites to run programmatically', async function() { const calls = [], suiteCallback = jasmine.createSpy('suite callback'); env.addReporter({ suiteDone: suiteCallback }); env.describe('first suite', function() { env.it('first spec', function() { calls.push('first spec'); }); env.it('second spec', function() { calls.push('second spec'); }); }); env.describe('second suite', function() { env.it('third spec', function() { calls.push('third spec'); }); }); env.configure({ specFilter: function(spec) { return /^first suite/.test(spec.getFullName()); } }); await env.execute(); expect(calls.length).toEqual(2); expect(calls).toEqual( jasmine.arrayContaining(['first spec', 'second spec']) ); expect(suiteCallback).toHaveBeenCalled(); }); it('Functions can be spied on and have their calls tracked', async function() { let originalFunctionWasCalled = false; const subject = { spiedFunc: function() { originalFunctionWasCalled = true; return 'original result'; } }; env.it('works with spies', function() { const spy = env .spyOn(subject, 'spiedFunc') .and.returnValue('stubbed result'); expect(subject.spiedFunc).toEqual(spy); expect(subject.spiedFunc.calls.any()).toEqual(false); expect(subject.spiedFunc.calls.count()).toEqual(0); subject.spiedFunc('foo'); expect(subject.spiedFunc.calls.any()).toEqual(true); expect(subject.spiedFunc.calls.count()).toEqual(1); expect(subject.spiedFunc.calls.mostRecent().args).toEqual(['foo']); expect(subject.spiedFunc.calls.mostRecent().object).toEqual(subject); expect(subject.spiedFunc.calls.mostRecent().returnValue).toEqual( 'stubbed result' ); expect(originalFunctionWasCalled).toEqual(false); subject.spiedFunc.and.callThrough(); subject.spiedFunc('bar'); expect(subject.spiedFunc.calls.count()).toEqual(2); expect(subject.spiedFunc.calls.mostRecent().args).toEqual(['bar']); expect(subject.spiedFunc.calls.mostRecent().returnValue).toEqual( 'original result' ); expect(originalFunctionWasCalled).toEqual(true); }); env.it( 'works with constructors when using callThrough spy strategy', function() { function MyClass(foo) { if (!(this instanceof MyClass)) throw new Error('You must use the new keyword.'); this.foo = foo; } const subject = { MyClass: MyClass }; const spy = env.spyOn(subject, 'MyClass').and.callThrough(); expect(function() { const result = new spy('hello world'); expect(result instanceof MyClass).toBeTruthy(); expect(result.foo).toEqual('hello world'); }).not.toThrow(); expect(function() { const result = new spy( 'passing', 'extra', 'arguments', 'to', 'constructor' ); expect(result instanceof MyClass).toBeTruthy(); expect(result.foo).toEqual('passing'); }).not.toThrow(); expect(function() { spy('hello world'); }).toThrowError('You must use the new keyword.'); } ); await env.execute(); }); it('can be configured to allow respying on functions', async function() { const foo = { bar: function() { return 1; } }; env.allowRespy(true); env.describe('test suite', function() { env.it('spec 0', function() { env.spyOn(foo, 'bar'); expect(function() { env.spyOn(foo, 'bar'); }).not.toThrow(); }); }); await env.execute(); }); it('removes all spies added in a spec after the spec is complete', async function() { const originalFoo = function() {}, testObj = { foo: originalFoo }, firstSpec = jasmine.createSpy('firstSpec').and.callFake(function() { env.spyOn(testObj, 'foo'); }), secondSpec = jasmine.createSpy('secondSpec').and.callFake(function() { expect(testObj.foo).toBe(originalFoo); }); env.describe('test suite', function() { env.it('spec 0', firstSpec); env.it('spec 1', secondSpec); }); env.configure({ random: false }); await env.execute(); expect(firstSpec).toHaveBeenCalled(); expect(secondSpec).toHaveBeenCalled(); }); it('removes all spies added in a suite after the suite is complete', async function() { const originalFoo = function() {}, testObj = { foo: originalFoo }; env.describe('test suite', function() { env.beforeAll(function() { env.spyOn(testObj, 'foo'); }); env.it('spec 0', function() { expect(jasmineUnderTest.isSpy(testObj.foo)).toBe(true); }); env.it('spec 1', function() { expect(jasmineUnderTest.isSpy(testObj.foo)).toBe(true); }); }); env.describe('another suite', function() { env.it('spec 2', function() { expect(jasmineUnderTest.isSpy(testObj.foo)).toBe(false); }); }); await env.execute(); }); it('removes a spy from the top suite after the run is complete', async function() { const originalFoo = function() {}, testObj = { foo: originalFoo }; env.beforeAll(function() { env.spyOn(testObj, 'foo'); }); env.it('spec', function() { expect(jasmineUnderTest.isSpy(testObj.foo)).toBe(true); }); await env.execute(); expect(jasmineUnderTest.isSpy(testObj.foo)).toBe(false); }); it('Mock clock can be installed and used in tests', async function() { const globalSetTimeout = jasmine .createSpy('globalSetTimeout') .and.callFake(function(cb, t) { return setTimeout(cb, t); }), delayedFunctionForGlobalClock = jasmine.createSpy( 'delayedFunctionForGlobalClock' ), delayedFunctionForMockClock = jasmine.createSpy( 'delayedFunctionForMockClock' ); env.cleanup_(); env = new jasmineUnderTest.Env({ global: { setTimeout: globalSetTimeout, clearTimeout: clearTimeout, queueMicrotask: function(fn) { queueMicrotask(fn); } } }); env.configure({ random: false }); env.describe('tests', function() { env.it('test with mock clock', function() { env.clock.install(); env.clock.setTimeout(delayedFunctionForMockClock, 100); env.clock.tick(100); env.clock.uninstall(); }); env.it('test without mock clock', function() { env.clock.setTimeout(delayedFunctionForGlobalClock, 100); }); }); expect(globalSetTimeout).not.toHaveBeenCalled(); expect(delayedFunctionForMockClock).not.toHaveBeenCalled(); await env.execute(); expect(delayedFunctionForMockClock).toHaveBeenCalled(); expect(globalSetTimeout).toHaveBeenCalledWith( delayedFunctionForGlobalClock, 100 ); }); it('should run async specs in order, waiting for them to complete', async function() { let mutatedVar; env.describe('tests', function() { env.beforeEach(function() { mutatedVar = 2; }); env.it('async spec', function(underTestCallback) { setTimeout(function() { expect(mutatedVar).toEqual(2); underTestCallback(); }, 0); }); env.it('after async spec', function() { mutatedVar = 3; }); }); await env.execute(); }); describe('with a mock clock', function() { let realSetTimeout; function createMockedEnv() { env.cleanup_(); // explicitly pass in timing functions so we can make sure that clear stack always works // no matter how long the suite in the spec is env = new jasmineUnderTest.Env({ global: { setTimeout: function(cb, t) { const stack = jasmine.util.errorWithStack().stack; if (stack.indexOf('ClearStack') >= 0) { return realSetTimeout(cb, t); } else { return setTimeout(cb, t); } }, clearTimeout: clearTimeout, setInterval: setInterval, clearInterval: clearInterval, queueMicrotask: function(fn) { queueMicrotask(fn); } } }); } beforeEach(function() { this.originalTimeout = jasmineUnderTest.DEFAULT_TIMEOUT_INTERVAL; realSetTimeout = setTimeout; jasmine.clock().install(); }); afterEach(function() { jasmine.clock().tick(1); jasmine.clock().tick(1); jasmine.clock().uninstall(); jasmineUnderTest.DEFAULT_TIMEOUT_INTERVAL = this.originalTimeout; }); it("should wait a default interval before failing specs that haven't called done yet", async function() { createMockedEnv(); const reporter = jasmine.createSpyObj('fakeReporter', ['specDone']); reporter.specDone.and.callFake(function(result) { expect(result).toEqual(jasmine.objectContaining({ status: 'failed' })); realSetTimeout(function() { jasmine.clock().tick(1); }, 0); }); env.addReporter(reporter); jasmineUnderTest.DEFAULT_TIMEOUT_INTERVAL = 8414; // eslint-disable-next-line no-unused-vars env.it("async spec that doesn't call done", function(underTestCallback) { env.expect(true).toBeTruthy(); jasmine.clock().tick(8416); jasmine.clock().tick(1); }); await env.execute(); expect(reporter.specDone.calls.count()).toEqual(1); jasmine.clock().tick(1); await new Promise(resolve => realSetTimeout(resolve)); }); it('should not use the mock clock for asynchronous timeouts', async function() { createMockedEnv(); const reporter = jasmine.createSpyObj('fakeReporter', ['specDone']), clock = env.clock; reporter.specDone.and.callFake(function() { realSetTimeout(function() { jasmine.debugLog('Ticking after specDone'); jasmine.clock().tick(1); }, 0); }); env.addReporter(reporter); jasmineUnderTest.DEFAULT_TIMEOUT_INTERVAL = 500; env.beforeAll(function() { clock.install(); }); env.afterAll(function() { clock.uninstall(); }); env.it('spec that should not time out', function(innerDone) { clock.tick(1000); expect(true).toEqual(true); jasmine.debugLog('Calling realSetTimeout in spec'); realSetTimeout(function() { jasmine.debugLog('Calling innerDone'); innerDone(); }); }); await env.execute(); expect(reporter.specDone).toHaveBeenCalledTimes(1); const event = reporter.specDone.calls.argsFor(0)[0]; jasmine.debugLog('Spec result: ' + jasmine.basicPrettyPrinter_(event)); expect(event).toEqual(jasmine.objectContaining({ status: 'passed' })); jasmine.clock().tick(1); await new Promise(resolve => realSetTimeout(resolve)); }); it('should wait a custom interval before reporting async functions that fail to complete', async function() { createMockedEnv(); const reporter = jasmine.createSpyObj('fakeReport', [ 'jasmineDone', 'suiteDone', 'specDone' ]); env.addReporter(reporter); jasmineUnderTest.DEFAULT_TIMEOUT_INTERVAL = 10000; env.describe('suite', function() { env.afterAll(function() { realSetTimeout(function() { try { jasmine.clock().tick(10); } catch (e) { // don't worry if the clock is already uninstalled } }, 100); }); env.describe('beforeAll', function() { // eslint-disable-next-line no-unused-vars env.beforeAll(function(innerDone) { realSetTimeout(function() { jasmine.clock().tick(5001); }, 0); }, 5000); env.it('times out', function(innerDone) { realSetTimeout(function() { jasmine.clock().tick(1); innerDone(); }, 0); }); }); env.describe('afterAll', function() { // eslint-disable-next-line no-unused-vars env.afterAll(function(innerDone) { realSetTimeout(function() { jasmine.clock().tick(2001); }, 0); }, 2000); env.it('times out', function(innerDone) { realSetTimeout(function() { jasmine.clock().tick(1); innerDone(); }, 0); }); }); env.describe('beforeEach', function() { // eslint-disable-next-line no-unused-vars env.beforeEach(function(innerDone) { realSetTimeout(function() { jasmine.clock().tick(1001); }, 0); }, 1000); env.it('times out', function(innerDone) { realSetTimeout(function() { jasmine.clock().tick(1); innerDone(); }, 0); }); }); env.describe('afterEach', function() { // eslint-disable-next-line no-unused-vars env.afterEach(function(innerDone) { realSetTimeout(function() { jasmine.clock().tick(4001); }, 0); }, 4000); env.it('times out', function(innerDone) { realSetTimeout(function() { jasmine.clock().tick(1); innerDone(); }, 0); }); }); env.it( 'it times out', // eslint-disable-next-line no-unused-vars function(innerDone) { realSetTimeout(function() { jasmine.clock().tick(6001); }, 0); }, 6000 ); }); await env.execute(); const r = reporter.jasmineDone.calls.argsFor(0)[0]; expect(r.failedExpectations).toEqual([]); expect(reporter.suiteDone).toHaveFailedExpectationsForRunnable( 'suite beforeAll', [ /^Error: Timeout - Async function did not complete within 5000ms \(custom timeout\)/ ] ); expect(reporter.suiteDone).toHaveFailedExpectationsForRunnable( 'suite afterAll', [ /^Error: Timeout - Async function did not complete within 2000ms \(custom timeout\)/ ] ); expect(reporter.specDone).toHaveFailedExpectationsForRunnable( 'suite beforeEach times out', [ /^Error: Timeout - Async function did not complete within 1000ms \(custom timeout\)/ ] ); expect(reporter.specDone).toHaveFailedExpectationsForRunnable( 'suite afterEach times out', [ /^Error: Timeout - Async function did not complete within 4000ms \(custom timeout\)/ ] ); expect(reporter.specDone).toHaveFailedExpectationsForRunnable( 'suite it times out', [ /^Error: Timeout - Async function did not complete within 6000ms \(custom timeout\)/ ] ); jasmine.clock().tick(1); await new Promise(resolve => realSetTimeout(resolve)); }); }); it('explicitly fails an async spec', async function() { const specDone = jasmine.createSpy('specDone'); env.addReporter({ specDone: specDone }); env.describe('failing', function() { env.it('has a default message', function(innerDone) { setTimeout(function() { env.fail(); innerDone(); }, 1); }); env.it('specifies a message', function(innerDone) { setTimeout(function() { env.fail('messy message'); innerDone(); }, 1); }); env.it('fails via the done callback', function(innerDone) { setTimeout(function() { innerDone.fail('done failed'); }, 1); }); env.it('has a message from an Error', function(innerDone) { setTimeout(function() { env.fail(new Error('error message')); innerDone(); }, 1); }); env.it('has a message from an Error to done', function(innerDone) { setTimeout(function() { innerDone(new Error('done error')); }, 1); }); }); await env.execute(); expect(specDone).toHaveFailedExpectationsForRunnable( 'failing has a default message', ['Failed'] ); expect(specDone).toHaveFailedExpectationsForRunnable( 'failing specifies a message', ['Failed: messy message'] ); expect(specDone).toHaveFailedExpectationsForRunnable( 'failing fails via the done callback', ['Failed: done failed'] ); expect(specDone).toHaveFailedExpectationsForRunnable( 'failing has a message from an Error', ['Failed: error message'] ); expect(specDone).toHaveFailedExpectationsForRunnable( 'failing has a message from an Error to done', ['Failed: done error'] ); await new Promise(resolve => setTimeout(resolve)); }); describe('focused tests', function() { it('should only run the focused tests', async function() { const calls = []; env.describe('a suite', function() { env.fit('is focused', function() { calls.push('focused'); }); env.it('is not focused', function() { calls.push('freakout'); }); }); await env.execute(); expect(calls).toEqual(['focused']); }); it('should only run focused suites', async function() { const calls = []; env.fdescribe('a focused suite', function() { env.it('is focused', function() { calls.push('focused'); }); }); env.describe('a regular suite', function() { env.it('is not focused', function() { calls.push('freakout'); }); }); await env.execute(); expect(calls).toEqual(['focused']); }); it('should run focused tests inside an xdescribe', async function() { const reporter = jasmine.createSpyObj('fakeReporter', [ 'jasmineStarted', 'suiteStarted', 'suiteDone', 'specStarted', 'specDone' ]); env.addReporter(reporter); env.xdescribe('xd suite', function() { env.fit('with a fit spec', function() { env.expect(true).toBe(false); }); }); await env.execute(); expect(reporter.jasmineStarted).toHaveBeenCalledWith({ totalSpecsDefined: 1, order: jasmine.any(jasmineUnderTest.Order) }); expect(reporter.specDone).toHaveBeenCalledWith( jasmine.objectContaining({ description: 'with a fit spec', status: 'failed' }) ); }); it('should run focused suites inside an xdescribe', async function() { const reporter = jasmine.createSpyObj('fakeReporter', [ 'jasmineStarted', 'suiteStarted', 'suiteDone', 'specStarted', 'specDone' ]); env.addReporter(reporter); env.xdescribe('xd suite', function() { env.fdescribe('fd suite', function() { env.it('with a spec', function() { env.expect(true).toBe(false); }); }); }); await env.execute(); expect(reporter.jasmineStarted).toHaveBeenCalledWith({ totalSpecsDefined: 1, order: jasmine.any(jasmineUnderTest.Order) }); expect(reporter.specDone).toHaveBeenCalledWith( jasmine.objectContaining({ description: 'with a spec', status: 'failed' }) ); }); }); it('should report as expected', async function() { const reporter = jasmine.createSpyObj('fakeReporter', [ 'jasmineStarted', 'suiteStarted', 'suiteDone', 'specStarted', 'specDone' ]); env.addReporter(reporter); env.describe('A Suite', function() { env.it('with a top level spec', function() { env.expect(true).toBe(true); }); env.describe('with a nested suite', function() { env.xit("with an x'ed spec", function() { env.expect(true).toBe(true); }); env.it('with a spec', function() { env.expect(true).toBe(false); }); }); env.describe('with only non-executable specs', function() { env.it('is pending'); env.xit('is xed', function() { env.expect(true).toBe(true); }); }); }); await env.execute(); expect(reporter.jasmineStarted).toHaveBeenCalledWith({ totalSpecsDefined: 5, order: jasmine.any(jasmineUnderTest.Order) }); expect(reporter.specDone.calls.count()).toBe(5); expect(reporter.specDone).toHaveBeenCalledWith( jasmine.objectContaining({ description: 'with a top level spec', status: 'passed' }) ); expect(reporter.specDone).toHaveBeenCalledWith( jasmine.objectContaining({ description: "with an x'ed spec", status: 'pending' }) ); expect(reporter.specDone).toHaveBeenCalledWith( jasmine.objectContaining({ description: 'with a spec', status: 'failed' }) ); expect(reporter.specDone).toHaveBeenCalledWith( jasmine.objectContaining({ description: 'is pending', status: 'pending' }) ); const suiteDone = reporter.suiteDone.calls.argsFor(0)[0]; expect(typeof suiteDone.duration).toBe('number'); const suiteResult = reporter.suiteStarted.calls.argsFor(0)[0]; expect(suiteResult.description).toEqual('A Suite'); }); it('should report the random seed at the beginning and end of execution', async function() { const reporter = jasmine.createSpyObj('fakeReporter', [ 'jasmineStarted', 'jasmineDone', 'suiteStarted', 'suiteDone', 'specStarted', 'specDone' ]); env.configure({ random: true, seed: '123456' }); env.addReporter(reporter); env.configure({ random: true }); await env.execute(); expect(reporter.jasmineStarted).toHaveBeenCalled(); const startedArg = reporter.jasmineStarted.calls.argsFor(0)[0]; expect(startedArg.order.random).toEqual(true); expect(startedArg.order.seed).toEqual('123456'); const doneArg = reporter.jasmineDone.calls.argsFor(0)[0]; expect(doneArg.order.random).toEqual(true); expect(doneArg.order.seed).toEqual('123456'); }); it('coerces the random seed to a string if it is a number', async function() { const reporter = jasmine.createSpyObj('fakeReporter', [ 'jasmineStarted', 'jasmineDone', 'suiteStarted', 'suiteDone', 'specStarted', 'specDone' ]); env.configure({ random: true, seed: 123456 }); env.addReporter(reporter); env.configure({ random: true }); await env.execute(); expect(reporter.jasmineStarted).toHaveBeenCalled(); const startedArg = reporter.jasmineStarted.calls.argsFor(0)[0]; expect(startedArg.order.seed).toEqual('123456'); const doneArg = reporter.jasmineDone.calls.argsFor(0)[0]; expect(doneArg.order.seed).toEqual('123456'); }); it('should report pending spec messages', async function() { const reporter = jasmine.createSpyObj('fakeReporter', ['specDone']); env.addReporter(reporter); env.it('will be pending', function() { env.pending('with a message'); }); await env.execute(); const specStatus = reporter.specDone.calls.argsFor(0)[0]; expect(specStatus.status).toBe('pending'); expect(specStatus.pendingReason).toBe('with a message'); }); it('should report pending spec messages from promise-returning functions', async function() { function StubPromise(fn) { try { fn(); } catch (e) { this.exception = e; } } StubPromise.prototype.then = function(resolve, reject) { reject(this.exception); }; const reporter = jasmine.createSpyObj('fakeReporter', ['specDone']); env.addReporter(reporter); env.it('will be pending', function() { return new StubPromise(function() { env.pending('with a message'); }); }); await env.execute(); const specStatus = reporter.specDone.calls.argsFor(0)[0]; expect(specStatus.status).toBe('pending'); expect(specStatus.pendingReason).toBe('with a message'); }); it('should report using fallback reporter', function(done) { const reporter = jasmine.createSpyObj('fakeReporter', [ 'specDone', 'jasmineDone' ]); reporter.jasmineDone.and.callFake(function() { expect(reporter.specDone).toHaveBeenCalled(); done(); }); env.provideFallbackReporter(reporter); env.it('will be pending', function() { env.pending('with a message'); }); env.execute(); }); it('should report xdescribes as expected', async function() { const reporter = jasmine.createSpyObj('fakeReporter', [ 'jasmineStarted', 'suiteStarted', 'suiteDone', 'specStarted', 'specDone' ]); env.addReporter(reporter); env.describe('A Suite', function() { env.describe('nested', function() { env.xdescribe('xd out', function() { env.describe('nested again', function() { env.it('with a spec', function() { env.expect(true).toBe(false); }); }); }); }); }); await env.execute(); expect(reporter.jasmineStarted).toHaveBeenCalledWith({ totalSpecsDefined: 1, order: jasmine.any(jasmineUnderTest.Order) }); expect(reporter.specDone).toHaveBeenCalledWith( jasmine.objectContaining({ status: 'pending' }) ); expect(reporter.suiteDone).toHaveBeenCalledWith( jasmine.objectContaining({ description: 'xd out', status: 'pending' }) ); expect(reporter.suiteDone.calls.count()).toBe(4); }); it('should be possible to get full name from a spec', function() { let topLevelSpec, nestedSpec, doublyNestedSpec; env.describe('my tests', function() { topLevelSpec = env.it('are sometimes top level', function() {}); env.describe('are sometimes', function() { nestedSpec = env.it('singly nested', function() {}); env.describe('even', function() { doublyNestedSpec = env.it('doubly nested', function() {}); }); }); }); expect(topLevelSpec.getFullName()).toBe('my tests are sometimes top level'); expect(nestedSpec.getFullName()).toBe( 'my tests are sometimes singly nested' ); expect(doublyNestedSpec.getFullName()).toBe( 'my tests are sometimes even doubly nested' ); }); it('Custom equality testers should be per spec', async function() { const reporter = jasmine.createSpyObj('fakeReporter', ['specDone']); env.addReporter(reporter); env.configure({ random: false }); env.describe('testing custom equality testers', function() { env.it('with a custom tester', function() { env.addCustomEqualityTester(function() { return true; }); env.expect('a').toEqual('b'); }); env.it('without a custom tester', function() { env.expect('a').toEqual('b'); }); }); await env.execute(); const firstSpecResult = reporter.specDone.calls.first().args[0], secondSpecResult = reporter.specDone.calls.mostRecent().args[0]; expect(firstSpecResult.status).toEqual('passed'); expect(secondSpecResult.status).toEqual('failed'); }); it('Custom equality testers should be per suite', async function() { const reporter = jasmine.createSpyObj('fakeReporter', ['specDone']); env.addReporter(reporter); env.configure({ random: false }); env.describe('testing custom equality testers', function() { env.beforeAll(function() { env.addCustomEqualityTester(function() { return true; }); }); env.it('with a custom tester', function() { env.expect('a').toEqual('b'); }); env.it('with the same custom tester', function() { env.expect('a').toEqual('b'); }); }); env.describe('another suite', function() { env.it('without the custom tester', function() { env.expect('a').toEqual('b'); }); }); await env.execute(); const firstSpecResult = reporter.specDone.calls.first().args[0], secondSpecResult = reporter.specDone.calls.argsFor(0)[0], thirdSpecResult = reporter.specDone.calls.mostRecent().args[0]; expect(firstSpecResult.status).toEqual('passed'); expect(secondSpecResult.status).toEqual('passed'); expect(thirdSpecResult.status).toEqual('failed'); }); it('Custom equality testers for toContain should be per spec', async function() { const reporter = jasmine.createSpyObj('fakeReporter', ['specDone']); env.addReporter(reporter); env.configure({ random: false }); env.describe('testing custom equality testers', function() { env.it('with a custom tester', function() { env.addCustomEqualityTester(function() { return true; }); env.expect(['a']).toContain('b'); }); env.it('without a custom tester', function() { env.expect(['a']).toContain('b'); }); }); await env.execute(); const firstSpecResult = reporter.specDone.calls.first().args[0], secondSpecResult = reporter.specDone.calls.mostRecent().args[0]; expect(firstSpecResult.status).toEqual('passed'); expect(secondSpecResult.status).toEqual('failed'); }); it("produces an understandable error message when an 'expect' is used outside of a current spec", async function() { env.describe('A Suite', function() { env.it('an async spec that is actually synchronous', function( underTestCallback ) { underTestCallback(); }); expect(function() { env.expect('a').toEqual('a'); }).toThrowError(/'expect' was used when there was no current spec/); }); await env.execute(); }); it('Custom equality testers for toContain should be per suite', async function() { const reporter = jasmine.createSpyObj('fakeReporter', ['specDone']); env.addReporter(reporter); env.configure({ random: false }); env.describe('testing custom equality testers', function() { env.beforeAll(function() { env.addCustomEqualityTester(function() { return true; }); }); env.it('with a custom tester', function() { env.expect(['a']).toContain('b'); }); env.it('also with the custom tester', function() { env.expect(['a']).toContain('b'); }); }); env.describe('another suite', function() { env.it('without the custom tester', function() { env.expect(['a']).toContain('b'); }); }); await env.execute(); const firstSpecResult = reporter.specDone.calls.first().args[0], secondSpecResult = reporter.specDone.calls.argsFor(1)[0], thirdSpecResult = reporter.specDone.calls.mostRecent().args[0]; expect(firstSpecResult.status).toEqual('passed'); expect(secondSpecResult.status).toEqual('passed'); expect(thirdSpecResult.status).toEqual('failed'); }); it('Custom matchers should be per spec', async function() { const matchers = { toFoo: function() {} }; env.describe('testing custom matchers', function() { env.it('with a custom matcher', function() { env.addMatchers(matchers); expect(env.expect().toFoo).toBeDefined(); }); env.it('without a custom matcher', function() { expect(env.expect().toFoo).toBeUndefined(); }); }); await env.execute(); }); it('Custom matchers should be per suite', async function() { const matchers = { toFoo: function() {} }; env.describe('testing custom matchers', function() { env.beforeAll(function() { env.addMatchers(matchers); }); env.it('with a custom matcher', function() { expect(env.expect().toFoo).toBeDefined(); }); env.it('with the same custom matcher', function() { expect(env.expect().toFoo).toBeDefined(); }); }); env.describe('another suite', function() { env.it('no longer has the custom matcher', function() { expect(env.expect().toFoo).not.toBeDefined(); }); }); await env.execute(); }); it('throws an exception if you try to create a spy outside of a runnable', async function() { const obj = { fn: function() {} }; let exception; env.describe('a suite', function() { try { env.spyOn(obj, 'fn'); } catch (e) { exception = e; } env.it('has a test', function() {}); }); await env.execute(); expect(exception.message).toBe( 'Spies must be created in a before function or a spec' ); }); it('throws an exception if you try to add a matcher outside of a runnable', async function() { let exception; env.describe('a suite', function() { try { env.addMatchers({ myMatcher: function() { return false; } }); } catch (e) { exception = e; } env.it('has a test', function() {}); }); await env.execute(); expect(exception.message).toBe( 'Matchers must be added in a before function or a spec' ); }); it('throws an exception if you try to add a custom equality outside of a runnable', async function() { let exception; env.describe('a suite', function() { try { env.addCustomEqualityTester(function() { return true; }); } catch (e) { exception = e; } env.it('has a test', function() {}); }); await env.execute(); expect(exception.message).toBe( 'Custom Equalities must be added in a before function or a spec' ); }); it('reports test properties on specs', async function() { const env = new jasmineUnderTest.Env(), reporter = jasmine.createSpyObj('reporter', ['suiteDone', 'specDone']); reporter.specDone.and.callFake(function(e) { expect(e.properties).toEqual({ a: 'Bee' }); }); env.addReporter(reporter); env.it('calls setSpecProperty', function() { env.setSpecProperty('a', 'Bee'); }); await env.execute(); expect(reporter.specDone).toHaveBeenCalled(); }); it('throws an exception if you try to setSpecProperty outside of a spec', async function() { const env = new jasmineUnderTest.Env(); let exception; env.describe('a suite', function() { env.it('a spec'); try { env.setSpecProperty('a prop', 'val'); } catch (e) { exception = e; } env.it('has a test', function() {}); }); await env.execute(); expect(exception.message).toBe( "'setSpecProperty' was used when there was no current spec" ); }); it('reports test properties on suites', async function() { const env = new jasmineUnderTest.Env(), reporter = jasmine.createSpyObj('reporter', [ 'jasmineDone', 'suiteDone', 'specDone' ]); reporter.suiteDone.and.callFake(function(e) { expect(e.properties).toEqual({ b: 'Sweet' }); }); env.addReporter(reporter); env.describe('calls setSuiteProperty', function() { env.beforeEach(function() { env.setSuiteProperty('b', 'Sweet'); }); env.it('a passing spec', function() { expect.nothing(); }); }); await env.execute(); expect(reporter.suiteDone).toHaveBeenCalled(); }); it('throws an exception if you try to setSuiteProperty outside of a suite', function(done) { const env = new jasmineUnderTest.Env(); try { env.setSuiteProperty('a', 'Bee'); } catch (e) { expect(e.message).toBe( "'setSuiteProperty' was used when there was no current suite" ); done(); } }); it('should associate errors thrown from async code with the correct runnable', async function() { const reporter = jasmine.createSpyObj('fakeReport', [ 'suiteDone', 'specDone' ]); env.addReporter(reporter); env.describe('async suite', function() { // eslint-disable-next-line no-unused-vars env.afterAll(function(innerDone) { setTimeout(function() { throw new Error('suite'); }, 1); }, 10); env.it('spec', function() {}); }); env.describe('suite', function() { env.it( 'async spec', // eslint-disable-next-line no-unused-vars function(innerDone) { setTimeout(function() { throw new Error('spec'); }, 1); }, 10 ); }); await env.execute(); expect(reporter.suiteDone).toHaveFailedExpectationsForRunnable( 'async suite', [/^(((Uncaught )?(exception: )?Error: suite( thrown)?)|(suite thrown))$/] ); expect(reporter.specDone).toHaveFailedExpectationsForRunnable( 'suite async spec', [/^(((Uncaught )?(exception: )?Error: spec( thrown)?)|(spec thrown))$/] ); }); it("should throw on suites/specs/befores/afters nested in methods other than 'describe'", async function() { const reporter = jasmine.createSpyObj('reporter', [ 'suiteDone', 'specDone' ]); env.addReporter(reporter); env.describe('suite', function() { env.it('describe', function() { env.describe('inner suite', function() {}); }); env.it('xdescribe', function() { env.xdescribe('inner suite', function() {}); }); env.it('fdescribe', function() { env.fdescribe('inner suite', function() {}); }); }); env.describe('spec', function() { env.it('it', function() { env.it('inner spec', function() {}); }); env.it('xit', function() { env.xit('inner spec', function() {}); }); env.it('fit', function() { env.fit('inner spec', function() {}); }); }); env.describe('beforeAll', function() { env.beforeAll(function() { env.beforeAll(function() {}); }); env.it('spec', function() {}); }); env.describe('beforeEach', function() { env.beforeEach(function() { env.beforeEach(function() {}); }); env.it('spec', function() {}); }); env.describe('afterAll', function() { env.afterAll(function() { env.afterAll(function() {}); }); env.it('spec', function() {}); }); env.describe('afterEach', function() { env.afterEach(function() { env.afterEach(function() {}); }); env.it('spec', function() {}); }); await env.execute(); const msg = /\'.*\' should only be used in \'describe\' function/; expect(reporter.specDone).toHaveFailedExpectationsForRunnable( 'suite describe', [msg] ); expect(reporter.specDone).toHaveFailedExpectationsForRunnable( 'suite xdescribe', [msg] ); expect(reporter.specDone).toHaveFailedExpectationsForRunnable( 'suite fdescribe', [msg] ); expect(reporter.specDone).toHaveFailedExpectationsForRunnable('spec it', [ msg ]); expect(reporter.specDone).toHaveFailedExpectationsForRunnable('spec xit', [ msg ]); expect(reporter.specDone).toHaveFailedExpectationsForRunnable('spec fit', [ msg ]); expect(reporter.suiteDone).toHaveFailedExpectationsForRunnable( 'beforeAll', [msg] ); expect(reporter.specDone).toHaveFailedExpectationsForRunnable( 'beforeEach spec', [msg] ); expect(reporter.suiteDone).toHaveFailedExpectationsForRunnable('afterAll', [ msg ]); expect(reporter.specDone).toHaveFailedExpectationsForRunnable( 'afterEach spec', [msg] ); }); it('reports errors that occur during loading', async function() { const global = { ...browserEventMethods(), setTimeout: function(fn, delay) { return setTimeout(fn, delay); }, clearTimeout: function(fn, delay) { clearTimeout(fn, delay); }, queueMicrotask: function(fn) { queueMicrotask(fn); }, onerror: function() {} }; spyOn(jasmineUnderTest, 'getGlobal').and.returnValue(global); env.cleanup_(); env = new jasmineUnderTest.Env(); const reporter = jasmine.createSpyObj('reporter', [ 'jasmineDone', 'suiteDone', 'specDone' ]); env.addReporter(reporter); global.onerror( 'Uncaught SyntaxError: Unexpected end of input', 'borkenSpec.js', 42, undefined, { stack: 'a stack' } ); global.onerror('Uncaught Error: ENOCHEESE'); await env.execute(); const e = reporter.jasmineDone.calls.argsFor(0)[0]; expect(e.failedExpectations).toEqual([ { passed: false, globalErrorType: 'load', message: 'Uncaught SyntaxError: Unexpected end of input', stack: 'a stack', filename: 'borkenSpec.js', lineno: 42 }, { passed: false, globalErrorType: 'load', message: 'Uncaught Error: ENOCHEESE', stack: undefined, filename: undefined, lineno: undefined } ]); }); describe('If suppressLoadErrors: true was passed', function() { it('does not install a global error handler during loading', async function() { const originalOnerror = jasmine.createSpy('original onerror'); const global = { ...browserEventMethods(), setTimeout: function(fn, delay) { return setTimeout(fn, delay); }, clearTimeout: function(fn, delay) { clearTimeout(fn, delay); }, queueMicrotask: function(fn) { queueMicrotask(fn); }, onerror: originalOnerror }; spyOn(jasmineUnderTest, 'getGlobal').and.returnValue(global); const globalErrors = new jasmineUnderTest.GlobalErrors(global); const onerror = jasmine.createSpy('onerror'); globalErrors.pushListener(onerror); spyOn(jasmineUnderTest, 'GlobalErrors').and.returnValue(globalErrors); env.cleanup_(); env = new jasmineUnderTest.Env({ suppressLoadErrors: true }); const reporter = jasmine.createSpyObj('reporter', [ 'jasmineDone', 'suiteDone', 'specDone' ]); env.addReporter(reporter); global.onerror('Uncaught Error: ENOCHEESE'); await env.execute(); const e = reporter.jasmineDone.calls.argsFor(0)[0]; expect(e.failedExpectations).toEqual([]); expect(originalOnerror).toHaveBeenCalledWith('Uncaught Error: ENOCHEESE'); }); }); describe('Overall status in the jasmineDone event', function() { describe('When everything passes', function() { it('is "passed"', async function() { const reporter = jasmine.createSpyObj('reporter', [ 'jasmineDone', 'suiteDone', 'specDone' ]); env.addReporter(reporter); env.it('passes', function() {}); await env.execute(); const e = reporter.jasmineDone.calls.argsFor(0)[0]; expect(e.overallStatus).toEqual('passed'); }); }); describe('When a spec fails', function() { it('is "failed"', async function() { const reporter = jasmine.createSpyObj('reporter', [ 'jasmineDone', 'suiteDone', 'specDone' ]); env.addReporter(reporter); env.it('fails', function() { env.expect(true).toBe(false); }); await env.execute(); const e = reporter.jasmineDone.calls.argsFor(0)[0]; expect(e.overallStatus).toEqual('failed'); }); }); describe('when spec has no expectations', function() { let reporter; beforeEach(function() { reporter = jasmine.createSpyObj('reporter', [ 'jasmineDone', 'suiteDone', 'specDone' ]); env.addReporter(reporter); env.it('is a spec without any expectations', function() { // does nothing, just a mock spec without expectations }); }); it('should report "failed" status if "failSpecWithNoExpectations" is enabled', async function() { env.configure({ failSpecWithNoExpectations: true }); await env.execute(); const e = reporter.jasmineDone.calls.argsFor(0)[0]; expect(e.overallStatus).toEqual('failed'); }); it('should report "passed" status if "failSpecWithNoExpectations" is disabled', async function() { env.configure({ failSpecWithNoExpectations: false }); await env.execute(); const e = reporter.jasmineDone.calls.argsFor(0)[0]; expect(e.overallStatus).toEqual('passed'); }); }); describe('When a top-level beforeAll fails', function() { it('is "failed"', async function() { const reporter = jasmine.createSpyObj('reporter', [ 'jasmineDone', 'suiteDone', 'specDone' ]); env.addReporter(reporter); env.beforeAll(function() { throw new Error('nope'); }); env.it('does not run', function() {}); await env.execute(); const e = reporter.jasmineDone.calls.argsFor(0)[0]; expect(e.overallStatus).toEqual('failed'); }); }); describe('When a suite beforeAll fails', function() { it('is "failed"', async function() { const reporter = jasmine.createSpyObj('reporter', [ 'jasmineDone', 'suiteDone', 'specDone' ]); env.addReporter(reporter); env.describe('something', function() { env.beforeAll(function() { throw new Error('nope'); }); env.it('does not run', function() {}); }); await env.execute(); const e = reporter.jasmineDone.calls.argsFor(0)[0]; expect(e.overallStatus).toEqual('failed'); }); }); describe('When a top-level afterAll fails', function() { it('is "failed"', async function() { const reporter = jasmine.createSpyObj('reporter', [ 'jasmineDone', 'suiteDone', 'specDone' ]); env.addReporter(reporter); env.afterAll(function() { throw new Error('nope'); }); env.it('does not run', function() {}); await env.execute(); const e = reporter.jasmineDone.calls.argsFor(0)[0]; expect(e.overallStatus).toEqual('failed'); }); }); describe('When a suite afterAll fails', function() { it('is "failed"', async function() { const reporter = jasmine.createSpyObj('reporter', [ 'jasmineDone', 'suiteDone', 'specDone' ]); env.addReporter(reporter); env.describe('something', function() { env.afterAll(function() { throw new Error('nope'); }); env.it('does not run', function() {}); }); await env.execute(); const e = reporter.jasmineDone.calls.argsFor(0)[0]; expect(e.overallStatus).toEqual('failed'); }); }); describe('When there are load errors', function() { it('is "failed"', async function() { const global = { ...browserEventMethods(), setTimeout: function(fn, delay) { return setTimeout(fn, delay); }, clearTimeout: function(fn, delay) { return clearTimeout(fn, delay); }, queueMicrotask: function(fn) { queueMicrotask(fn); } }; spyOn(jasmineUnderTest, 'getGlobal').and.returnValue(global); env.cleanup_(); env = new jasmineUnderTest.Env(); const reporter = jasmine.createSpyObj('reporter', [ 'jasmineDone', 'suiteDone', 'specDone' ]); reporter.jasmineDone.and.callFake(function(e) { expect(e.overallStatus).toEqual('failed'); }); env.addReporter(reporter); env.it('passes', function() {}); global.onerror('Uncaught Error: ENOCHEESE'); await env.execute(); expect(reporter.jasmineDone).toHaveBeenCalled(); }); }); describe('When there are no specs', function() { it('is "incomplete"', async function() { const reporter = jasmine.createSpyObj('reporter', [ 'jasmineDone', 'suiteDone', 'specDone' ]); env.addReporter(reporter); await env.execute(); const e = reporter.jasmineDone.calls.argsFor(0)[0]; expect(e.overallStatus).toEqual('incomplete'); expect(e.incompleteReason).toEqual('No specs found'); }); }); describe('When a spec is focused', function() { it('is "incomplete"', async function() { const reporter = jasmine.createSpyObj('reporter', [ 'jasmineDone', 'suiteDone', 'specDone' ]); env.addReporter(reporter); env.fit('is focused', function() {}); await env.execute(); const e = reporter.jasmineDone.calls.argsFor(0)[0]; expect(e.overallStatus).toEqual('incomplete'); expect(e.incompleteReason).toEqual('fit() or fdescribe() was found'); }); }); describe('When a suite is focused', function() { it('is "incomplete"', async function() { const reporter = jasmine.createSpyObj('reporter', [ 'jasmineDone', 'suiteDone', 'specDone' ]); env.addReporter(reporter); env.fdescribe('something focused', function() { env.it('does a thing', function() {}); }); await env.execute(); const e = reporter.jasmineDone.calls.argsFor(0)[0]; expect(e.overallStatus).toEqual('incomplete'); expect(e.incompleteReason).toEqual('fit() or fdescribe() was found'); }); }); describe('When there are both failures and focused specs', function() { it('is "failed"', async function() { const reporter = jasmine.createSpyObj('reporter', [ 'jasmineDone', 'suiteDone', 'specDone' ]); env.addReporter(reporter); env.fit('is focused', function() { env.expect(true).toBe(false); }); await env.execute(); const e = reporter.jasmineDone.calls.argsFor(0)[0]; expect(e.overallStatus).toEqual('failed'); expect(e.incompleteReason).toBeUndefined(); }); }); }); it('should report deprecation stack with an error object', async function() { const exceptionFormatter = new jasmineUnderTest.ExceptionFormatter(), reporter = jasmine.createSpyObj('reporter', [ 'jasmineDone', 'suiteDone', 'specDone' ]), topLevelError = new Error('top level deprecation'), suiteLevelError = new Error('suite level deprecation'), specLevelError = new Error('spec level deprecation'); // prevent deprecation from being displayed spyOn(console, 'error'); env.addReporter(reporter); env.deprecated(topLevelError); env.describe('suite', function() { env.beforeAll(function() { env.deprecated(suiteLevelError); }); env.it('spec', function() { env.deprecated(specLevelError); }); }); await env.execute(); const result = reporter.jasmineDone.calls.argsFor(0)[0]; expect(result.deprecationWarnings).toEqual([ jasmine.objectContaining({ message: topLevelError.message, stack: exceptionFormatter.stack(topLevelError, { omitMessage: true }) }) ]); expect(reporter.suiteDone).toHaveBeenCalledWith( jasmine.objectContaining({ fullName: 'suite', deprecationWarnings: [ jasmine.objectContaining({ message: suiteLevelError.message, stack: exceptionFormatter.stack(suiteLevelError, { omitMessage: true }) }) ] }) ); expect(reporter.specDone).toHaveBeenCalledWith( jasmine.objectContaining({ fullName: 'suite spec', deprecationWarnings: [ jasmine.objectContaining({ message: specLevelError.message, stack: exceptionFormatter.stack(specLevelError, { omitMessage: true }) }) ] }) ); }); it('supports async matchers', async function() { const specDone = jasmine.createSpy('specDone'), suiteDone = jasmine.createSpy('suiteDone'), jasmineDone = jasmine.createSpy('jasmineDone'); env.addReporter({ specDone: specDone, suiteDone: suiteDone, jasmineDone: jasmineDone }); function fail(innerDone) { let resolve; const p = new Promise(function(res) { resolve = res; }); env .expectAsync(p) .toBeRejected() .then(innerDone); resolve(); } env.afterAll(fail); env.describe('a suite', function() { env.afterAll(fail); env.it('has an async failure', fail); }); await env.execute(); const result = jasmineDone.calls.argsFor(0)[0]; expect(result.failedExpectations).toEqual([ jasmine.objectContaining({ message: 'Expected [object Promise] to be rejected.' }) ]); expect(specDone).toHaveBeenCalledWith( jasmine.objectContaining({ description: 'has an async failure', failedExpectations: [ jasmine.objectContaining({ message: 'Expected [object Promise] to be rejected.' }) ] }) ); expect(suiteDone).toHaveBeenCalledWith( jasmine.objectContaining({ description: 'a suite', failedExpectations: [ jasmine.objectContaining({ message: 'Expected [object Promise] to be rejected.' }) ] }) ); }); it('provides custom equality testers to async matchers', async function() { const specDone = jasmine.createSpy('specDone'); env.addReporter({ specDone: specDone }); env.it('has an async failure', function() { env.addCustomEqualityTester(function() { return true; }); const p = Promise.resolve('something'); return env.expectAsync(p).toBeResolvedTo('something else'); }); await env.execute(); expect(specDone).toHaveBeenCalledWith( jasmine.objectContaining({ description: 'has an async failure', failedExpectations: [] }) ); }); it('includes useful stack frames in async matcher failures', async function() { const specDone = jasmine.createSpy('specDone'); env.addReporter({ specDone: specDone }); env.it('has an async failure', function() { env.addCustomEqualityTester(function() { return true; }); const p = Promise.resolve(); return env.expectAsync(p).toBeRejected(); }); await env.execute(); expect(specDone).toHaveBeenCalledWith( jasmine.objectContaining({ failedExpectations: [ jasmine.objectContaining({ stack: jasmine.stringMatching('EnvSpec.js') }) ] }) ); }); it('reports an error when an async expectation occurs after the spec finishes', async function() { const jasmineDone = jasmine.createSpy('jasmineDone'); let resolve; const promise = new Promise(function(res) { resolve = res; }); env.configure({ random: false }); env.describe('a suite', function() { env.it('does not wait', function() { // Note: we intentionally don't return the result of each expectAsync. // This causes the spec to finish before the expectations are evaluated. env.expectAsync(promise).toBeResolved(); env.expectAsync(promise).toBeResolvedTo('something else'); }); }); env.it('another spec', function(done) { // This is here to make sure that the async expectation evaluates // before the Jasmine under test finishes, especially on Safari 8 and 9. setTimeout(done, 10); }); env.addReporter({ specDone: function() { resolve(); }, jasmineDone: jasmineDone }); await env.execute(); const result = jasmineDone.calls.argsFor(0)[0]; expect(result.failedExpectations).toEqual([ jasmine.objectContaining({ passed: false, globalErrorType: 'lateExpectation', message: 'Spec "a suite does not wait" ran a "toBeResolved" expectation ' + 'after it finished.\n' + '1. Did you forget to return or await the result of expectAsync?\n' + '2. Was done() invoked before an async operation completed?\n' + '3. Did an expectation follow a call to done()?', matcherName: 'toBeResolved' }), jasmine.objectContaining({ passed: false, globalErrorType: 'lateExpectation', message: 'Spec "a suite does not wait" ran a "toBeResolvedTo" expectation ' + 'after it finished.\n' + "Message: \"Expected a promise to be resolved to 'something else' " + 'but it was resolved to undefined."\n' + '1. Did you forget to return or await the result of expectAsync?\n' + '2. Was done() invoked before an async operation completed?\n' + '3. Did an expectation follow a call to done()?', matcherName: 'toBeResolvedTo' }) ]); }); it('reports an error when an async expectation occurs after the suite finishes', async function() { const jasmineDone = jasmine.createSpy('jasmineDone'); let resolve; const promise = new Promise(function(res) { resolve = res; }); env.configure({ random: false }); env.describe('a suite', function() { env.afterAll(function() { // Note: we intentionally don't return the result of expectAsync. // This causes the suite to finish before the expectations are evaluated. env.expectAsync(promise).toBeResolved(); }); env.it('is a spec', function() {}); }); env.it('another spec', function(done) { // This is here to make sure that the async expectation evaluates // before the Jasmine under test finishes, especially on Safari 8 and 9. setTimeout(done, 10); }); env.addReporter({ suiteDone: function() { resolve(); }, jasmineDone: jasmineDone }); await env.execute(); const result = jasmineDone.calls.argsFor(0)[0]; expect(result.failedExpectations).toEqual([ jasmine.objectContaining({ passed: false, globalErrorType: 'lateExpectation', message: 'Suite "a suite" ran a "toBeResolved" expectation ' + 'after it finished.\n' + '1. Did you forget to return or await the result of expectAsync?\n' + '2. Was done() invoked before an async operation completed?\n' + '3. Did an expectation follow a call to done()?', matcherName: 'toBeResolved' }) ]); }); it('supports asymmetric equality testers that take a matchersUtil', async function() { const env = new jasmineUnderTest.Env(); env.it('spec using custom asymmetric equality tester', function() { const customEqualityFn = function(a, b) { if (a === 2 && b === 'two') { return true; } }; const arrayWithFirstElement = function(sample) { return { asymmetricMatch: function(actual, matchersUtil) { return matchersUtil.equals(sample, actual[0]); } }; }; env.addCustomEqualityTester(customEqualityFn); env.expect(['two']).toEqual(arrayWithFirstElement(2)); }); const specExpectations = function(result) { expect(result.status).toEqual('passed'); }; env.addReporter({ specDone: specExpectations }); await env.execute(); }); describe('The promise returned by #execute', function() { beforeEach(function() { this.savedInterval = jasmineUnderTest.DEFAULT_TIMEOUT_INTERVAL; }); afterEach(function() { jasmineUnderTest.DEFAULT_TIMEOUT_INTERVAL = this.savedInterval; }); it('is resolved after reporter events are dispatched', function() { const reporter = jasmine.createSpyObj('reporter', [ 'specDone', 'suiteDone', 'jasmineDone' ]); env.addReporter(reporter); env.describe('suite', function() { env.it('spec', function() {}); }); return env.execute(null).then(function() { expect(reporter.specDone).toHaveBeenCalled(); expect(reporter.suiteDone).toHaveBeenCalled(); expect(reporter.jasmineDone).toHaveBeenCalled(); }); }); it('is resolved after the stack is cleared', function(done) { const realClearStack = jasmineUnderTest.getClearStack( jasmineUnderTest.getGlobal() ), clearStackSpy = jasmine .createSpy('clearStack') .and.callFake(realClearStack); spyOn(jasmineUnderTest, 'getClearStack').and.returnValue(clearStackSpy); // Create a new env that has the clearStack defined above env.cleanup_(); env = new jasmineUnderTest.Env(); env.describe('suite', function() { env.it('spec', function() {}); }); env.execute(null).then(function() { expect(clearStackSpy).toHaveBeenCalled(); // (many times) clearStackSpy.calls.reset(); setTimeout(function() { expect(clearStackSpy).not.toHaveBeenCalled(); done(); }); }); }); it('is resolved after QueueRunner timeouts are cleared', function() { const setTimeoutSpy = spyOn( jasmineUnderTest.getGlobal(), 'setTimeout' ).and.callThrough(); const clearTimeoutSpy = spyOn( jasmineUnderTest.getGlobal(), 'clearTimeout' ).and.callThrough(); jasmineUnderTest.DEFAULT_TIMEOUT_INTERVAL = 123456; // a distinctive value env = new jasmineUnderTest.Env(); env.describe('suite', function() { env.it('spec', function() {}); }); return env.execute(null).then(function() { const timeoutIds = setTimeoutSpy.calls .all() .filter(function(call) { return call.args[1] === 123456; }) .map(function(call) { return call.returnValue; }); expect(timeoutIds.length).toBeGreaterThan(0); timeoutIds.forEach(function(timeoutId) { expect(clearTimeoutSpy).toHaveBeenCalledWith(timeoutId); }); }); }); it('is resolved to the value of the jasmineDone event', async function() { env.describe('suite', function() { env.it('spec', function() { env.expect(true).toBe(false); }); }); let event; env.addReporter({ jasmineDone: e => (event = e) }); const result = await env.execute(); expect(event.overallStatus).toEqual('failed'); expect(result).toEqual(event); }); }); describe('The optional callback argument to #execute', function() { beforeEach(function() { this.savedInterval = jasmineUnderTest.DEFAULT_TIMEOUT_INTERVAL; }); afterEach(function() { jasmineUnderTest.DEFAULT_TIMEOUT_INTERVAL = this.savedInterval; }); it('is called after reporter events are dispatched', async function() { const reporter = jasmine.createSpyObj('reporter', [ 'specDone', 'suiteDone', 'jasmineDone' ]); env.addReporter(reporter); env.describe('suite', function() { env.it('spec', function() {}); }); await env.execute(); expect(reporter.specDone).toHaveBeenCalled(); expect(reporter.suiteDone).toHaveBeenCalled(); expect(reporter.jasmineDone).toHaveBeenCalled(); }); it('is called after the stack is cleared', async function() { const realClearStack = jasmineUnderTest.getClearStack( jasmineUnderTest.getGlobal() ), clearStackSpy = jasmine .createSpy('clearStack') .and.callFake(realClearStack); spyOn(jasmineUnderTest, 'getClearStack').and.returnValue(clearStackSpy); // Create a new env that has the clearStack defined above env.cleanup_(); env = new jasmineUnderTest.Env(); env.describe('suite', function() { env.it('spec', function() {}); }); await env.execute(); expect(clearStackSpy).toHaveBeenCalled(); // (many times) clearStackSpy.calls.reset(); await new Promise(resolve => setTimeout(resolve)); expect(clearStackSpy).not.toHaveBeenCalled(); }); it('is called after QueueRunner timeouts are cleared', async function() { const setTimeoutSpy = spyOn( jasmineUnderTest.getGlobal(), 'setTimeout' ).and.callThrough(); const clearTimeoutSpy = spyOn( jasmineUnderTest.getGlobal(), 'clearTimeout' ).and.callThrough(); jasmineUnderTest.DEFAULT_TIMEOUT_INTERVAL = 123456; // a distinctive value env = new jasmineUnderTest.Env(); env.describe('suite', function() { env.it('spec', function() {}); }); await env.execute(); const timeoutIds = setTimeoutSpy.calls .all() .filter(function(call) { return call.args[1] === 123456; }) .map(function(call) { return call.returnValue; }); expect(timeoutIds.length).toBeGreaterThan(0); timeoutIds.forEach(function(timeoutId) { expect(clearTimeoutSpy).toHaveBeenCalledWith(timeoutId); }); }); }); it('sends debug logs to the reporter when the spec fails', async function() { const reporter = jasmine.createSpyObj('reporter', ['specDone']); env.addReporter(reporter); env.configure({ random: false }); env.it('fails', function() { env.debugLog('message 1'); env.debugLog('message 2'); env.expect(1).toBe(2); }); env.it('passes', function() { env.debugLog('message that should not be reported'); }); await env.execute(); function numberInRange(min, max) { return { asymmetricMatch: function(compareTo) { return compareTo >= min && compareTo <= max; }, jasmineToString: function() { return ''; } }; } expect(reporter.specDone).toHaveBeenCalledTimes(2); const duration = reporter.specDone.calls.argsFor(0)[0].duration; expect(reporter.specDone.calls.argsFor(0)[0]).toEqual( jasmine.objectContaining({ debugLogs: [ { timestamp: numberInRange(0, duration), message: 'message 1' }, { timestamp: numberInRange(0, duration), message: 'message 2' } ] }) ); expect(reporter.specDone.calls.argsFor(1)[0].debugLogs).toBeFalsy(); }); it('reports an error when debugLog is used when a spec is not running', async function() { const reporter = jasmine.createSpyObj('reporter', ['suiteDone']); env.describe('a suite', function() { env.beforeAll(function() { env.debugLog('a message'); }); env.it('a spec', function() {}); }); env.addReporter(reporter); await env.execute(); expect(reporter.suiteDone).toHaveBeenCalledWith( jasmine.objectContaining({ failedExpectations: [ jasmine.objectContaining({ message: jasmine.stringContaining( "'debugLog' was called when there was no current spec" ) }) ] }) ); }); it('uses custom equality testers in Spy#withArgs', async function() { env.it('a spec', function() { const createSpySpy = env.createSpy('via createSpy'); const spiedOn = { foo: function() {} }; env.spyOn(spiedOn, 'foo'); const spyObj = env.createSpyObj('spyObj', ['foo']); const spiedOnAllFuncs = { foo: function() {} }; env.spyOnAllFunctions(spiedOnAllFuncs); for (const spy of [ createSpySpy, spiedOn.foo, spyObj.foo, spiedOnAllFuncs.foo ]) { spy.and.returnValue('default strategy'); spy.withArgs(42).and.returnValue('custom strategy'); } env.addCustomEqualityTester(function(a, b) { if ((a === 'x' && b === 42) || (a === 42 && b === 'x')) { return true; } }); env .expect(createSpySpy('x')) .withContext('createSpy') .toEqual('custom strategy'); env .expect(spiedOn.foo('x')) .withContext('spyOn') .toEqual('custom strategy'); env .expect(spyObj.foo('x')) .withContext('createSpyObj') .toEqual('custom strategy'); env .expect(spiedOnAllFuncs.foo('x')) .withContext('spyOnAllFunctions') .toEqual('custom strategy'); }); let failedExpectations; env.addReporter({ specDone: r => (failedExpectations = r.failedExpectations) }); await env.execute(); expect(failedExpectations).toEqual([]); }); it('calls the optional done callback when finished', function(done) { const reporter = jasmine.createSpyObj('reporter', ['jasmineDone']); env.addReporter(reporter); env.execute(null, function() { expect(reporter.jasmineDone).toHaveBeenCalled(); done(); }); }); describe('#spyOnGlobalErrorsAsync', function() { const leftInstalledMessage = 'Global error spy was not uninstalled. ' + '(Did you forget to await the return value of spyOnGlobalErrorsAsync?)'; function resultForRunable(reporterSpy, fullName) { const match = reporterSpy.calls.all().find(function(call) { return call.args[0].fullName === fullName; }); if (!match) { throw new Error(`No result for runable "${fullName}"`); } return match.args[0]; } it('allows global errors to be suppressed and spied on', async function() { env.it('a passing spec', async function() { await env.spyOnGlobalErrorsAsync(async spy => { setTimeout(() => { throw new Error('nope'); }); await new Promise(resolve => setTimeout(resolve)); env.expect(spy).toHaveBeenCalledWith(new Error('nope')); }); }); env.it('a failing spec', async function() { await env.spyOnGlobalErrorsAsync(async spy => { setTimeout(() => { throw new Error('yep'); }); await new Promise(resolve => setTimeout(resolve)); env.expect(spy).toHaveBeenCalledWith(new Error('nope')); }); }); const reporter = jasmine.createSpyObj('reporter', ['specDone']); env.addReporter(reporter); await env.execute(); const passingResult = resultForRunable( reporter.specDone, 'a passing spec' ); expect(passingResult.status).toEqual('passed'); expect(passingResult.failedExpectations).toEqual([]); const failingResult = resultForRunable( reporter.specDone, 'a failing spec' ); expect(failingResult.status).toEqual('failed'); expect(failingResult.failedExpectations[0].message).toMatch( /Expected \$\[0] = Error: yep to equal Error: nope\./ ); }); it('cleans up if the global error spy is left installed in a beforeAll', async function() { env.configure({ random: false }); env.describe('Suite 1', function() { env.beforeAll(async function() { env.spyOnGlobalErrorsAsync(function() { // Never resolves return new Promise(() => {}); }); }); env.it('a spec', function() {}); }); env.describe('Suite 2', function() { env.it('a spec', async function() { setTimeout(function() { throw new Error('should fail the spec'); }); await new Promise(resolve => setTimeout(resolve)); }); }); const reporter = jasmine.createSpyObj('reporter', [ 'specDone', 'suiteDone' ]); env.addReporter(reporter); await env.execute(); const suiteResult = resultForRunable(reporter.suiteDone, 'Suite 1'); expect(suiteResult.status).toEqual('failed'); expect(suiteResult.failedExpectations.length).toEqual(1); expect(suiteResult.failedExpectations[0].message).toEqual( leftInstalledMessage ); const specResult = resultForRunable(reporter.specDone, 'Suite 2 a spec'); expect(specResult.status).toEqual('failed'); expect(specResult.failedExpectations.length).toEqual(1); expect(specResult.failedExpectations[0].message).toMatch( /Error: should fail the spec/ ); }); it('cleans up if the global error spy is left installed in an afterAll', async function() { env.configure({ random: false }); env.describe('Suite 1', function() { env.afterAll(async function() { env.spyOnGlobalErrorsAsync(function() { // Never resolves return new Promise(() => {}); }); }); env.it('a spec', function() {}); }); env.describe('Suite 2', function() { env.it('a spec', async function() { setTimeout(function() { throw new Error('should fail the spec'); }); await new Promise(resolve => setTimeout(resolve)); }); }); const reporter = jasmine.createSpyObj('reporter', [ 'specDone', 'suiteDone' ]); env.addReporter(reporter); await env.execute(); expect(reporter.suiteDone).toHaveFailedExpectationsForRunnable( 'Suite 1', [leftInstalledMessage] ); const suiteResult = resultForRunable(reporter.suiteDone, 'Suite 1'); expect(suiteResult.status).toEqual('failed'); expect(suiteResult.failedExpectations.length).toEqual(1); expect(suiteResult.failedExpectations[0].message).toEqual( leftInstalledMessage ); const specResult = resultForRunable(reporter.specDone, 'Suite 2 a spec'); expect(specResult.status).toEqual('failed'); expect(specResult.failedExpectations.length).toEqual(1); expect(specResult.failedExpectations[0].message).toMatch( /Error: should fail the spec/ ); }); it('cleans up if the global error spy is left installed in a beforeEach', async function() { env.configure({ random: false }); env.describe('Suite 1', function() { env.beforeEach(async function() { env.spyOnGlobalErrorsAsync(function() { // Never resolves return new Promise(() => {}); }); }); env.it('a spec', function() {}); }); env.describe('Suite 2', function() { env.it('a spec', async function() { setTimeout(function() { throw new Error('should fail the spec'); }); await new Promise(resolve => setTimeout(resolve)); }); }); const reporter = jasmine.createSpyObj('reporter', [ 'specDone', 'suiteDone' ]); env.addReporter(reporter); await env.execute(); const spec1Result = resultForRunable(reporter.specDone, 'Suite 1 a spec'); expect(spec1Result.status).toEqual('failed'); expect(spec1Result.failedExpectations.length).toEqual(1); expect(spec1Result.failedExpectations[0].message).toEqual( leftInstalledMessage ); const spec2Result = resultForRunable(reporter.specDone, 'Suite 2 a spec'); expect(spec2Result.status).toEqual('failed'); expect(spec2Result.failedExpectations.length).toEqual(1); expect(spec2Result.failedExpectations[0].message).toMatch( /Error: should fail the spec/ ); }); it('cleans up if the global error spy is left installed in an it', async function() { env.configure({ random: false }); env.it('spec 1', async function() { env.spyOnGlobalErrorsAsync(function() { // Never resolves return new Promise(() => {}); }); }); env.it('spec 2', async function() { setTimeout(function() { throw new Error('should fail the spec'); }); await new Promise(resolve => setTimeout(resolve)); }); const reporter = jasmine.createSpyObj('reporter', ['specDone']); env.addReporter(reporter); await env.execute(); const spec1Result = resultForRunable(reporter.specDone, 'spec 1'); expect(spec1Result.status).toEqual('failed'); expect(spec1Result.failedExpectations.length).toEqual(1); expect(spec1Result.failedExpectations[0].message).toEqual( leftInstalledMessage ); const spec2Result = resultForRunable(reporter.specDone, 'spec 2'); expect(spec2Result.status).toEqual('failed'); expect(spec2Result.failedExpectations.length).toEqual(1); expect(spec2Result.failedExpectations[0].message).toMatch( /Error: should fail the spec/ ); }); it('cleans up if the global error spy is left installed in an afterEach', async function() { env.configure({ random: false }); env.describe('Suite 1', function() { env.afterEach(async function() { env.spyOnGlobalErrorsAsync(function() { // Never resolves return new Promise(() => {}); }); }); env.it('a spec', function() {}); }); env.describe('Suite 2', function() { env.it('a spec', async function() { setTimeout(function() { throw new Error('should fail the spec'); }); await new Promise(resolve => setTimeout(resolve)); }); }); const reporter = jasmine.createSpyObj('reporter', [ 'specDone', 'suiteDone' ]); env.addReporter(reporter); await env.execute(); const spec1Result = resultForRunable(reporter.specDone, 'Suite 1 a spec'); expect(spec1Result.status).toEqual('failed'); expect(spec1Result.failedExpectations.length).toEqual(1); expect(spec1Result.failedExpectations[0].message).toEqual( leftInstalledMessage ); const spec2Result = resultForRunable(reporter.specDone, 'Suite 2 a spec'); expect(spec2Result.status).toEqual('failed'); expect(spec2Result.failedExpectations.length).toEqual(1); expect(spec2Result.failedExpectations[0].message).toMatch( /Error: should fail the spec/ ); }); }); it('reports a suite level error when a describe fn throws', async function() { const reporter = jasmine.createSpyObj('reporter', ['suiteDone']); env.addReporter(reporter); env.describe('throws before defining specs', function() { throw new Error('nope'); }); env.describe('throws after defining specs', function() { env.it('is a spec'); throw new Error('nope'); }); await env.execute(); expect(reporter.suiteDone).toHaveBeenCalledWith( jasmine.objectContaining({ fullName: 'throws after defining specs', failedExpectations: [ jasmine.objectContaining({ message: jasmine.stringContaining('Error: nope') }) ] }) ); expect(reporter.suiteDone).toHaveBeenCalledWith( jasmine.objectContaining({ fullName: 'throws after defining specs', failedExpectations: [ jasmine.objectContaining({ message: jasmine.stringContaining('Error: nope') }) ] }) ); }); function browserEventMethods() { return { addEventListener() {}, removeEventListener() {} }; } }); jasmine-4.5.0/spec/core/integration/MatchersSpec.js000077500000000000000000000543041432731766000223140ustar00rootroot00000000000000describe('Matchers (Integration)', function() { let env; beforeEach(function() { env = new jasmineUnderTest.Env(); }); afterEach(function() { env.cleanup_(); }); function verifyPasses(expectations) { it('passes', async function() { env.it('a spec', function() { expectations(env); }); const reporter = jasmine.createSpyObj('reporter', ['specDone']); env.addReporter(reporter); await env.execute(); expect(reporter.specDone).toHaveBeenCalledTimes(1); const result = reporter.specDone.calls.argsFor(0)[0]; expect(result.status).toEqual('passed'); expect(result.passedExpectations.length) .withContext('Number of passed expectations') .toEqual(1); expect(result.failedExpectations.length) .withContext('Number of failed expectations') .toEqual(0); expect( result.failedExpectations[0] && result.failedExpectations[0].message ) .withContext('Failure message') .toBeUndefined(); }); } function verifyFails(expectations) { it('fails', async function() { env.it('a spec', function() { expectations(env); }); const reporter = jasmine.createSpyObj('reporter', ['specDone']); env.addReporter(reporter); await env.execute(); expect(reporter.specDone).toHaveBeenCalledTimes(1); const result = reporter.specDone.calls.argsFor(0)[0]; expect(result.status).toEqual('failed'); expect(result.failedExpectations.length) .withContext('Number of failed expectations') .toEqual(1); expect(result.failedExpectations[0].message) .withContext('Failed with a thrown error rather than a matcher failure') .not.toMatch(/^Error: /); expect(result.failedExpectations[0].message) .withContext( 'Failed with a thrown type error rather than a matcher failure' ) .not.toMatch(/^TypeError: /); expect(result.failedExpectations[0].matcherName) .withContext('Matcher name') .not.toEqual(''); }); } function verifyFailsWithCustomObjectFormatters(config) { it('uses custom object formatters', async function() { env.it('a spec', function() { env.addCustomObjectFormatter(config.formatter); config.expectations(env); }); const reporter = jasmine.createSpyObj('reporter', ['specDone']); env.addReporter(reporter); await env.execute(); expect(reporter.specDone).toHaveBeenCalledTimes(1); const result = reporter.specDone.calls.argsFor(0)[0]; expect(result.status).toEqual('failed'); expect(result.failedExpectations.length) .withContext('Number of failed expectations') .toEqual(1); expect(result.failedExpectations[0].message).toEqual( config.expectedMessage ); }); } function verifyPassesAsync(expectations) { it('passes', async function() { env.it('a spec', function() { return expectations(env); }); const reporter = jasmine.createSpyObj('reporter', ['specDone']); env.addReporter(reporter); await env.execute(); expect(reporter.specDone).toHaveBeenCalledTimes(1); const result = reporter.specDone.calls.argsFor(0)[0]; expect(result.status).toEqual('passed'); expect(result.passedExpectations.length) .withContext('Number of passed expectations') .toEqual(1); expect(result.failedExpectations.length) .withContext('Number of failed expectations') .toEqual(0); expect( result.failedExpectations[0] && result.failedExpectations[0].message ) .withContext('Failure message') .toBeUndefined(); }); } function verifyFailsAsync(expectations) { it('fails', async function() { env.it('a spec', function() { return expectations(env); }); const reporter = jasmine.createSpyObj('reporter', ['specDone']); env.addReporter(reporter); await env.execute(); expect(reporter.specDone).toHaveBeenCalledTimes(1); const result = reporter.specDone.calls.argsFor(0)[0]; expect(result.status).toEqual('failed'); expect(result.failedExpectations.length) .withContext('Number of failed expectations') .toEqual(1); expect(result.failedExpectations[0].message) .withContext('Failed with a thrown error rather than a matcher failure') .not.toMatch(/^Error: /); expect(result.failedExpectations[0].matcherName) .withContext('Matcher name') .not.toEqual(''); }); } function verifyFailsWithCustomObjectFormattersAsync(config) { it('uses custom object formatters', async function() { const env = new jasmineUnderTest.Env(); env.it('a spec', function() { env.addCustomObjectFormatter(config.formatter); return config.expectations(env); }); const reporter = jasmine.createSpyObj('reporter', ['specDone']); env.addReporter(reporter); await env.execute(); expect(reporter.specDone).toHaveBeenCalledTimes(1); const result = reporter.specDone.calls.argsFor(0)[0]; expect(result.status).toEqual('failed'); expect(result.failedExpectations.length) .withContext('Number of failed expectations') .toEqual(1); expect(result.failedExpectations[0].message).toEqual( config.expectedMessage ); }); } describe('nothing', function() { verifyPasses(function(env) { env.expect().nothing(); }); }); describe('toBe', function() { verifyPasses(function(env) { env.expect(1).toBe(1); }); verifyFails(function(env) { env.expect(2).toBe(1); }); }); describe('toBeCloseTo', function() { verifyPasses(function(env) { env.expect(1.001).toBeCloseTo(1, 2); }); verifyFails(function(env) { env.expect(1.1).toBeCloseTo(1, 2); }); }); describe('toBeDefined', function() { verifyPasses(function(env) { env.expect({}).toBeDefined(); }); verifyFails(function(env) { env.expect(undefined).toBeDefined(); }); }); describe('toBeFalse', function() { verifyPasses(function(env) { env.expect(false).toBeFalse(); }); verifyFails(function(env) { env.expect(true).toBeFalse(); }); }); describe('toBeFalsy', function() { verifyPasses(function(env) { env.expect(false).toBeFalsy(); }); verifyFails(function(env) { env.expect(true).toBeFalsy(); }); }); describe('toBeGreaterThan', function() { verifyPasses(function(env) { env.expect(2).toBeGreaterThan(1); }); verifyFails(function(env) { env.expect(1).toBeGreaterThan(2); }); }); describe('toBeGreaterThanOrEqual', function() { verifyPasses(function(env) { env.expect(2).toBeGreaterThanOrEqual(1); }); verifyFails(function(env) { env.expect(1).toBeGreaterThanOrEqual(2); }); }); describe('toBeInstanceOf', function() { function Ctor() {} verifyPasses(function(env) { env.expect(new Ctor()).toBeInstanceOf(Ctor); }); verifyFails(function(env) { env.expect({}).toBeInstanceOf(Ctor); }); }); describe('toBeLessThan', function() { verifyPasses(function(env) { env.expect(1).toBeLessThan(2); }); verifyFails(function(env) { env.expect(2).toBeLessThan(1); }); }); describe('toBeLessThanOrEqual', function() { verifyPasses(function(env) { env.expect(1).toBeLessThanOrEqual(2); }); verifyFails(function(env) { env.expect(2).toBeLessThanOrEqual(1); }); }); describe('toBeNaN', function() { verifyPasses(function(env) { env.expect(NaN).toBeNaN(); }); verifyFails(function(env) { env.expect(2).toBeNaN(); }); verifyFailsWithCustomObjectFormatters({ formatter: function(val) { return '|' + val + '|'; }, expectations: function(env) { env.expect(1).toBeNaN(); }, expectedMessage: 'Expected |1| to be NaN.' }); }); describe('toBeNegativeInfinity', function() { verifyPasses(function(env) { env.expect(Number.NEGATIVE_INFINITY).toBeNegativeInfinity(); }); verifyFails(function(env) { env.expect(2).toBeNegativeInfinity(); }); verifyFailsWithCustomObjectFormatters({ formatter: function(val) { return '|' + val + '|'; }, expectations: function(env) { env.expect(1).toBeNegativeInfinity(); }, expectedMessage: 'Expected |1| to be -Infinity.' }); }); describe('toBeNull', function() { verifyPasses(function(env) { env.expect(null).toBeNull(); }); verifyFails(function(env) { env.expect(2).toBeNull(); }); }); describe('toBePositiveInfinity', function() { verifyPasses(function(env) { env.expect(Number.POSITIVE_INFINITY).toBePositiveInfinity(); }); verifyFails(function(env) { env.expect(2).toBePositiveInfinity(); }); verifyFailsWithCustomObjectFormatters({ formatter: function(val) { return '|' + val + '|'; }, expectations: function(env) { env.expect(1).toBePositiveInfinity(); }, expectedMessage: 'Expected |1| to be Infinity.' }); }); describe('toBeResolved', function() { verifyPassesAsync(function(env) { return env.expectAsync(Promise.resolve()).toBeResolved(); }); verifyFailsAsync(function(env) { return env.expectAsync(Promise.reject()).toBeResolved(); }); }); describe('toBeResolvedTo', function() { verifyPassesAsync(function(env) { env.addCustomEqualityTester(function(a, b) { return a.toString() === b.toString(); }); return env.expectAsync(Promise.resolve('5')).toBeResolvedTo(5); }); verifyFailsAsync(function(env) { return env.expectAsync(Promise.resolve('foo')).toBeResolvedTo('bar'); }); verifyFailsWithCustomObjectFormattersAsync({ formatter: function(val) { return '|' + val + '|'; }, expectations: function(env) { return env.expectAsync(Promise.resolve('x')).toBeResolvedTo('y'); }, expectedMessage: 'Expected a promise to be resolved to |y| ' + 'but it was resolved to |x|.' }); }); describe('toBeRejected', function() { verifyPassesAsync(function(env) { return env.expectAsync(Promise.reject('nope')).toBeRejected(); }); verifyFailsAsync(function(env) { return env.expectAsync(Promise.resolve()).toBeRejected(); }); }); describe('toBeRejectedWith', function() { verifyPassesAsync(function(env) { env.addCustomEqualityTester(function(a, b) { return a.toString() === b.toString(); }); return env.expectAsync(Promise.reject('5')).toBeRejectedWith(5); }); verifyFailsAsync(function(env) { return env.expectAsync(Promise.resolve()).toBeRejectedWith('nope'); }); verifyFailsWithCustomObjectFormattersAsync({ formatter: function(val) { return '|' + val + '|'; }, expectations: function(env) { return env.expectAsync(Promise.reject('x')).toBeRejectedWith('y'); }, expectedMessage: 'Expected a promise to be rejected with |y| ' + 'but it was rejected with |x|.' }); }); describe('toBeRejectedWithError', function() { verifyPassesAsync(function(env) { return env .expectAsync(Promise.reject(new Error())) .toBeRejectedWithError(Error); }); verifyFailsAsync(function(env) { return env.expectAsync(Promise.resolve()).toBeRejectedWithError(Error); }); verifyFailsWithCustomObjectFormattersAsync({ formatter: function(val) { return '|' + val + '|'; }, expectations: function(env) { return env .expectAsync(Promise.reject('foo')) .toBeRejectedWithError('foo'); }, expectedMessage: 'Expected a promise to be rejected with Error: |foo| ' + 'but it was rejected with |foo|.' }); }); describe('toBeTrue', function() { verifyPasses(function(env) { env.expect(true).toBeTrue(); }); verifyFails(function(env) { env.expect(false).toBeTrue(); }); }); describe('toBeTruthy', function() { verifyPasses(function(env) { env.expect(true).toBeTruthy(); }); verifyFails(function(env) { env.expect(false).toBeTruthy(); }); }); describe('toBeUndefined', function() { verifyPasses(function(env) { env.expect(undefined).toBeUndefined(); }); verifyFails(function(env) { env.expect(1).toBeUndefined(); }); }); describe('toContain', function() { verifyPasses(function(env) { env.addCustomEqualityTester(function(a, b) { return a.toString() === b.toString(); }); env.expect(['1', '2', '3']).toContain(2); }); verifyFails(function(env) { env.expect('bar').toContain('oo'); }); }); describe('toEqual', function() { verifyPasses(function(env) { env.addCustomEqualityTester(function(a, b) { return a.toString() === b.toString(); }); env.expect(5).toEqual('5'); }); verifyFails(function(env) { env.expect('a').toEqual('b'); }); verifyFailsWithCustomObjectFormatters({ formatter: function(val) { if (val === 5) { return 'five'; } else if (val === 4) { return 'four'; } }, expectations: function(env) { env.expect([{ foo: 4 }]).toEqual([{ foo: 5 }]); }, expectedMessage: 'Expected $[0].foo = four to equal five.' }); }); describe('toHaveSize', function() { verifyPasses(function(env) { env.expect(['a', 'b']).toHaveSize(2); }); verifyFails(function(env) { env.expect(['a', 'b']).toHaveSize(1); }); }); describe('toHaveBeenCalled', function() { verifyPasses(function(env) { const spy = env.createSpy('spy'); spy(); env.expect(spy).toHaveBeenCalled(); }); verifyFails(function(env) { const spy = env.createSpy('spy'); env.expect(spy).toHaveBeenCalled(); }); }); describe('toHaveBeenCalledBefore', function() { verifyPasses(function(env) { const a = env.createSpy('a'), b = env.createSpy('b'); a(); b(); env.expect(a).toHaveBeenCalledBefore(b); }); verifyFails(function(env) { const a = env.createSpy('a'), b = env.createSpy('b'); b(); a(); env.expect(a).toHaveBeenCalledBefore(b); }); }); describe('toHaveBeenCalledTimes', function() { verifyPasses(function(env) { const spy = env.createSpy('spy'); spy(); env.expect(spy).toHaveBeenCalledTimes(1); }); verifyFails(function(env) { const spy = env.createSpy('spy'); env.expect(spy).toHaveBeenCalledTimes(1); }); }); describe('toHaveBeenCalledWith', function() { verifyPasses(function(env) { const spy = env.createSpy(); spy('5'); env.addCustomEqualityTester(function(a, b) { return a.toString() === b.toString(); }); env.expect(spy).toHaveBeenCalledWith(5); }); verifyFails(function(env) { const spy = env.createSpy(); env.expect(spy).toHaveBeenCalledWith('foo'); }); verifyFailsWithCustomObjectFormatters({ formatter: function(val) { return '|' + val + '|'; }, expectations: function(env) { const spy = env.createSpy('foo'); env.expect(spy).toHaveBeenCalledWith('x'); }, expectedMessage: 'Expected spy foo to have been called with:\n' + ' |x|\n' + 'but it was never called.' }); }); describe('toHaveBeenCalledOnceWith', function() { verifyPasses(function(env) { const spy = env.createSpy(); spy('5', 3); env.addCustomEqualityTester(function(a, b) { return a.toString() === b.toString(); }); env.expect(spy).toHaveBeenCalledOnceWith(5, 3); }); verifyFails(function(env) { const spy = env.createSpy(); env.expect(spy).toHaveBeenCalledOnceWith(5, 3); }); }); describe('toHaveClass', function() { beforeEach(function() { this.domHelpers = jasmine.getEnv().domHelpers(); }); verifyPasses(function(env) { const domHelpers = jasmine.getEnv().domHelpers(); const el = domHelpers.createElementWithClassName('foo'); env.expect(el).toHaveClass('foo'); }); verifyFails(function(env) { const domHelpers = jasmine.getEnv().domHelpers(); const el = domHelpers.createElementWithClassName('foo'); env.expect(el).toHaveClass('bar'); }); }); describe('toHaveSpyInteractions', function() { let spyObj; beforeEach(function() { spyObj = env.createSpyObj('NewClass', ['spyA', 'spyB']); spyObj.otherMethod = function() {}; }); verifyPasses(function(env) { spyObj.spyA(); env.expect(spyObj).toHaveSpyInteractions(); }); verifyFails(function(env) { env.expect(spyObj).toHaveSpyInteractions(); }); verifyFails(function(env) { spyObj.otherMethod(); env.expect(spyObj).toHaveSpyInteractions(); }); }); describe('toMatch', function() { verifyPasses(function(env) { env.expect('foo').toMatch(/oo$/); }); verifyFails(function(env) { env.expect('bar').toMatch(/oo$/); }); }); describe('toThrow', function() { verifyPasses(function(env) { env.addCustomEqualityTester(function(a, b) { return a.toString() === b.toString(); }); env .expect(function() { throw '5'; }) .toThrow(5); }); verifyFails(function(env) { env.expect(function() {}).toThrow(); }); verifyFailsWithCustomObjectFormatters({ formatter: function(val) { return '|' + val + '|'; }, expectations: function(env) { env .expect(function() { throw 'x'; }) .not.toThrow(); }, expectedMessage: 'Expected function not to throw, but it threw |x|.' }); }); describe('toThrowError', function() { verifyPasses(function(env) { env .expect(function() { throw new Error(); }) .toThrowError(); }); verifyFails(function(env) { env.expect(function() {}).toThrowError(); }); verifyFailsWithCustomObjectFormatters({ formatter: function(val) { return '|' + val + '|'; }, expectations: function(env) { env .expect(function() { throw 'x'; }) .toThrowError(); }, expectedMessage: 'Expected function to throw an Error, but it threw |x|.' }); }); describe('toThrowMatching', function() { function throws() { throw new Error('nope'); } verifyPasses(function(env) { env.expect(throws).toThrowMatching(function() { return true; }); }); verifyFails(function(env) { env.expect(throws).toThrowMatching(function() { return false; }); }); verifyFailsWithCustomObjectFormatters({ formatter: function(val) { return '|' + val + '|'; }, expectations: function(env) { env .expect(function() { throw new Error('nope'); }) .toThrowMatching(function() { return false; }); }, expectedMessage: 'Expected function to throw an exception matching ' + 'a predicate, but it threw Error with message |nope|.' }); }); describe('When an async matcher is used with .already()', function() { it('propagates the matcher result when the promise is resolved', async function() { env.it('a spec', function() { return env.expectAsync(Promise.resolve()).already.toBeRejected(); }); const reporter = jasmine.createSpyObj('reporter', ['specDone']); env.addReporter(reporter); await env.execute(); expect(reporter.specDone).toHaveBeenCalledTimes(1); const result = reporter.specDone.calls.argsFor(0)[0]; expect(result.status).toEqual('failed'); expect(result.failedExpectations.length) .withContext('Number of failed expectations') .toEqual(1); expect(result.failedExpectations[0].message).toEqual( 'Expected [object Promise] to be rejected.' ); expect(result.failedExpectations[0].matcherName) .withContext('Matcher name') .not.toEqual(''); }); it('propagates the matcher result when the promise is rejected', async function() { env.it('a spec', function() { return env .expectAsync(Promise.reject(new Error('nope'))) .already.toBeResolved(); }); const reporter = jasmine.createSpyObj('reporter', ['specDone']); env.addReporter(reporter); await env.execute(); expect(reporter.specDone).toHaveBeenCalledTimes(1); const result = reporter.specDone.calls.argsFor(0)[0]; expect(result.status).toEqual('failed'); expect(result.failedExpectations.length) .withContext('Number of failed expectations') .toEqual(1); expect(result.failedExpectations[0].message).toEqual( 'Expected a promise to be resolved but it was ' + 'rejected with Error: nope.' ); expect(result.failedExpectations[0].matcherName) .withContext('Matcher name') .not.toEqual(''); }); it('fails when the promise is pending', async function() { const promise = new Promise(function() {}); env.it('a spec', function() { return env.expectAsync(promise).already.toBeResolved(); }); const reporter = jasmine.createSpyObj('reporter', ['specDone']); env.addReporter(reporter); await env.execute(); expect(reporter.specDone).toHaveBeenCalledTimes(1); const result = reporter.specDone.calls.argsFor(0)[0]; expect(result.status).toEqual('failed'); expect(result.failedExpectations.length) .withContext('Number of failed expectations') .toEqual(1); expect(result.failedExpectations[0].message).toEqual( 'Expected a promise to be settled ' + '(via expectAsync(...).already) but it was pending.' ); expect(result.failedExpectations[0].matcherName) .withContext('Matcher name') .not.toEqual(''); }); }); }); jasmine-4.5.0/spec/core/integration/SpecRunningSpec.js000066400000000000000000001164611432731766000230010ustar00rootroot00000000000000describe('spec running', function() { let env; beforeEach(function() { jasmine.getEnv().registerIntegrationMatchers(); env = new jasmineUnderTest.Env(); env.configure({ random: false }); }); afterEach(function() { env.cleanup_(); }); it('should assign spec ids sequentially', function() { let it0, it1, it2, it3, it4; env.describe('test suite', function() { it0 = env.it('spec 0', function() {}); it1 = env.it('spec 1', function() {}); it2 = env.xit('spec 2', function() {}); it3 = env.it('spec 3', function() {}); }); env.describe('test suite 2', function() { it4 = env.it('spec 4', function() {}); }); expect(it0.id).toEqual('spec0'); expect(it1.id).toEqual('spec1'); expect(it2.id).toEqual('spec2'); expect(it3.id).toEqual('spec3'); expect(it4.id).toEqual('spec4'); }); it('nested suites', async function() { let foo = 0; let bar = 0; let baz = 0; let quux = 0; env.describe('suite', function() { env.describe('nested', function() { env.it('should run nested suites', function() { foo++; }); env.it('should run nested suites', function() { bar++; }); }); env.describe('nested 2', function() { env.it('should run suites following nested suites', function() { baz++; }); }); env.it('should run tests following nested suites', function() { quux++; }); }); expect(foo).toEqual(0); expect(bar).toEqual(0); expect(baz).toEqual(0); expect(quux).toEqual(0); await env.execute(); expect(foo).toEqual(1); expect(bar).toEqual(1); expect(baz).toEqual(1); expect(quux).toEqual(1); }); it('should permit nested describes', async function() { const actions = []; env.beforeEach(function() { actions.push('topSuite beforeEach'); }); env.afterEach(function() { actions.push('topSuite afterEach'); }); env.describe('Something', function() { env.beforeEach(function() { actions.push('outer beforeEach'); }); env.afterEach(function() { actions.push('outer afterEach'); }); env.it('does it 1', function() { actions.push('outer it 1'); }); env.describe('Inner 1', function() { env.beforeEach(function() { actions.push('inner 1 beforeEach'); }); env.afterEach(function() { actions.push('inner 1 afterEach'); }); env.it('does it 2', function() { actions.push('inner 1 it'); }); }); env.it('does it 3', function() { actions.push('outer it 2'); }); env.describe('Inner 2', function() { env.beforeEach(function() { actions.push('inner 2 beforeEach'); }); env.afterEach(function() { actions.push('inner 2 afterEach'); }); env.it('does it 2', function() { actions.push('inner 2 it'); }); }); }); await env.execute(); const expected = [ 'topSuite beforeEach', 'outer beforeEach', 'outer it 1', 'outer afterEach', 'topSuite afterEach', 'topSuite beforeEach', 'outer beforeEach', 'inner 1 beforeEach', 'inner 1 it', 'inner 1 afterEach', 'outer afterEach', 'topSuite afterEach', 'topSuite beforeEach', 'outer beforeEach', 'outer it 2', 'outer afterEach', 'topSuite afterEach', 'topSuite beforeEach', 'outer beforeEach', 'inner 2 beforeEach', 'inner 2 it', 'inner 2 afterEach', 'outer afterEach', 'topSuite afterEach' ]; expect(actions).toEqual(expected); }); it('should run multiple befores and afters ordered so functions declared later are treated as more specific', async function() { const actions = []; env.beforeAll(function() { actions.push('runner beforeAll1'); }); env.afterAll(function() { actions.push('runner afterAll1'); }); env.beforeAll(function() { actions.push('runner beforeAll2'); }); env.afterAll(function() { actions.push('runner afterAll2'); }); env.beforeEach(function() { actions.push('runner beforeEach1'); }); env.afterEach(function() { actions.push('runner afterEach1'); }); env.beforeEach(function() { actions.push('runner beforeEach2'); }); env.afterEach(function() { actions.push('runner afterEach2'); }); env.describe('Something', function() { env.beforeEach(function() { actions.push('beforeEach1'); }); env.afterEach(function() { actions.push('afterEach1'); }); env.beforeEach(function() { actions.push('beforeEach2'); }); env.afterEach(function() { actions.push('afterEach2'); }); env.it('does it 1', function() { actions.push('outer it 1'); }); }); await env.execute(); const expected = [ 'runner beforeAll1', 'runner beforeAll2', 'runner beforeEach1', 'runner beforeEach2', 'beforeEach1', 'beforeEach2', 'outer it 1', 'afterEach2', 'afterEach1', 'runner afterEach2', 'runner afterEach1', 'runner afterAll2', 'runner afterAll1' ]; expect(actions).toEqual(expected); }); it('should run beforeAlls before beforeEachs and afterAlls after afterEachs', async function() { const actions = []; env.beforeAll(function() { actions.push('runner beforeAll'); }); env.afterAll(function() { actions.push('runner afterAll'); }); env.beforeEach(function() { actions.push('runner beforeEach'); }); env.afterEach(function() { actions.push('runner afterEach'); }); env.describe('Something', function() { env.beforeEach(function() { actions.push('inner beforeEach'); }); env.afterEach(function() { actions.push('inner afterEach'); }); env.beforeAll(function() { actions.push('inner beforeAll'); }); env.afterAll(function() { actions.push('inner afterAll'); }); env.it('does something or other', function() { actions.push('it'); }); }); await env.execute(); const expected = [ 'runner beforeAll', 'inner beforeAll', 'runner beforeEach', 'inner beforeEach', 'it', 'inner afterEach', 'runner afterEach', 'inner afterAll', 'runner afterAll' ]; expect(actions).toEqual(expected); }); it('should run beforeAlls and afterAlls in the order declared when runnablesToRun is provided', async function() { const actions = []; let spec; let spec2; env.beforeAll(function() { actions.push('runner beforeAll'); }); env.afterAll(function() { actions.push('runner afterAll'); }); env.beforeEach(function() { actions.push('runner beforeEach'); }); env.afterEach(function() { actions.push('runner afterEach'); }); env.describe('Something', function() { env.beforeEach(function() { actions.push('inner beforeEach'); }); env.afterEach(function() { actions.push('inner afterEach'); }); env.beforeAll(function() { actions.push('inner beforeAll'); }); env.afterAll(function() { actions.push('inner afterAll'); }); spec = env.it('does something', function() { actions.push('it'); }); spec2 = env.it('does something or other', function() { actions.push('it2'); }); }); await env.execute([spec2.id, spec.id]); const expected = [ 'runner beforeAll', 'inner beforeAll', 'runner beforeEach', 'inner beforeEach', 'it2', 'inner afterEach', 'runner afterEach', 'runner beforeEach', 'inner beforeEach', 'it', 'inner afterEach', 'runner afterEach', 'inner afterAll', 'runner afterAll' ]; expect(actions).toEqual(expected); }); it('only runs *Alls once in a focused suite', async function() { const actions = []; env.fdescribe('Suite', function() { env.beforeAll(function() { actions.push('beforeAll'); }); env.it('should run beforeAll once', function() { actions.push('spec'); }); env.afterAll(function() { actions.push('afterAll'); }); }); await env.execute(); expect(actions).toEqual(['beforeAll', 'spec', 'afterAll']); }); describe('focused runnables', function() { it('runs the relevant alls and eachs for each runnable', async function() { const actions = []; env.beforeAll(function() { actions.push('beforeAll'); }); env.afterAll(function() { actions.push('afterAll'); }); env.beforeEach(function() { actions.push('beforeEach'); }); env.afterEach(function() { actions.push('afterEach'); }); env.fdescribe('a focused suite', function() { env.it('is run', function() { actions.push('spec in fdescribe'); }); }); env.describe('an unfocused suite', function() { env.fit('has a focused spec', function() { actions.push('focused spec'); }); }); await env.execute(); const expected = [ 'beforeAll', 'beforeEach', 'spec in fdescribe', 'afterEach', 'beforeEach', 'focused spec', 'afterEach', 'afterAll' ]; expect(actions).toEqual(expected); }); it('focused specs in focused suites cause non-focused siblings to not run', async function() { const actions = []; env.fdescribe('focused suite', function() { env.it('unfocused spec', function() { actions.push('unfocused spec'); }); env.fit('focused spec', function() { actions.push('focused spec'); }); }); await env.execute(); const expected = ['focused spec']; expect(actions).toEqual(expected); }); it('focused suites in focused suites cause non-focused siblings to not run', async function() { const actions = []; env.fdescribe('focused suite', function() { env.it('unfocused spec', function() { actions.push('unfocused spec'); }); env.fdescribe('inner focused suite', function() { env.it('inner spec', function() { actions.push('inner spec'); }); }); }); await env.execute(); const expected = ['inner spec']; expect(actions).toEqual(expected); }); it('focused runnables unfocus ancestor focused suites', async function() { const actions = []; env.fdescribe('focused suite', function() { env.it('unfocused spec', function() { actions.push('unfocused spec'); }); env.describe('inner focused suite', function() { env.fit('focused spec', function() { actions.push('focused spec'); }); }); }); await env.execute(); const expected = ['focused spec']; expect(actions).toEqual(expected); }); }); it("shouldn't run disabled suites", async function() { const specInADisabledSuite = jasmine.createSpy('specInADisabledSuite'); env.describe('A Suite', function() { env.xdescribe('with a disabled suite', function() { env.it('spec inside a disabled suite', specInADisabledSuite); }); }); await env.execute(); expect(specInADisabledSuite).not.toHaveBeenCalled(); }); it("shouldn't run before/after functions in disabled suites", async function() { const shouldNotRun = jasmine.createSpy('shouldNotRun'); env.xdescribe('A disabled Suite', function() { // None of the before/after functions should run. env.beforeAll(shouldNotRun); env.beforeEach(shouldNotRun); env.afterEach(shouldNotRun); env.afterAll(shouldNotRun); env.it('spec inside a disabled suite', shouldNotRun); }); await env.execute(); expect(shouldNotRun).not.toHaveBeenCalled(); }); it('should allow top level suites to be disabled', async function() { const specInADisabledSuite = jasmine.createSpy('specInADisabledSuite'), otherSpec = jasmine.createSpy('otherSpec'); env.xdescribe('A disabled suite', function() { env.it('spec inside a disabled suite', specInADisabledSuite); }); env.describe('Another suite', function() { env.it('another spec', otherSpec); }); await env.execute(); expect(specInADisabledSuite).not.toHaveBeenCalled(); expect(otherSpec).toHaveBeenCalled(); }); it('should set all pending specs to pending when a suite is run', async function() { env.describe('default current suite', function() { env.it('I am a pending spec'); }); const reporter = jasmine.createSpyObj('reporter', ['specDone']); env.addReporter(reporter); await env.execute(); expect(reporter.specDone).toHaveBeenCalledWith( jasmine.objectContaining({ status: 'pending' }) ); }); it('should recover gracefully when there are errors in describe functions', async function() { const specs = [], reporter = jasmine.createSpyObj(['specDone', 'suiteDone']); reporter.specDone.and.callFake(function(result) { specs.push(result.fullName); }); expect(function() { env.describe('outer1', function() { env.describe('inner1', function() { env.it('should thingy', function() { this.expect(true).toEqual(true); }); throw new Error('inner error'); }); env.describe('inner2', function() { env.it('should other thingy', function() { this.expect(true).toEqual(true); }); }); throw new Error('outer error'); }); }).not.toThrow(); env.describe('outer2', function() { env.it('should xxx', function() { this.expect(true).toEqual(true); }); }); env.addReporter(reporter); await env.execute(); expect(specs).toEqual([ 'outer1 inner1 should thingy', 'outer1 inner2 should other thingy', 'outer2 should xxx' ]); expect(reporter.suiteDone).toHaveFailedExpectationsForRunnable( 'outer1 inner1', [/inner error/] ); expect(reporter.suiteDone).toHaveFailedExpectationsForRunnable('outer1', [ /outer error/ ]); }); it('re-enters suites that have no *Alls', async function() { const actions = []; let spec1; let spec2; let spec3; env.describe('top', function() { spec1 = env.it('spec1', function() { actions.push('spec1'); }); spec2 = env.it('spec2', function() { actions.push('spec2'); }); }); spec3 = env.it('spec3', function() { actions.push('spec3'); }); await env.execute([spec2.id, spec3.id, spec1.id]); expect(actions).toEqual(['spec2', 'spec3', 'spec1']); }); it('refuses to re-enter suites with a beforeAll', function() { const actions = []; let spec1; let spec2; let spec3; env.describe('top', function() { env.beforeAll(function() {}); spec1 = env.it('spec1', function() { actions.push('spec1'); }); spec2 = env.it('spec2', function() { actions.push('spec2'); }); }); spec3 = env.it('spec3', function() { actions.push('spec3'); }); expect(function() { env.execute([spec2.id, spec3.id, spec1.id]); }).toThrowError(/beforeAll/); expect(actions).toEqual([]); }); it('refuses to re-enter suites with a afterAll', function() { const actions = []; let spec1; let spec2; let spec3; env.describe('top', function() { env.afterAll(function() {}); spec1 = env.it('spec1', function() { actions.push('spec1'); }); spec2 = env.it('spec2', function() { actions.push('spec2'); }); }); spec3 = env.it('spec3', function() { actions.push('spec3'); }); expect(function() { env.execute([spec2.id, spec3.id, spec1.id]); }).toThrowError(/afterAll/); expect(actions).toEqual([]); }); it('should run the tests in a consistent order when a seed is supplied', async function() { const actions = []; env.configure({ random: true, seed: '123456' }); env.beforeEach(function() { actions.push('topSuite beforeEach'); }); env.afterEach(function() { actions.push('topSuite afterEach'); }); env.describe('Something', function() { env.beforeEach(function() { actions.push('outer beforeEach'); }); env.afterEach(function() { actions.push('outer afterEach'); }); env.it('does it 1', function() { actions.push('outer it 1'); }); env.describe('Inner 1', function() { env.beforeEach(function() { actions.push('inner 1 beforeEach'); }); env.afterEach(function() { actions.push('inner 1 afterEach'); }); env.it('does it 2', function() { actions.push('inner 1 it'); }); }); env.it('does it 3', function() { actions.push('outer it 2'); }); env.describe('Inner 2', function() { env.beforeEach(function() { actions.push('inner 2 beforeEach'); }); env.afterEach(function() { actions.push('inner 2 afterEach'); }); env.it('does it 2', function() { actions.push('inner 2 it'); }); }); }); await env.execute(); const expected = [ 'topSuite beforeEach', 'outer beforeEach', 'outer it 2', 'outer afterEach', 'topSuite afterEach', 'topSuite beforeEach', 'outer beforeEach', 'inner 2 beforeEach', 'inner 2 it', 'inner 2 afterEach', 'outer afterEach', 'topSuite afterEach', 'topSuite beforeEach', 'outer beforeEach', 'inner 1 beforeEach', 'inner 1 it', 'inner 1 afterEach', 'outer afterEach', 'topSuite afterEach', 'topSuite beforeEach', 'outer beforeEach', 'outer it 1', 'outer afterEach', 'topSuite afterEach' ]; expect(actions).toEqual(expected); }); function hasStandardErrorHandlingBehavior() { it('skips to cleanup functions after a thrown error', async function() { const actions = []; env.describe('Something', function() { env.beforeEach(function() { actions.push('outer beforeEach'); throw new Error('error'); }); env.afterEach(function() { actions.push('outer afterEach'); }); env.describe('Inner', function() { env.beforeEach(function() { actions.push('inner beforeEach'); }); env.afterEach(function() { actions.push('inner afterEach'); }); env.it('does it', function() { actions.push('inner it'); }); }); }); await env.execute(); expect(actions).toEqual(['outer beforeEach', 'outer afterEach']); }); it('skips to cleanup functions after a rejected promise', async function() { const actions = []; env.describe('Something', function() { env.beforeEach(function() { actions.push('outer beforeEach'); return Promise.reject(new Error('error')); }); env.afterEach(function() { actions.push('outer afterEach'); }); env.describe('Inner', function() { env.beforeEach(function() { actions.push('inner beforeEach'); }); env.afterEach(function() { actions.push('inner afterEach'); }); env.it('does it', function() { actions.push('inner it'); }); }); }); await env.execute(); expect(actions).toEqual(['outer beforeEach', 'outer afterEach']); }); it('skips to cleanup functions after done.fail is called', async function() { const actions = []; env.describe('Something', function() { env.beforeEach(function(done) { actions.push('beforeEach'); done.fail('error'); }); env.afterEach(function() { actions.push('afterEach'); }); env.it('does it', function() { actions.push('it'); }); }); await env.execute(); expect(actions).toEqual(['beforeEach', 'afterEach']); }); it('skips to cleanup functions when an async function times out', async function() { const actions = []; env.describe('Something', function() { // eslint-disable-next-line no-unused-vars env.beforeEach(function(innerDone) { actions.push('beforeEach'); }, 1); env.afterEach(function() { actions.push('afterEach'); }); env.it('does it', function() { actions.push('it'); }); }); await env.execute(); expect(actions).toEqual(['beforeEach', 'afterEach']); }); it('skips to cleanup functions after pending() is called', async function() { const actions = []; env.describe('Something', function() { env.beforeEach(function() { actions.push('outer beforeEach'); pending(); }); env.afterEach(function() { actions.push('outer afterEach'); }); env.describe('Inner', function() { env.beforeEach(function() { actions.push('inner beforeEach'); }); env.afterEach(function() { actions.push('inner afterEach'); }); env.it('does it', function() { actions.push('inner it'); }); }); }); await env.execute(); expect(actions).toEqual(['outer beforeEach', 'outer afterEach']); }); it('runs all reporter callbacks even if one fails', async function() { const laterReporter = jasmine.createSpyObj('laterReporter', ['specDone']); env.it('a spec', function() {}); env.addReporter({ specDone: function() { throw new Error('nope'); } }); env.addReporter(laterReporter); await env.execute(); expect(laterReporter.specDone).toHaveBeenCalled(); }); it('skips cleanup functions that are defined in child suites when a beforeEach errors', async function() { const parentAfterEachFn = jasmine.createSpy('parentAfterEachFn'); const childAfterEachFn = jasmine.createSpy('childAfterEachFn'); env.describe('parent suite', function() { env.beforeEach(function() { throw new Error('nope'); }); env.afterEach(parentAfterEachFn); env.describe('child suite', function() { env.it('a spec', function() {}); env.afterEach(childAfterEachFn); }); }); await env.execute(); expect(parentAfterEachFn).toHaveBeenCalled(); expect(childAfterEachFn).not.toHaveBeenCalled(); }); } describe('When stopSpecOnExpectationFailure is true', function() { beforeEach(function() { env.configure({ stopSpecOnExpectationFailure: true }); }); hasStandardErrorHandlingBehavior(); it('skips to cleanup functions after an expectation failure', async function() { const actions = []; env.describe('Something', function() { env.beforeEach(function() { actions.push('outer beforeEach'); env.expect(1).toBe(2); }); env.afterEach(function() { actions.push('outer afterEach'); }); env.describe('Inner', function() { env.beforeEach(function() { actions.push('inner beforeEach'); }); env.afterEach(function() { actions.push('inner afterEach'); }); env.it('does it', function() { actions.push('inner it'); }); }); }); await env.execute(); expect(actions).toEqual(['outer beforeEach', 'outer afterEach']); }); }); describe('When stopSpecOnExpectationFailure is false', function() { beforeEach(function() { env.configure({ stopSpecOnExpectationFailure: false }); }); hasStandardErrorHandlingBehavior(); it('does not skip anything after an expectation failure', async function() { const actions = []; env.describe('Something', function() { env.beforeEach(function() { actions.push('outer beforeEach'); env.expect(1).toBe(2); }); env.afterEach(function() { actions.push('outer afterEach'); }); env.describe('Inner', function() { env.beforeEach(function() { actions.push('inner beforeEach'); }); env.afterEach(function() { actions.push('inner afterEach'); }); env.it('does it', function() { actions.push('inner it'); }); }); }); await env.execute(); expect(actions).toEqual([ 'outer beforeEach', 'inner beforeEach', 'inner it', 'inner afterEach', 'outer afterEach' ]); }); }); describe('When a top-level beforeAll function fails', function() { it('skips and reports contained specs', async function() { const outerBeforeEach = jasmine.createSpy('outerBeforeEach'); const nestedBeforeEach = jasmine.createSpy('nestedBeforeEach'); const outerAfterEach = jasmine.createSpy('outerAfterEach'); const nestedAfterEach = jasmine.createSpy('nestedAfterEach'); const outerIt = jasmine.createSpy('outerIt'); const nestedIt = jasmine.createSpy('nestedIt'); const nestedBeforeAll = jasmine.createSpy('nestedBeforeAll'); env.beforeAll(function() { throw new Error('nope'); }); env.beforeEach(outerBeforeEach); env.it('a spec', outerIt); env.describe('a nested suite', function() { env.beforeAll(nestedBeforeAll); env.beforeEach(nestedBeforeEach); env.it('a nested spec', nestedIt); env.afterEach(nestedAfterEach); }); env.afterEach(outerAfterEach); const reporter = jasmine.createSpyObj('reporter', [ 'suiteStarted', 'suiteDone', 'specStarted', 'specDone' ]); env.addReporter(reporter); await env.execute(); expect(outerBeforeEach).not.toHaveBeenCalled(); expect(outerIt).not.toHaveBeenCalled(); expect(nestedBeforeAll).not.toHaveBeenCalled(); expect(nestedBeforeEach).not.toHaveBeenCalled(); expect(nestedIt).not.toHaveBeenCalled(); expect(nestedAfterEach).not.toHaveBeenCalled(); expect(outerAfterEach).not.toHaveBeenCalled(); expect(reporter.suiteStarted).toHaveBeenCalledWith( jasmine.objectContaining({ fullName: 'a nested suite' }) ); // The child suite should be reported as passed, for consistency with // suites that contain failing specs but no suite-level errors. expect(reporter.suiteDone).toHaveBeenCalledWith( jasmine.objectContaining({ fullName: 'a nested suite', status: 'passed', failedExpectations: [] }) ); expect(reporter.specStarted).toHaveBeenCalledWith( jasmine.objectContaining({ fullName: 'a spec' }) ); expect(reporter.specDone).toHaveBeenCalledWith( jasmine.objectContaining({ fullName: 'a spec', status: 'failed', failedExpectations: [ jasmine.objectContaining({ passed: false, message: 'Not run because a beforeAll function failed. The ' + 'beforeAll failure will be reported on the suite that ' + 'caused it.' }) ] }) ); expect(reporter.specStarted).toHaveBeenCalledWith( jasmine.objectContaining({ fullName: 'a nested suite a nested spec' }) ); expect(reporter.specDone).toHaveBeenCalledWith( jasmine.objectContaining({ fullName: 'a nested suite a nested spec', status: 'failed', failedExpectations: [ jasmine.objectContaining({ passed: false, message: 'Not run because a beforeAll function failed. The ' + 'beforeAll failure will be reported on the suite that ' + 'caused it.' }) ] }) ); }); }); describe('When a suite beforeAll function fails', function() { it('skips and reports contained specs', async function() { const outerBeforeEach = jasmine.createSpy('outerBeforeEach'); const nestedBeforeEach = jasmine.createSpy('nestedBeforeEach'); const outerAfterEach = jasmine.createSpy('outerAfterEach'); const nestedAfterEach = jasmine.createSpy('nestedAfterEach'); const outerIt = jasmine.createSpy('outerIt'); const nestedIt = jasmine.createSpy('nestedIt'); const nestedBeforeAll = jasmine.createSpy('nestedBeforeAll'); env.describe('a suite', function() { env.beforeAll(function() { throw new Error('nope'); }); env.beforeEach(outerBeforeEach); env.it('a spec', outerIt); env.describe('a nested suite', function() { env.beforeAll(nestedBeforeAll); env.beforeEach(nestedBeforeEach); env.it('a nested spec', nestedIt); env.afterEach(nestedAfterEach); }); env.afterEach(outerAfterEach); }); const reporter = jasmine.createSpyObj('reporter', [ 'suiteStarted', 'suiteDone', 'specStarted', 'specDone' ]); env.addReporter(reporter); await env.execute(); expect(outerBeforeEach).not.toHaveBeenCalled(); expect(outerIt).not.toHaveBeenCalled(); expect(nestedBeforeAll).not.toHaveBeenCalled(); expect(nestedBeforeEach).not.toHaveBeenCalled(); expect(nestedIt).not.toHaveBeenCalled(); expect(nestedAfterEach).not.toHaveBeenCalled(); expect(outerAfterEach).not.toHaveBeenCalled(); expect(reporter.suiteStarted).toHaveBeenCalledWith( jasmine.objectContaining({ fullName: 'a suite a nested suite' }) ); // The child suite should be reported as passed, for consistency with // suites that contain failing specs but no suite-level errors. expect(reporter.suiteDone).toHaveBeenCalledWith( jasmine.objectContaining({ fullName: 'a suite a nested suite', status: 'passed', failedExpectations: [] }) ); expect(reporter.specStarted).toHaveBeenCalledWith( jasmine.objectContaining({ fullName: 'a suite a spec' }) ); expect(reporter.specDone).toHaveBeenCalledWith( jasmine.objectContaining({ fullName: 'a suite a spec', status: 'failed', failedExpectations: [ jasmine.objectContaining({ passed: false, message: 'Not run because a beforeAll function failed. The ' + 'beforeAll failure will be reported on the suite that ' + 'caused it.' }) ] }) ); expect(reporter.specStarted).toHaveBeenCalledWith( jasmine.objectContaining({ fullName: 'a suite a nested suite a nested spec' }) ); expect(reporter.specDone).toHaveBeenCalledWith( jasmine.objectContaining({ fullName: 'a suite a nested suite a nested spec', status: 'failed', failedExpectations: [ jasmine.objectContaining({ passed: false, message: 'Not run because a beforeAll function failed. The ' + 'beforeAll failure will be reported on the suite that ' + 'caused it.' }) ] }) ); }); it('runs afterAll functions in the current suite and outer scopes', async function() { const outerAfterAll = jasmine.createSpy('outerAfterAll'); const nestedAfterAll = jasmine.createSpy('nestedAfterAll'); const secondNestedAfterAll = jasmine.createSpy('secondNestedAfterAll'); env.describe('a nested suite', function() { env.beforeAll(function() { throw new Error('nope'); }); env.describe('more nesting', function() { env.it('a nested spec', function() {}); env.afterAll(secondNestedAfterAll); }); env.afterAll(nestedAfterAll); }); env.afterAll(outerAfterAll); await env.execute(); expect(secondNestedAfterAll).not.toHaveBeenCalled(); expect(nestedAfterAll).toHaveBeenCalled(); expect(outerAfterAll).toHaveBeenCalled(); }); }); describe('when stopOnSpecFailure is on', function() { it('does not run further specs when one fails', async function() { const actions = []; env.describe('wrapper', function() { env.it('fails', function() { actions.push('fails'); env.expect(1).toBe(2); }); }); env.describe('holder', function() { env.it('does not run', function() { actions.push('does not run'); }); }); env.configure({ random: false }); env.configure({ stopOnSpecFailure: true }); await env.execute(); expect(actions).toEqual(['fails']); }); it('runs afterAll functions', async function() { const actions = []; env.describe('outer suite', function() { env.describe('inner suite', function() { env.it('fails', function() { actions.push('fails'); env.expect(1).toBe(2); }); env.afterAll(function() { actions.push('inner afterAll'); }); }); env.afterAll(function() { actions.push('outer afterAll'); }); }); env.afterAll(function() { actions.push('top afterAll'); }); env.configure({ stopOnSpecFailure: true }); await env.execute(); expect(actions).toEqual([ 'fails', 'inner afterAll', 'outer afterAll', 'top afterAll' ]); }); }); describe('run multiple times', function() { beforeEach(function() { env.configure({ autoCleanClosures: false, random: false }); }); it('should be able to run multiple times', async function() { const actions = []; env.describe('Suite', function() { env.it('spec1', function() { actions.push('spec1'); }); env.describe('inner suite', function() { env.it('spec2', function() { actions.push('spec2'); }); }); }); await env.execute(); expect(actions).toEqual(['spec1', 'spec2']); await env.execute(); expect(actions).toEqual(['spec1', 'spec2', 'spec1', 'spec2']); }); it('should reset results between runs', async function() { const specResults = {}; const suiteResults = {}; let firstExecution = true; env.addReporter({ specDone: function(spec) { specResults[spec.description] = spec.status; }, suiteDone: function(suite) { suiteResults[suite.description] = suite.status; }, jasmineDone: function() { firstExecution = false; } }); env.describe('suite0', function() { env.it('spec1', function() { if (firstExecution) { env.expect(1).toBe(2); } }); env.describe('suite1', function() { env.it('spec2', function() { if (firstExecution) { env.pending(); } }); env.xit('spec3', function() {}); // Always pending }); env.describe('suite2', function() { env.it('spec4', function() { if (firstExecution) { throw new Error('spec 3 fails'); } }); }); env.describe('suite3', function() { env.beforeEach(function() { throw new Error('suite 3 fails'); }); env.it('spec5', function() {}); }); env.xdescribe('suite4', function() { // Always pending env.it('spec6', function() {}); }); env.describe('suite5', function() { env.it('spec7'); }); }); await env.execute(); expect(specResults).toEqual({ spec1: 'failed', spec2: 'pending', spec3: 'pending', spec4: 'failed', spec5: 'failed', spec6: 'pending', spec7: 'pending' }); expect(suiteResults).toEqual({ suite0: 'passed', suite1: 'passed', suite2: 'passed', suite3: 'passed', suite4: 'pending', suite5: 'passed' }); await env.execute(); expect(specResults).toEqual({ spec1: 'passed', spec2: 'passed', spec3: 'pending', spec4: 'passed', spec5: 'failed', spec6: 'pending', spec7: 'pending' }); expect(suiteResults).toEqual({ suite0: 'passed', suite1: 'passed', suite2: 'passed', suite3: 'passed', suite4: 'pending', suite5: 'passed' }); }); it('should execute before and after hooks per run', async function() { let timeline = []; const timelineFn = function(hookName) { return function() { timeline.push(hookName); }; }; const expectedTimeLine = [ 'beforeAll', 'beforeEach', 'spec1', 'afterEach', 'beforeEach', 'spec2', 'afterEach', 'afterAll' ]; env.describe('suite0', function() { env.beforeAll(timelineFn('beforeAll')); env.beforeEach(timelineFn('beforeEach')); env.afterEach(timelineFn('afterEach')); env.afterAll(timelineFn('afterAll')); env.it('spec1', timelineFn('spec1')); env.it('spec2', timelineFn('spec2')); }); await env.execute(); expect(timeline).toEqual(expectedTimeLine); timeline = []; await env.execute(); expect(timeline).toEqual(expectedTimeLine); }); it('should be able to filter out different tests in subsequent runs', async function() { const specResults = {}; let focussedSpec = 'spec1'; env.configure({ specFilter: function(spec) { return spec.description === focussedSpec; } }); env.addReporter({ specDone: function(spec) { specResults[spec.description] = spec.status; } }); env.describe('suite0', function() { env.it('spec1', function() {}); env.it('spec2', function() {}); env.it('spec3', function() {}); }); await env.execute(); expect(specResults).toEqual({ spec1: 'passed', spec2: 'excluded', spec3: 'excluded' }); focussedSpec = 'spec2'; await env.execute(); expect(specResults).toEqual({ spec1: 'excluded', spec2: 'passed', spec3: 'excluded' }); focussedSpec = 'spec3'; await env.execute(); expect(specResults).toEqual({ spec1: 'excluded', spec2: 'excluded', spec3: 'passed' }); }); }); }); jasmine-4.5.0/spec/core/matchers/000077500000000000000000000000001432731766000166475ustar00rootroot00000000000000jasmine-4.5.0/spec/core/matchers/DiffBuilderSpec.js000066400000000000000000000145561432731766000222120ustar00rootroot00000000000000describe('DiffBuilder', function() { it('records the actual and expected objects', function() { const diffBuilder = new jasmineUnderTest.DiffBuilder(); diffBuilder.setRoots({ x: 'actual' }, { x: 'expected' }); diffBuilder.recordMismatch(); expect(diffBuilder.getMessage()).toEqual( "Expected Object({ x: 'actual' }) to equal Object({ x: 'expected' })." ); }); it('prints the path at which the difference was found', function() { const diffBuilder = new jasmineUnderTest.DiffBuilder(); diffBuilder.setRoots({ foo: { x: 'actual' } }, { foo: { x: 'expected' } }); diffBuilder.withPath('foo', function() { diffBuilder.recordMismatch(); }); expect(diffBuilder.getMessage()).toEqual( "Expected $.foo = Object({ x: 'actual' }) to equal Object({ x: 'expected' })." ); }); it('prints multiple messages, separated by newlines', function() { const diffBuilder = new jasmineUnderTest.DiffBuilder(); diffBuilder.setRoots({ foo: 1, bar: 3 }, { foo: 2, bar: 4 }); diffBuilder.withPath('foo', function() { diffBuilder.recordMismatch(); }); diffBuilder.withPath('bar', function() { diffBuilder.recordMismatch(); }); const message = 'Expected $.foo = 1 to equal 2.\n' + 'Expected $.bar = 3 to equal 4.'; expect(diffBuilder.getMessage()).toEqual(message); }); it('allows customization of the message', function() { const diffBuilder = new jasmineUnderTest.DiffBuilder(); diffBuilder.setRoots({ x: 'bar' }, { x: 'foo' }); function darthVaderFormatter(actual, expected, path) { return ( 'I find your lack of ' + expected + ' disturbing. (was ' + actual + ', at ' + path + ')' ); } diffBuilder.withPath('x', function() { diffBuilder.recordMismatch(darthVaderFormatter); }); expect(diffBuilder.getMessage()).toEqual( 'I find your lack of foo disturbing. (was bar, at $.x)' ); }); it('uses the injected pretty-printer', function() { const prettyPrinter = function(val) { return '|' + val + '|'; }, diffBuilder = new jasmineUnderTest.DiffBuilder({ prettyPrinter: prettyPrinter }); prettyPrinter.customFormat_ = function() {}; diffBuilder.setRoots({ foo: 'actual' }, { foo: 'expected' }); diffBuilder.withPath('foo', function() { diffBuilder.recordMismatch(); }); expect(diffBuilder.getMessage()).toEqual( 'Expected $.foo = |actual| to equal |expected|.' ); }); it('passes the injected pretty-printer to the diff formatter', function() { const diffFormatter = jasmine.createSpy('diffFormatter'), prettyPrinter = function() {}, diffBuilder = new jasmineUnderTest.DiffBuilder({ prettyPrinter: prettyPrinter }); prettyPrinter.customFormat_ = function() {}; diffBuilder.setRoots({ x: 'bar' }, { x: 'foo' }); diffBuilder.withPath('x', function() { diffBuilder.recordMismatch(diffFormatter); }); diffBuilder.getMessage(); expect(diffFormatter).toHaveBeenCalledWith( 'bar', 'foo', jasmine.anything(), prettyPrinter ); }); it('uses custom object formatters on leaf nodes', 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, 4); diffBuilder.recordMismatch(); expect(diffBuilder.getMessage()).toEqual( 'Expected [number:5] to equal [number:4].' ); }); it('uses custom object formatters on non leaf nodes', function() { const formatter = function(x) { if (x.hasOwnProperty('a')) { return '[thing with a=' + x.a + ', b=' + JSON.stringify(x.b) + ']'; } }; const prettyPrinter = jasmineUnderTest.makePrettyPrinter([formatter]); const diffBuilder = new jasmineUnderTest.DiffBuilder({ prettyPrinter: prettyPrinter }); const expectedMsg = 'Expected $[0].foo = [thing with a=1, b={"x":42}] to equal [thing with a=1, b={"x":43}].\n' + "Expected $[0].bar = 'yes' to equal 'no'."; diffBuilder.setRoots( [{ foo: { a: 1, b: { x: 42 } }, bar: 'yes' }], [{ foo: { a: 1, b: { x: 43 } }, bar: 'no' }] ); diffBuilder.withPath(0, function() { diffBuilder.withPath('foo', function() { diffBuilder.withPath('b', function() { diffBuilder.withPath('x', function() { diffBuilder.recordMismatch(); }); }); }); diffBuilder.withPath('bar', function() { diffBuilder.recordMismatch(); }); }); expect(diffBuilder.getMessage()).toEqual(expectedMsg); }); it('builds diffs involving asymmetric equality testers that implement valuesForDiff_ at the root', function() { const prettyPrinter = jasmineUnderTest.makePrettyPrinter([]), diffBuilder = new jasmineUnderTest.DiffBuilder({ prettyPrinter: prettyPrinter }), expectedMsg = 'Expected $.foo = 1 to equal 2.\n' + 'Expected $.baz = undefined to equal 3.'; diffBuilder.setRoots( { foo: 1, bar: 2 }, jasmine.objectContaining({ foo: 2, baz: 3 }) ); diffBuilder.withPath('foo', function() { diffBuilder.recordMismatch(); }); diffBuilder.withPath('baz', function() { diffBuilder.recordMismatch(); }); expect(diffBuilder.getMessage()).toEqual(expectedMsg); }); it('builds diffs involving asymmetric equality testers that implement valuesForDiff_ below the root', function() { const prettyPrinter = jasmineUnderTest.makePrettyPrinter([]), diffBuilder = new jasmineUnderTest.DiffBuilder({ prettyPrinter: prettyPrinter }), expectedMsg = 'Expected $.x.foo = 1 to equal 2.\n' + 'Expected $.x.baz = undefined to equal 3.'; diffBuilder.setRoots( { x: { foo: 1, bar: 2 } }, { x: jasmine.objectContaining({ foo: 2, baz: 3 }) } ); diffBuilder.withPath('x', function() { diffBuilder.withPath('foo', function() { diffBuilder.recordMismatch(); }); diffBuilder.withPath('baz', function() { diffBuilder.recordMismatch(); }); }); expect(diffBuilder.getMessage()).toEqual(expectedMsg); }); }); jasmine-4.5.0/spec/core/matchers/MismatchTreeSpec.js000066400000000000000000000104441432731766000224100ustar00rootroot00000000000000describe('MismatchTree', function() { describe('#add', function() { describe('When the path is empty', function() { it('flags the root node as mismatched', function() { const tree = new jasmineUnderTest.MismatchTree(); tree.add(new jasmineUnderTest.ObjectPath([])); expect(tree.isMismatch).toBe(true); }); }); describe('When the path is not empty', function() { it('flags the node as mismatched', function() { const tree = new jasmineUnderTest.MismatchTree(); tree.add(new jasmineUnderTest.ObjectPath(['a', 'b'])); expect(tree.child('a').child('b').isMismatch).toBe(true); }); it('does not flag ancestors as mismatched', function() { const tree = new jasmineUnderTest.MismatchTree(); tree.add(new jasmineUnderTest.ObjectPath(['a', 'b'])); expect(tree.isMismatch).toBe(false); expect(tree.child('a').isMismatch).toBe(false); }); }); it('stores the formatter on only the target node', function() { const tree = new jasmineUnderTest.MismatchTree(); tree.add(new jasmineUnderTest.ObjectPath(['a', 'b']), formatter); expect(tree.formatter).toBeFalsy(); expect(tree.child('a').formatter).toBeFalsy(); expect(tree.child('a').child('b').formatter).toBe(formatter); }); it('stores the path to the node', function() { const tree = new jasmineUnderTest.MismatchTree(); tree.add(new jasmineUnderTest.ObjectPath(['a', 'b']), formatter); expect(tree.child('a').child('b').path.components).toEqual(['a', 'b']); }); }); describe('#traverse', function() { it('calls the callback for all nodes that are or contain mismatches', function() { const tree = new jasmineUnderTest.MismatchTree(); tree.add(new jasmineUnderTest.ObjectPath(['a', 'b']), formatter); tree.add(new jasmineUnderTest.ObjectPath(['c'])); const visit = jasmine.createSpy('visit').and.returnValue(true); tree.traverse(visit); expect(visit).toHaveBeenCalledWith( new jasmineUnderTest.ObjectPath([]), false, undefined ); expect(visit).toHaveBeenCalledWith( new jasmineUnderTest.ObjectPath(['a']), false, undefined ); expect(visit).toHaveBeenCalledWith( new jasmineUnderTest.ObjectPath(['a', 'b']), true, formatter ); expect(visit).toHaveBeenCalledWith( new jasmineUnderTest.ObjectPath(['c']), true, undefined ); }); it('does not call the callback if there are no mismatches', function() { const tree = new jasmineUnderTest.MismatchTree(); const visit = jasmine.createSpy('visit'); tree.traverse(visit); expect(visit).not.toHaveBeenCalled(); }); it('visits parents before children', function() { const tree = new jasmineUnderTest.MismatchTree(); tree.add(new jasmineUnderTest.ObjectPath(['a', 'b'])); const visited = []; tree.traverse(function(path) { visited.push(path); return true; }); expect(visited).toEqual([ new jasmineUnderTest.ObjectPath([]), new jasmineUnderTest.ObjectPath(['a']), new jasmineUnderTest.ObjectPath(['a', 'b']) ]); }); it('visits children in the order they were recorded', function() { const tree = new jasmineUnderTest.MismatchTree(); tree.add(new jasmineUnderTest.ObjectPath(['length'])); tree.add(new jasmineUnderTest.ObjectPath([1])); const visited = []; tree.traverse(function(path) { visited.push(path); return true; }); expect(visited).toEqual([ new jasmineUnderTest.ObjectPath([]), new jasmineUnderTest.ObjectPath(['length']), new jasmineUnderTest.ObjectPath([1]) ]); }); it('does not visit children if the callback returns falsy', function() { const tree = new jasmineUnderTest.MismatchTree(); tree.add(new jasmineUnderTest.ObjectPath(['a', 'b'])); const visited = []; tree.traverse(function(path) { visited.push(path); return path.depth() === 0; }); expect(visited).toEqual([ new jasmineUnderTest.ObjectPath([]), new jasmineUnderTest.ObjectPath(['a']) ]); }); }); function formatter() {} }); jasmine-4.5.0/spec/core/matchers/NullDiffBuilderSpec.js000066400000000000000000000004321432731766000230310ustar00rootroot00000000000000describe('NullDiffBuilder', function() { it('responds to withPath() by calling the passed function', function() { const spy = jasmine.createSpy('callback'); jasmineUnderTest.NullDiffBuilder().withPath('does not matter', spy); expect(spy).toHaveBeenCalled(); }); }); jasmine-4.5.0/spec/core/matchers/ObjectPathSpec.js000066400000000000000000000032711432731766000220460ustar00rootroot00000000000000describe('ObjectPath', function() { const ObjectPath = jasmineUnderTest.ObjectPath; it('represents the path to a node in an object tree', function() { expect(new ObjectPath(['foo', 'bar']).toString()).toEqual('$.foo.bar'); }); it('has a depth', function() { expect(new ObjectPath().depth()).toEqual(0); expect(new ObjectPath(['foo']).depth()).toEqual(1); }); it('renders numbers as array access', function() { expect(new ObjectPath(['foo', 0]).toString()).toEqual('$.foo[0]'); }); it('renders properties that are valid identifiers with dot notation', function() { expect(new ObjectPath(['foo123']).toString()).toEqual('$.foo123'); expect(new ObjectPath(['x_y']).toString()).toEqual('$.x_y'); expect(new ObjectPath(['A$B']).toString()).toEqual('$.A$B'); }); it('renders properties with non-identifier-safe characters with square bracket notation', function() { expect(new ObjectPath(['a b c']).toString()).toEqual("$['a b c']"); expect(new ObjectPath(['1hello']).toString()).toEqual("$['1hello']"); }); it('renders symbols with squre bracket notation', function() { expect(new ObjectPath([Symbol('a')]).toString()).toEqual('$[Symbol(a)]'); }); it('renders as the empty string when empty', function() { expect(new ObjectPath().toString()).toEqual(''); }); it('stringifies properties that are not strings or numbers', function() { expect(new ObjectPath([{}]).toString()).toEqual("$['[object Object]']"); }); it('can be created based on another path', function() { const root = new ObjectPath(); const path = root.add('foo'); expect(path.toString()).toEqual('$.foo'); expect(root.toString()).toEqual(''); }); }); jasmine-4.5.0/spec/core/matchers/async/000077500000000000000000000000001432731766000177645ustar00rootroot00000000000000jasmine-4.5.0/spec/core/matchers/async/toBePendingSpec.js000066400000000000000000000030661432731766000233400ustar00rootroot00000000000000describe('toBePending', function() { it('passes if the actual promise is pending', function() { const matchersUtil = new jasmineUnderTest.MatchersUtil(), matcher = jasmineUnderTest.asyncMatchers.toBePending(matchersUtil), actual = new Promise(function() {}); return matcher.compare(actual).then(function(result) { expect(result).toEqual(jasmine.objectContaining({ pass: true })); }); }); it('fails if the actual promise is resolved', function() { const matchersUtil = new jasmineUnderTest.MatchersUtil(), matcher = jasmineUnderTest.asyncMatchers.toBePending(matchersUtil), actual = Promise.resolve(); return matcher.compare(actual).then(function(result) { expect(result).toEqual(jasmine.objectContaining({ pass: false })); }); }); it('fails if the actual promise is rejected', function() { const matchersUtil = new jasmineUnderTest.MatchersUtil(), matcher = jasmineUnderTest.asyncMatchers.toBePending(matchersUtil), actual = Promise.reject(new Error('promise was rejected')); return matcher.compare(actual).then(function(result) { expect(result).toEqual(jasmine.objectContaining({ pass: false })); }); }); it('fails if actual is not a promise', function() { const matchersUtil = new jasmineUnderTest.MatchersUtil(), matcher = jasmineUnderTest.asyncMatchers.toBePending(matchersUtil), actual = 'not a promise'; function f() { return matcher.compare(actual); } expect(f).toThrowError('Expected toBePending to be called on a promise.'); }); }); jasmine-4.5.0/spec/core/matchers/async/toBeRejectedSpec.js000066400000000000000000000022471432731766000235010ustar00rootroot00000000000000describe('toBeRejected', function() { it('passes if the actual is rejected', function() { const matchersUtil = new jasmineUnderTest.MatchersUtil(), matcher = jasmineUnderTest.asyncMatchers.toBeRejected(matchersUtil), actual = Promise.reject('AsyncExpectationSpec rejection'); return matcher.compare(actual).then(function(result) { expect(result).toEqual(jasmine.objectContaining({ pass: true })); }); }); it('fails if the actual is resolved', function() { const matchersUtil = new jasmineUnderTest.MatchersUtil(), matcher = jasmineUnderTest.asyncMatchers.toBeRejected(matchersUtil), actual = Promise.resolve(); return matcher.compare(actual).then(function(result) { expect(result).toEqual(jasmine.objectContaining({ pass: false })); }); }); it('fails if actual is not a promise', function() { const matchersUtil = new jasmineUnderTest.MatchersUtil(), matcher = jasmineUnderTest.asyncMatchers.toBeRejected(matchersUtil), actual = 'not a promise'; function f() { return matcher.compare(actual); } expect(f).toThrowError('Expected toBeRejected to be called on a promise.'); }); }); jasmine-4.5.0/spec/core/matchers/async/toBeRejectedWithErrorSpec.js000066400000000000000000000162611432731766000253500ustar00rootroot00000000000000describe('#toBeRejectedWithError', function() { it('passes when Error type matches', function() { const matchersUtil = new jasmineUnderTest.MatchersUtil({ pp: jasmineUnderTest.makePrettyPrinter() }), matcher = jasmineUnderTest.asyncMatchers.toBeRejectedWithError( matchersUtil ), actual = Promise.reject(new TypeError('foo')); return matcher.compare(actual, TypeError).then(function(result) { expect(result).toEqual( jasmine.objectContaining({ pass: true, message: 'Expected a promise not to be rejected with TypeError, but it was.' }) ); }); }); it('passes when Error type and message matches', function() { const matchersUtil = new jasmineUnderTest.MatchersUtil({ pp: jasmineUnderTest.makePrettyPrinter() }), matcher = jasmineUnderTest.asyncMatchers.toBeRejectedWithError( matchersUtil ), actual = Promise.reject(new TypeError('foo')); return matcher.compare(actual, TypeError, 'foo').then(function(result) { expect(result).toEqual( jasmine.objectContaining({ pass: true, message: "Expected a promise not to be rejected with TypeError: 'foo', but it was." }) ); }); }); it('passes when Error matches and is exactly Error', function() { const matchersUtil = new jasmineUnderTest.MatchersUtil({ pp: jasmineUnderTest.makePrettyPrinter() }), matcher = jasmineUnderTest.asyncMatchers.toBeRejectedWithError( matchersUtil ), actual = Promise.reject(new Error()); return matcher.compare(actual, Error).then(function(result) { expect(result).toEqual( jasmine.objectContaining({ pass: true, message: 'Expected a promise not to be rejected with Error, but it was.' }) ); }); }); it('passes when Error message matches a string', function() { const matchersUtil = new jasmineUnderTest.MatchersUtil({ pp: jasmineUnderTest.makePrettyPrinter() }), matcher = jasmineUnderTest.asyncMatchers.toBeRejectedWithError( matchersUtil ), actual = Promise.reject(new Error('foo')); return matcher.compare(actual, 'foo').then(function(result) { expect(result).toEqual( jasmine.objectContaining({ pass: true, message: "Expected a promise not to be rejected with Error: 'foo', but it was." }) ); }); }); it('passes when Error message matches a RegExp', function() { const matchersUtil = new jasmineUnderTest.MatchersUtil({ pp: jasmineUnderTest.makePrettyPrinter() }), matcher = jasmineUnderTest.asyncMatchers.toBeRejectedWithError( matchersUtil ), actual = Promise.reject(new Error('foo')); return matcher.compare(actual, /foo/).then(function(result) { expect(result).toEqual( jasmine.objectContaining({ pass: true, message: 'Expected a promise not to be rejected with Error: /foo/, but it was.' }) ); }); }); it('passes when Error message is empty', function() { const matchersUtil = new jasmineUnderTest.MatchersUtil({ pp: jasmineUnderTest.makePrettyPrinter() }), matcher = jasmineUnderTest.asyncMatchers.toBeRejectedWithError( matchersUtil ), actual = Promise.reject(new Error()); return matcher.compare(actual, '').then(function(result) { expect(result).toEqual( jasmine.objectContaining({ pass: true, message: "Expected a promise not to be rejected with Error: '', but it was." }) ); }); }); it('passes when no arguments', function() { const matchersUtil = new jasmineUnderTest.MatchersUtil({ pp: jasmineUnderTest.makePrettyPrinter() }), matcher = jasmineUnderTest.asyncMatchers.toBeRejectedWithError( matchersUtil ), actual = Promise.reject(new Error()); return matcher.compare(actual, void 0).then(function(result) { expect(result).toEqual( jasmine.objectContaining({ pass: true, message: 'Expected a promise not to be rejected with Error, but it was.' }) ); }); }); it('fails when resolved', function() { const matchersUtil = new jasmineUnderTest.MatchersUtil({ pp: jasmineUnderTest.makePrettyPrinter() }), matcher = jasmineUnderTest.asyncMatchers.toBeRejectedWithError( matchersUtil ), actual = Promise.resolve(new Error('foo')); return matcher.compare(actual, 'foo').then(function(result) { expect(result).toEqual( jasmine.objectContaining({ pass: false, message: 'Expected a promise to be rejected but it was resolved.' }) ); }); }); it('fails when rejected with non Error type', function() { const matchersUtil = new jasmineUnderTest.MatchersUtil({ pp: jasmineUnderTest.makePrettyPrinter() }), matcher = jasmineUnderTest.asyncMatchers.toBeRejectedWithError( matchersUtil ), actual = Promise.reject('foo'); return matcher.compare(actual, 'foo').then(function(result) { expect(result).toEqual( jasmine.objectContaining({ pass: false, message: "Expected a promise to be rejected with Error: 'foo' but it was rejected with 'foo'." }) ); }); }); it('fails when Error type mismatches', function() { const matchersUtil = new jasmineUnderTest.MatchersUtil({ pp: jasmineUnderTest.makePrettyPrinter() }), matcher = jasmineUnderTest.asyncMatchers.toBeRejectedWithError( matchersUtil ), actual = Promise.reject(new Error('foo')); return matcher.compare(actual, TypeError, 'foo').then(function(result) { expect(result).toEqual( jasmine.objectContaining({ pass: false, message: "Expected a promise to be rejected with TypeError: 'foo' but it was rejected with type Error." }) ); }); }); it('fails when Error message mismatches', function() { const matchersUtil = new jasmineUnderTest.MatchersUtil({ pp: jasmineUnderTest.makePrettyPrinter() }), matcher = jasmineUnderTest.asyncMatchers.toBeRejectedWithError( matchersUtil ), actual = Promise.reject(new Error('foo')); return matcher.compare(actual, 'bar').then(function(result) { expect(result).toEqual( jasmine.objectContaining({ pass: false, message: "Expected a promise to be rejected with Error: 'bar' but it was rejected with Error: foo." }) ); }); }); it('fails if actual is not a promise', function() { const matchersUtil = new jasmineUnderTest.MatchersUtil({ pp: jasmineUnderTest.makePrettyPrinter() }), matcher = jasmineUnderTest.asyncMatchers.toBeRejectedWithError( matchersUtil ), actual = 'not a promise'; function f() { return matcher.compare(actual); } expect(f).toThrowError( 'Expected toBeRejectedWithError to be called on a promise.' ); }); }); jasmine-4.5.0/spec/core/matchers/async/toBeRejectedWithSpec.js000066400000000000000000000060421432731766000243320ustar00rootroot00000000000000describe('#toBeRejectedWith', function() { it('should return true if the promise is rejected with the expected value', function() { const matchersUtil = new jasmineUnderTest.MatchersUtil(), matcher = jasmineUnderTest.asyncMatchers.toBeRejectedWith(matchersUtil), actual = Promise.reject({ error: 'PEBCAK' }); return matcher.compare(actual, { error: 'PEBCAK' }).then(function(result) { expect(result).toEqual(jasmine.objectContaining({ pass: true })); }); }); it('should fail if the promise resolves', function() { const matchersUtil = new jasmineUnderTest.MatchersUtil(), matcher = jasmineUnderTest.asyncMatchers.toBeRejectedWith(matchersUtil), actual = Promise.resolve(); return matcher.compare(actual, '').then(function(result) { expect(result).toEqual(jasmine.objectContaining({ pass: false })); }); }); it('should fail if the promise is rejected with a different value', function() { const matchersUtil = new jasmineUnderTest.MatchersUtil({ pp: jasmineUnderTest.makePrettyPrinter() }), matcher = jasmineUnderTest.asyncMatchers.toBeRejectedWith(matchersUtil), actual = Promise.reject('A Bad Apple'); return matcher.compare(actual, 'Some Cool Thing').then(function(result) { expect(result).toEqual( jasmine.objectContaining({ pass: false, message: "Expected a promise to be rejected with 'Some Cool Thing' but it was rejected with 'A Bad Apple'." }) ); }); }); it('should build its error correctly when negated', function() { const matchersUtil = new jasmineUnderTest.MatchersUtil({ pp: jasmineUnderTest.makePrettyPrinter() }), matcher = jasmineUnderTest.asyncMatchers.toBeRejectedWith(matchersUtil), actual = Promise.reject(true); return matcher.compare(actual, true).then(function(result) { expect(result).toEqual( jasmine.objectContaining({ pass: true, message: 'Expected a promise not to be rejected with true.' }) ); }); }); it('should support custom equality testers', function() { const customEqualityTesters = [ function() { return true; } ], matchersUtil = new jasmineUnderTest.MatchersUtil({ customTesters: customEqualityTesters }), matcher = jasmineUnderTest.asyncMatchers.toBeRejectedWith(matchersUtil), actual = Promise.reject('actual'); return matcher.compare(actual, 'expected').then(function(result) { expect(result).toEqual(jasmine.objectContaining({ pass: true })); }); }); it('fails if actual is not a promise', function() { const matchersUtil = new jasmineUnderTest.MatchersUtil({ pp: jasmineUnderTest.makePrettyPrinter() }), matcher = jasmineUnderTest.asyncMatchers.toBeRejectedWith(matchersUtil), actual = 'not a promise'; function f() { return matcher.compare(actual); } expect(f).toThrowError( 'Expected toBeRejectedWith to be called on a promise.' ); }); }); jasmine-4.5.0/spec/core/matchers/async/toBeResolvedSpec.js000066400000000000000000000025611432731766000235360ustar00rootroot00000000000000describe('toBeResolved', function() { it('passes if the actual is resolved', function() { const matchersUtil = new jasmineUnderTest.MatchersUtil(), matcher = jasmineUnderTest.asyncMatchers.toBeResolved(matchersUtil), actual = Promise.resolve(); return matcher.compare(actual).then(function(result) { expect(result).toEqual(jasmine.objectContaining({ pass: true })); }); }); it('fails if the actual is rejected', function() { const matchersUtil = new jasmineUnderTest.MatchersUtil({ pp: jasmineUnderTest.makePrettyPrinter([]) }), matcher = jasmineUnderTest.asyncMatchers.toBeResolved(matchersUtil), actual = Promise.reject(new Error('AsyncExpectationSpec rejection')); return matcher.compare(actual).then(function(result) { expect(result).toEqual({ pass: false, message: 'Expected a promise to be resolved but it was rejected ' + 'with Error: AsyncExpectationSpec rejection.' }); }); }); it('fails if actual is not a promise', function() { const matchersUtil = new jasmineUnderTest.MatchersUtil(), matcher = jasmineUnderTest.asyncMatchers.toBeResolved(matchersUtil), actual = 'not a promise'; function f() { return matcher.compare(actual); } expect(f).toThrowError('Expected toBeResolved to be called on a promise.'); }); }); jasmine-4.5.0/spec/core/matchers/async/toBeResolvedToSpec.js000066400000000000000000000064501432731766000240420ustar00rootroot00000000000000describe('#toBeResolvedTo', function() { it('passes if the promise is resolved to the expected value', function() { const matchersUtil = new jasmineUnderTest.MatchersUtil(), matcher = jasmineUnderTest.asyncMatchers.toBeResolvedTo(matchersUtil), actual = Promise.resolve({ foo: 42 }); return matcher.compare(actual, { foo: 42 }).then(function(result) { expect(result).toEqual(jasmine.objectContaining({ pass: true })); }); }); it('fails if the promise is rejected', function() { const matchersUtil = new jasmineUnderTest.MatchersUtil({ pp: jasmineUnderTest.makePrettyPrinter() }), matcher = jasmineUnderTest.asyncMatchers.toBeResolvedTo(matchersUtil), actual = Promise.reject(new Error('AsyncExpectationSpec error')); return matcher.compare(actual, '').then(function(result) { expect(result).toEqual( jasmine.objectContaining({ pass: false, message: "Expected a promise to be resolved to '' but it was rejected " + 'with Error: AsyncExpectationSpec error.' }) ); }); }); it('fails if the promise is resolved to a different value', function() { const matchersUtil = new jasmineUnderTest.MatchersUtil({ pp: jasmineUnderTest.makePrettyPrinter() }), matcher = jasmineUnderTest.asyncMatchers.toBeResolvedTo(matchersUtil), actual = Promise.resolve({ foo: 17 }); return matcher.compare(actual, { foo: 42 }).then(function(result) { expect(result).toEqual( jasmine.objectContaining({ pass: false, message: 'Expected a promise to be resolved to Object({ foo: 42 }) but it was resolved to Object({ foo: 17 }).' }) ); }); }); it('builds its message correctly when negated', function() { const matchersUtil = new jasmineUnderTest.MatchersUtil({ pp: jasmineUnderTest.makePrettyPrinter() }), matcher = jasmineUnderTest.asyncMatchers.toBeResolvedTo(matchersUtil), actual = Promise.resolve(true); return matcher.compare(actual, true).then(function(result) { expect(result).toEqual( jasmine.objectContaining({ pass: true, message: 'Expected a promise not to be resolved to true.' }) ); }); }); it('supports custom equality testers', function() { const customEqualityTesters = [ function() { return true; } ], matchersUtil = new jasmineUnderTest.MatchersUtil({ customTesters: customEqualityTesters, pp: jasmineUnderTest.makePrettyPrinter() }), matcher = jasmineUnderTest.asyncMatchers.toBeResolvedTo(matchersUtil), actual = Promise.resolve('actual'); return matcher.compare(actual, 'expected').then(function(result) { expect(result).toEqual(jasmine.objectContaining({ pass: true })); }); }); it('fails if actual is not a promise', function() { const matchersUtil = new jasmineUnderTest.MatchersUtil({ pp: jasmineUnderTest.makePrettyPrinter() }), matcher = jasmineUnderTest.asyncMatchers.toBeResolvedTo(matchersUtil), actual = 'not a promise'; function f() { return matcher.compare(actual); } expect(f).toThrowError( 'Expected toBeResolvedTo to be called on a promise.' ); }); }); jasmine-4.5.0/spec/core/matchers/matchersUtilSpec.js000066400000000000000000001132731432731766000224730ustar00rootroot00000000000000describe('matchersUtil', function() { it('exposes the injected pretty-printer as .pp', function() { const pp = function() {}, matchersUtil = new jasmineUnderTest.MatchersUtil({ pp: pp }); expect(matchersUtil.pp).toBe(pp); }); describe('equals', function() { it('passes for literals that are triple-equal', function() { const matchersUtil = new jasmineUnderTest.MatchersUtil(); expect(matchersUtil.equals(null, null)).toBe(true); expect(matchersUtil.equals(void 0, void 0)).toBe(true); }); it('fails for things that are not equivalent', function() { const matchersUtil = new jasmineUnderTest.MatchersUtil(); expect(matchersUtil.equals({ a: 'foo' }, 1)).toBe(false); }); it('passes for Strings that are equivalent', function() { const matchersUtil = new jasmineUnderTest.MatchersUtil(); expect(matchersUtil.equals('foo', 'foo')).toBe(true); }); it('fails for Strings that are not equivalent', function() { const matchersUtil = new jasmineUnderTest.MatchersUtil(); expect(matchersUtil.equals('foo', 'bar')).toBe(false); }); it('passes for Numbers that are equivalent', function() { const matchersUtil = new jasmineUnderTest.MatchersUtil(); expect(matchersUtil.equals(123, 123)).toBe(true); }); it('fails for Numbers that are not equivalent', function() { const matchersUtil = new jasmineUnderTest.MatchersUtil(); expect(matchersUtil.equals(123, 456)).toBe(false); }); it('fails for a Number and a String that have equivalent values', function() { const matchersUtil = new jasmineUnderTest.MatchersUtil(); expect(matchersUtil.equals(123, '123')).toBe(false); }); it('passes for Dates that are equivalent', function() { const matchersUtil = new jasmineUnderTest.MatchersUtil(); expect( matchersUtil.equals(new Date('Jan 1, 1970'), new Date('Jan 1, 1970')) ).toBe(true); }); it('fails for Dates that are not equivalent', function() { const matchersUtil = new jasmineUnderTest.MatchersUtil(); expect( matchersUtil.equals(new Date('Jan 1, 1970'), new Date('Feb 3, 1991')) ).toBe(false); }); it('passes for Booleans that are equivalent', function() { const matchersUtil = new jasmineUnderTest.MatchersUtil(); expect(matchersUtil.equals(true, true)).toBe(true); }); it('fails for Booleans that are not equivalent', function() { const matchersUtil = new jasmineUnderTest.MatchersUtil(); expect(matchersUtil.equals(true, false)).toBe(false); }); it('passes for RegExps that are equivalent', function() { const matchersUtil = new jasmineUnderTest.MatchersUtil(); expect(matchersUtil.equals(/foo/, /foo/)).toBe(true); }); it('fails for RegExps that are not equivalent', function() { const matchersUtil = new jasmineUnderTest.MatchersUtil(); expect(matchersUtil.equals(/foo/, /bar/)).toBe(false); expect( matchersUtil.equals(new RegExp('foo', 'i'), new RegExp('foo')) ).toBe(false); }); it('passes for Arrays that are equivalent', function() { const matchersUtil = new jasmineUnderTest.MatchersUtil(); expect(matchersUtil.equals([1, 2], [1, 2])).toBe(true); }); it('passes for Arrays that are equivalent, with elements added by changing length', function() { const foo = [], matchersUtil = new jasmineUnderTest.MatchersUtil(); foo.length = 1; expect(matchersUtil.equals(foo, [undefined])).toBe(true); }); it('fails for Arrays that have different lengths', function() { const matchersUtil = new jasmineUnderTest.MatchersUtil(); expect(matchersUtil.equals([1, 2], [1, 2, 3])).toBe(false); }); it('fails for Arrays that have different elements', function() { const matchersUtil = new jasmineUnderTest.MatchersUtil(); expect(matchersUtil.equals([1, 2, 3], [1, 5, 3])).toBe(false); }); it('fails for Arrays whose contents are equivalent, but have differing properties', function() { const one = [1, 2, 3], two = [1, 2, 3], matchersUtil = new jasmineUnderTest.MatchersUtil(); one.foo = 'bar'; two.foo = 'baz'; expect(matchersUtil.equals(one, two)).toBe(false); }); it('passes for Arrays with equivalent contents and properties', function() { const one = [1, 2, 3], two = [1, 2, 3], matchersUtil = new jasmineUnderTest.MatchersUtil(); one.foo = 'bar'; two.foo = 'bar'; expect(matchersUtil.equals(one, two)).toBe(true); }); it('handles symbol keys in Arrays', function() { const matchersUtil = new jasmineUnderTest.MatchersUtil(), sym = Symbol('foo'), arr1 = []; let arr2 = []; arr1[sym] = 'bar'; arr2[sym] = 'bar'; expect(matchersUtil.equals(arr1, arr2)).toBe(true); arr2[sym] = 'baz'; expect(matchersUtil.equals(arr1, arr2)).toBe(false); arr2 = []; arr2[Symbol('foo')] = 'bar'; expect(matchersUtil.equals(arr1, arr2)).toBe(false); arr2 = []; arr2['foo'] = 'bar'; expect(matchersUtil.equals(arr1, arr2)).toBe(false); }); it('passes for Errors that are the same type and have the same message', function() { const matchersUtil = new jasmineUnderTest.MatchersUtil(); expect(matchersUtil.equals(new Error('foo'), new Error('foo'))).toBe( true ); }); it('fails for Errors that are the same type and have different messages', function() { const matchersUtil = new jasmineUnderTest.MatchersUtil(); expect(matchersUtil.equals(new Error('foo'), new Error('bar'))).toBe( false ); }); it('fails for objects with different constructors', function() { const matchersUtil = new jasmineUnderTest.MatchersUtil(); function One() {} function Two() {} expect(matchersUtil.equals(new One(), new Two())).toBe(false); }); it('passes for Objects that are equivalent (simple case)', function() { const matchersUtil = new jasmineUnderTest.MatchersUtil(); expect(matchersUtil.equals({ a: 'foo' }, { a: 'foo' })).toBe(true); }); it('fails for Objects that are not equivalent (simple case)', function() { const matchersUtil = new jasmineUnderTest.MatchersUtil(); expect(matchersUtil.equals({ a: 'foo' }, { a: 'bar' })).toBe(false); }); it('passes for Objects that are equivalent (deep case)', function() { const matchersUtil = new jasmineUnderTest.MatchersUtil(); expect( matchersUtil.equals( { a: 'foo', b: { c: 'bar' } }, { a: 'foo', b: { c: 'bar' } } ) ).toBe(true); }); it('fails for Objects that are not equivalent (deep case)', function() { const matchersUtil = new jasmineUnderTest.MatchersUtil(); expect( matchersUtil.equals( { a: 'foo', b: { c: 'baz' } }, { a: 'foo', b: { c: 'bar' } } ) ).toBe(false); }); it('passes for Objects that are equivalent (with cycles)', function() { const actual = { a: 'foo' }, expected = { a: 'foo' }, matchersUtil = new jasmineUnderTest.MatchersUtil(); actual.b = actual; expected.b = actual; expect(matchersUtil.equals(actual, expected)).toBe(true); }); it('fails for Objects that are not equivalent (with cycles)', function() { const actual = { a: 'foo' }, expected = { a: 'bar' }, matchersUtil = new jasmineUnderTest.MatchersUtil(); actual.b = actual; expected.b = actual; expect(matchersUtil.equals(actual, expected)).toBe(false); }); it('fails for Objects that have the same number of keys, but different keys/values', function() { const expected = { a: undefined }, actual = { b: 1 }, matchersUtil = new jasmineUnderTest.MatchersUtil(); expect(matchersUtil.equals(actual, expected)).toBe(false); }); it('fails when comparing an empty object to an empty array (issue #114)', function() { const emptyObject = {}, emptyArray = [], matchersUtil = new jasmineUnderTest.MatchersUtil(); expect(matchersUtil.equals(emptyObject, emptyArray)).toBe(false); expect(matchersUtil.equals(emptyArray, emptyObject)).toBe(false); }); it('passes for equivalent frozen objects (GitHub issue #266)', function() { const a = { foo: 1 }, b = { foo: 1 }, matchersUtil = new jasmineUnderTest.MatchersUtil(); Object.freeze(a); Object.freeze(b); expect(matchersUtil.equals(a, b)).toBe(true); }); it('passes for equivalent Promises (GitHub issue #1314)', function() { if (typeof Promise === 'undefined') { return; } const p1 = new Promise(function() {}), p2 = new Promise(function() {}), matchersUtil = new jasmineUnderTest.MatchersUtil(); expect(matchersUtil.equals(p1, p1)).toBe(true); expect(matchersUtil.equals(p1, p2)).toBe(false); }); describe('when running in a browser', function() { function isNotRunningInBrowser() { return typeof document === 'undefined'; } it('passes for equivalent DOM nodes', function() { if (isNotRunningInBrowser()) { return; } const a = document.createElement('div'); const matchersUtil = new jasmineUnderTest.MatchersUtil(); a.setAttribute('test-attr', 'attr-value'); a.appendChild(document.createTextNode('test')); const b = document.createElement('div'); b.setAttribute('test-attr', 'attr-value'); b.appendChild(document.createTextNode('test')); expect(matchersUtil.equals(a, b)).toBe(true); }); it('passes for equivalent objects from different frames', function() { if (isNotRunningInBrowser()) { return; } const matchersUtil = new jasmineUnderTest.MatchersUtil(); const iframe = document.createElement('iframe'); document.body.appendChild(iframe); iframe.contentWindow.eval('window.testObject = {}'); expect(matchersUtil.equals({}, iframe.contentWindow.testObject)).toBe( true ); document.body.removeChild(iframe); }); it('fails for DOM nodes with different attributes or child nodes', function() { if (isNotRunningInBrowser()) { return; } const matchersUtil = new jasmineUnderTest.MatchersUtil(); const a = document.createElement('div'); a.setAttribute('test-attr', 'attr-value'); a.appendChild(document.createTextNode('test')); const b = document.createElement('div'); b.setAttribute('test-attr', 'attr-value2'); b.appendChild(document.createTextNode('test')); expect(matchersUtil.equals(a, b)).toBe(false); b.setAttribute('test-attr', 'attr-value'); expect(matchersUtil.equals(a, b)).toBe(true); b.appendChild(document.createTextNode('2')); expect(matchersUtil.equals(a, b)).toBe(false); a.appendChild(document.createTextNode('2')); expect(matchersUtil.equals(a, b)).toBe(true); }); }); describe('when running in Node', function() { function isNotRunningInNode() { return typeof require !== 'function'; } it('passes for equivalent objects from different vm contexts', function() { if (isNotRunningInNode()) { return; } const matchersUtil = new jasmineUnderTest.MatchersUtil(); const vm = require('vm'); const sandbox = { obj: null }; vm.runInNewContext('obj = {a: 1, b: 2}', sandbox); expect(matchersUtil.equals(sandbox.obj, { a: 1, b: 2 })).toBe(true); }); it('passes for equivalent arrays from different vm contexts', function() { if (isNotRunningInNode()) { return; } const matchersUtil = new jasmineUnderTest.MatchersUtil(); const vm = require('vm'); const sandbox = { arr: null }; vm.runInNewContext('arr = [1, 2]', sandbox); expect(matchersUtil.equals(sandbox.arr, [1, 2])).toBe(true); }); }); it('passes when Any is used', function() { const number = 3, anyNumber = new jasmineUnderTest.Any(Number), matchersUtil = new jasmineUnderTest.MatchersUtil(); expect(matchersUtil.equals(number, anyNumber)).toBe(true); expect(matchersUtil.equals(anyNumber, number)).toBe(true); }); it('fails when Any is compared to something unexpected', function() { const number = 3, anyString = new jasmineUnderTest.Any(String), matchersUtil = new jasmineUnderTest.MatchersUtil(); expect(matchersUtil.equals(number, anyString)).toBe(false); expect(matchersUtil.equals(anyString, number)).toBe(false); }); it('passes when ObjectContaining is used', function() { const obj = { foo: 3, bar: 7 }, containing = new jasmineUnderTest.ObjectContaining({ foo: 3 }), matchersUtil = new jasmineUnderTest.MatchersUtil(); expect(matchersUtil.equals(obj, containing)).toBe(true); expect(matchersUtil.equals(containing, obj)).toBe(true); }); it('passes when MapContaining is used', function() { const matchersUtil = new jasmineUnderTest.MatchersUtil(); const obj = new Map(); obj.set(1, 2); obj.set('foo', 'bar'); const containing = new jasmineUnderTest.MapContaining(new Map()); containing.sample.set('foo', 'bar'); expect(matchersUtil.equals(obj, containing)).toBe(true); expect(matchersUtil.equals(containing, obj)).toBe(true); }); it('passes when SetContaining is used', function() { const matchersUtil = new jasmineUnderTest.MatchersUtil(); const obj = new Set(); obj.add(1); obj.add('foo'); const containing = new jasmineUnderTest.SetContaining(new Set()); containing.sample.add(1); expect(matchersUtil.equals(obj, containing)).toBe(true); expect(matchersUtil.equals(containing, obj)).toBe(true); }); it('passes when an asymmetric equality tester returns true', function() { const tester = { asymmetricMatch: function() { return true; } }, matchersUtil = new jasmineUnderTest.MatchersUtil(); expect(matchersUtil.equals(false, tester)).toBe(true); expect(matchersUtil.equals(tester, false)).toBe(true); }); it('fails when an asymmetric equality tester returns false', function() { const tester = { asymmetricMatch: function() { return false; } }, matchersUtil = new jasmineUnderTest.MatchersUtil(); expect(matchersUtil.equals(true, tester)).toBe(false); expect(matchersUtil.equals(tester, true)).toBe(false); }); it('passes when ArrayContaining is used', function() { const arr = ['foo', 'bar'], matchersUtil = new jasmineUnderTest.MatchersUtil(); expect( matchersUtil.equals(arr, new jasmineUnderTest.ArrayContaining(['bar'])) ).toBe(true); }); it('passes when a custom equality matcher returns true', function() { const tester = function() { return true; }, matchersUtil = new jasmineUnderTest.MatchersUtil({ customTesters: [tester], pp: function() {} }); expect(matchersUtil.equals(1, 2)).toBe(true); }); it('passes for two empty Objects', function() { const matchersUtil = new jasmineUnderTest.MatchersUtil(); expect(matchersUtil.equals({}, {})).toBe(true); }); describe("when a custom equality matcher returns 'undefined'", function() { const tester = function() { return jasmine.undefined; }; it('passes for two empty Objects', function() { const matchersUtil = new jasmineUnderTest.MatchersUtil({ customTesters: [tester], pp: function() {} }); expect(matchersUtil.equals({}, {})).toBe(true); }); }); it('fails for equivalents when a custom equality matcher returns false', function() { const tester = function() { return false; }, matchersUtil = new jasmineUnderTest.MatchersUtil({ customTesters: [tester], pp: function() {} }); expect(matchersUtil.equals(1, 1)).toBe(false); }); it('passes for an asymmetric equality tester that returns true when a custom equality tester return false', function() { const asymmetricTester = { asymmetricMatch: function() { return true; } }, symmetricTester = function() { return false; }, matchersUtil = new jasmineUnderTest.MatchersUtil({ customTesters: [symmetricTester()], pp: function() {} }); expect(matchersUtil.equals(asymmetricTester, true)).toBe(true); expect(matchersUtil.equals(true, asymmetricTester)).toBe(true); }); it('passes when an Any is compared to an Any that checks for the same type', function() { const any1 = new jasmineUnderTest.Any(Function), any2 = new jasmineUnderTest.Any(Function), matchersUtil = new jasmineUnderTest.MatchersUtil(); expect(matchersUtil.equals(any1, any2)).toBe(true); }); it('passes for null prototype objects with same properties', function() { const objA = Object.create(null), objB = Object.create(null), matchersUtil = new jasmineUnderTest.MatchersUtil(); objA.name = 'test'; objB.name = 'test'; expect(matchersUtil.equals(objA, objB)).toBe(true); }); it('fails for null prototype objects with different properties', function() { const objA = Object.create(null), objB = Object.create(null), matchersUtil = new jasmineUnderTest.MatchersUtil(); objA.name = 'test'; objB.test = 'name'; expect(matchersUtil.equals(objA, objB)).toBe(false); }); it('passes when comparing two empty sets', function() { const matchersUtil = new jasmineUnderTest.MatchersUtil(); expect(matchersUtil.equals(new Set(), new Set())).toBe(true); }); it('passes when comparing identical sets', function() { const matchersUtil = new jasmineUnderTest.MatchersUtil(); const setA = new Set(); setA.add(6); setA.add(5); const setB = new Set(); setB.add(6); setB.add(5); expect(matchersUtil.equals(setA, setB)).toBe(true); }); it('passes when comparing identical sets with different insertion order and simple elements', function() { const matchersUtil = new jasmineUnderTest.MatchersUtil(); const setA = new Set(); setA.add(3); setA.add(6); const setB = new Set(); setB.add(6); setB.add(3); expect(matchersUtil.equals(setA, setB)).toBe(true); }); it('passes when comparing identical sets with different insertion order and complex elements 1', function() { const matchersUtil = new jasmineUnderTest.MatchersUtil(); const setA1 = new Set(); setA1.add(['a', 3]); setA1.add([6, 1]); const setA2 = new Set(); setA1.add(['y', 3]); setA1.add([6, 1]); const setA = new Set(); setA.add(setA1); setA.add(setA2); const setB1 = new Set(); setB1.add([6, 1]); setB1.add(['a', 3]); const setB2 = new Set(); setB1.add([6, 1]); setB1.add(['y', 3]); const setB = new Set(); setB.add(setB1); setB.add(setB2); expect(matchersUtil.equals(setA, setB)).toBe(true); }); it('passes when comparing identical sets with different insertion order and complex elements 2', function() { const matchersUtil = new jasmineUnderTest.MatchersUtil(); const setA = new Set(); setA.add([[1, 2], [3, 4]]); setA.add([[5, 6], [7, 8]]); const setB = new Set(); setB.add([[5, 6], [7, 8]]); setB.add([[1, 2], [3, 4]]); expect(matchersUtil.equals(setA, setB)).toBe(true); }); it('fails for sets with different elements', function() { const matchersUtil = new jasmineUnderTest.MatchersUtil(); const setA = new Set(); setA.add(6); setA.add(3); setA.add(5); const setB = new Set(); setB.add(6); setB.add(4); setB.add(5); expect(matchersUtil.equals(setA, setB)).toBe(false); }); it('fails for sets of different size', function() { const matchersUtil = new jasmineUnderTest.MatchersUtil(); const setA = new Set(); setA.add(6); setA.add(3); const setB = new Set(); setB.add(6); setB.add(4); setB.add(5); expect(matchersUtil.equals(setA, setB)).toBe(false); }); it('passes when comparing two empty maps', function() { const matchersUtil = new jasmineUnderTest.MatchersUtil(); expect(matchersUtil.equals(new Map(), new Map())).toBe(true); }); it('passes when comparing identical maps', function() { const matchersUtil = new jasmineUnderTest.MatchersUtil(); const mapA = new Map(); mapA.set(6, 5); const mapB = new Map(); mapB.set(6, 5); expect(matchersUtil.equals(mapA, mapB)).toBe(true); }); it('passes when comparing identical maps with different insertion order', function() { const matchersUtil = new jasmineUnderTest.MatchersUtil(); const mapA = new Map(); mapA.set('a', 3); mapA.set(6, 1); const mapB = new Map(); mapB.set(6, 1); mapB.set('a', 3); expect(matchersUtil.equals(mapA, mapB)).toBe(true); }); it('fails for maps with different elements', function() { const matchersUtil = new jasmineUnderTest.MatchersUtil(); const mapA = new Map(); mapA.set(6, 3); mapA.set(5, 1); const mapB = new Map(); mapB.set(6, 4); mapB.set(5, 1); expect(matchersUtil.equals(mapA, mapB)).toBe(false); }); it('fails for maps of different size', function() { const matchersUtil = new jasmineUnderTest.MatchersUtil(); const mapA = new Map(); mapA.set(6, 3); const mapB = new Map(); mapB.set(6, 4); mapB.set(5, 1); expect(matchersUtil.equals(mapA, mapB)).toBe(false); }); it('passes when comparing two identical URLs', function() { const matchersUtil = new jasmineUnderTest.MatchersUtil(); expect( matchersUtil.equals( new URL('http://localhost/1'), new URL('http://localhost/1') ) ).toBe(true); }); it('fails when comparing two different URLs', function() { const matchersUtil = new jasmineUnderTest.MatchersUtil(), url1 = new URL('http://localhost/1'); expect(matchersUtil.equals(url1, new URL('http://localhost/2'))).toBe( false ); expect(matchersUtil.equals(url1, new URL('http://localhost/1?foo'))).toBe( false ); expect(matchersUtil.equals(url1, new URL('http://localhost/1#foo'))).toBe( false ); expect(matchersUtil.equals(url1, new URL('https://localhost/1'))).toBe( false ); expect( matchersUtil.equals(url1, new URL('http://localhost:8080/1')) ).toBe(false); expect(matchersUtil.equals(url1, new URL('http://example.com/1'))).toBe( false ); }); it('passes for ArrayBuffers with same length and content', function() { const buffer1 = new ArrayBuffer(4); const buffer2 = new ArrayBuffer(4); const matchersUtil = new jasmineUnderTest.MatchersUtil(); expect(matchersUtil.equals(buffer1, buffer2)).toBe(true); }); it('fails for ArrayBuffers with same length but different content', function() { const buffer1 = new ArrayBuffer(4); const buffer2 = new ArrayBuffer(4); const array1 = new Uint8Array(buffer1); array1[0] = 1; const matchersUtil = new jasmineUnderTest.MatchersUtil(); expect(matchersUtil.equals(buffer1, buffer2)).toBe(false); }); describe('Typed arrays', function() { it('fails for typed arrays of same length and contents but different types', function() { const matchersUtil = new jasmineUnderTest.MatchersUtil(); const a1 = new Int8Array(1); const a2 = new Uint8Array(1); a1[0] = a2[0] = 0; expect(matchersUtil.equals(a1, a2)).toBe(false); }); [ 'Int8Array', 'Uint8Array', 'Uint8ClampedArray', 'Int16Array', 'Uint16Array', 'Int32Array', 'Uint32Array', 'Float32Array', 'Float64Array' ].forEach(function(typeName) { const TypedArrayCtor = jasmine.getGlobal()[typeName]; it( 'passes for ' + typeName + 's with same length and content', function() { const matchersUtil = new jasmineUnderTest.MatchersUtil(); const a1 = new TypedArrayCtor(2); const a2 = new TypedArrayCtor(2); a1[0] = a2[0] = 0; a1[1] = a2[1] = 1; expect(matchersUtil.equals(a1, a2)).toBe(true); } ); it('fails for ' + typeName + 's with different length', function() { const matchersUtil = new jasmineUnderTest.MatchersUtil(); const a1 = new TypedArrayCtor(2); const a2 = new TypedArrayCtor(1); a1[0] = a1[1] = a2[0] = 0; expect(matchersUtil.equals(a1, a2)).toBe(false); }); it( 'fails for ' + typeName + 's with same length but different content', function() { const matchersUtil = new jasmineUnderTest.MatchersUtil(); const a1 = new TypedArrayCtor(1); const a2 = new TypedArrayCtor(1); a1[0] = 0; a2[0] = 1; expect(matchersUtil.equals(a1, a2)).toBe(false); } ); it('checks nonstandard properties of ' + typeName, function() { const matchersUtil = new jasmineUnderTest.MatchersUtil(); const a1 = new TypedArrayCtor(1); const a2 = new TypedArrayCtor(1); a1[0] = a2[0] = 0; a1.extra = 'yes'; expect(matchersUtil.equals(a1, a2)).toBe(false); }); it('works with custom equality testers with ' + typeName, function() { const a1 = new TypedArrayCtor(1); const a2 = new TypedArrayCtor(1); const matchersUtil = new jasmineUnderTest.MatchersUtil({ customTesters: [ function() { return true; } ] }); a1[0] = 0; a2[0] = 1; expect(matchersUtil.equals(a1, a2)).toBe(true); }); }); ['BigInt64Array', 'BigUint64Array'].forEach(function(typeName) { function requireType() { const TypedArrayCtor = jasmine.getGlobal()[typeName]; if (!TypedArrayCtor) { pending('Browser does not support ' + typeName); } return TypedArrayCtor; } it( 'passes for ' + typeName + 's with same length and content', function() { const TypedArrayCtor = requireType(); 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); } ); it('fails for ' + typeName + 's with different length', function() { const TypedArrayCtor = requireType(); 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); }); it( 'fails for ' + typeName + 's with same length but different content', function() { const TypedArrayCtor = requireType(); 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); } ); }); }); describe('when running in an environment with array polyfills', function() { const findIndexDescriptor = Object.getOwnPropertyDescriptor( Array.prototype, 'findIndex' ); beforeEach(function() { if (!findIndexDescriptor) { jasmine .getEnv() .pending( 'Environment does not have a property descriptor for Array.prototype.findIndex' ); } Object.defineProperty(Array.prototype, 'findIndex', { enumerable: true, value: function(predicate) { if (this === null) { throw new TypeError( 'Array.prototype.findIndex called on null or undefined' ); } if (typeof predicate !== 'function') { throw new TypeError('predicate must be a function'); } const list = Object(this); const length = list.length >>> 0; const thisArg = arguments[1]; for (let i = 0; i < length; i++) { const value = list[i]; if (predicate.call(thisArg, value, i, list)) { return i; } } return -1; } }); }); afterEach(function() { Object.defineProperty( Array.prototype, 'findIndex', findIndexDescriptor ); }); it("passes when there's an array polyfill", function() { expect(['foo']).toEqual(['foo']); }); }); describe('Building diffs for asymmetric equality testers', function() { it('diffs the values returned by valuesForDiff_', function() { const tester = { asymmetricMatch: function() { return false; }, valuesForDiff_: function() { return { self: 'asymmetric tester value', other: 'other value' }; } }, actual = { x: 42 }, expected = { x: tester }, diffBuilder = jasmine.createSpyObj('diffBuilder', [ 'recordMismatch', 'withPath', 'setRoots' ]), matchersUtil = new jasmineUnderTest.MatchersUtil(); diffBuilder.withPath.and.callFake(function(p, block) { block(); }); matchersUtil.equals(actual, expected, diffBuilder); expect(diffBuilder.setRoots).toHaveBeenCalledWith(actual, expected); expect(diffBuilder.withPath).toHaveBeenCalledWith( 'x', jasmine.any(Function) ); expect(diffBuilder.recordMismatch).toHaveBeenCalledWith(); }); it('records both objects when the tester does not implement valuesForDiff', function() { const tester = { asymmetricMatch: function() { return false; } }, actual = { x: 42 }, expected = { x: tester }, diffBuilder = jasmine.createSpyObj('diffBuilder', [ 'recordMismatch', 'withPath', 'setRoots' ]), matchersUtil = new jasmineUnderTest.MatchersUtil(); diffBuilder.withPath.and.callFake(function(p, block) { block(); }); matchersUtil.equals(actual, expected, diffBuilder); expect(diffBuilder.setRoots).toHaveBeenCalledWith(actual, expected); expect(diffBuilder.withPath).toHaveBeenCalledWith( 'x', jasmine.any(Function) ); expect(diffBuilder.recordMismatch).toHaveBeenCalledWith(); }); }); it('uses a diffBuilder if one is provided as the third argument', function() { const diffBuilder = new jasmineUnderTest.DiffBuilder(), matchersUtil = new jasmineUnderTest.MatchersUtil(); spyOn(diffBuilder, 'recordMismatch'); spyOn(diffBuilder, 'withPath').and.callThrough(); matchersUtil.equals([1], [2], diffBuilder); expect(diffBuilder.withPath).toHaveBeenCalledWith( 'length', jasmine.any(Function) ); expect(diffBuilder.withPath).toHaveBeenCalledWith( 0, jasmine.any(Function) ); expect(diffBuilder.recordMismatch).toHaveBeenCalled(); }); }); describe('contains', function() { it('passes when expected is a substring of actual', function() { const matchersUtil = new jasmineUnderTest.MatchersUtil(); expect(matchersUtil.contains('ABC', 'BC')).toBe(true); }); it('fails when expected is a not substring of actual', function() { const matchersUtil = new jasmineUnderTest.MatchersUtil(); expect(matchersUtil.contains('ABC', 'X')).toBe(false); }); it('passes when expected is an element in an actual array', function() { const matchersUtil = new jasmineUnderTest.MatchersUtil(); expect(matchersUtil.contains(['foo', 'bar'], 'foo')).toBe(true); }); it('fails when expected is not an element in an actual array', function() { const matchersUtil = new jasmineUnderTest.MatchersUtil(); expect(matchersUtil.contains(['foo', 'bar'], 'baz')).toBe(false); }); it('passes with mixed-element arrays', function() { const matchersUtil = new jasmineUnderTest.MatchersUtil(); expect(matchersUtil.contains(['foo', { some: 'bar' }], 'foo')).toBe(true); expect( matchersUtil.contains(['foo', { some: 'bar' }], { some: 'bar' }) ).toBe(true); }); it('uses custom equality testers if actual is an Array', function() { const customTester = function() { return true; }, matchersUtil = new jasmineUnderTest.MatchersUtil({ customTesters: [customTester], pp: function() {} }); expect(matchersUtil.contains([1, 2], 3)).toBe(true); }); it('fails when actual is undefined', function() { const matchersUtil = new jasmineUnderTest.MatchersUtil(); expect(matchersUtil.contains(undefined, 'A')).toBe(false); }); it('fails when actual is null', function() { const matchersUtil = new jasmineUnderTest.MatchersUtil(); expect(matchersUtil.contains(null, 'A')).toBe(false); }); it('works with array-like objects that implement iterable', function() { let capturedArgs = null; const matchersUtil = new jasmineUnderTest.MatchersUtil(); function testFunction() { capturedArgs = arguments; } testFunction('foo', 'bar'); expect(matchersUtil.contains(capturedArgs, 'bar')).toBe(true); expect(matchersUtil.contains(capturedArgs, 'baz')).toBe(false); }); it("passes with array-like objects that don't implement iterable", function() { const arrayLike = { 0: 'a', 1: 'b', length: 2 }; const matchersUtil = new jasmineUnderTest.MatchersUtil(); expect(matchersUtil.contains(arrayLike, 'b')).toBe(true); expect(matchersUtil.contains(arrayLike, 'c')).toBe(false); }); it('passes for set members', function() { const matchersUtil = new jasmineUnderTest.MatchersUtil(); const setItem = { foo: 'bar' }; const set = new Set(); set.add(setItem); expect(matchersUtil.contains(set, setItem)).toBe(true); }); it('passes for objects that equal to a set member', function() { const matchersUtil = new jasmineUnderTest.MatchersUtil(); const set = new Set(); set.add({ foo: 'bar' }); expect(matchersUtil.contains(set, { foo: 'bar' })).toBe(true); }); }); describe('buildFailureMessage', function() { it('builds an English sentence for a failure case', function() { const actual = 'foo', name = 'toBar', pp = jasmineUnderTest.makePrettyPrinter(), matchersUtil = new jasmineUnderTest.MatchersUtil({ pp: pp }), message = matchersUtil.buildFailureMessage(name, false, actual); expect(message).toEqual("Expected 'foo' to bar."); }); it("builds an English sentence for a 'not' failure case", function() { const actual = 'foo', name = 'toBar', isNot = true, pp = jasmineUnderTest.makePrettyPrinter(), matchersUtil = new jasmineUnderTest.MatchersUtil({ pp: pp }), message = matchersUtil.buildFailureMessage(name, isNot, actual); expect(message).toEqual("Expected 'foo' not to bar."); }); it('builds an English sentence for an arbitrary array of expected arguments', function() { const actual = 'foo', name = 'toBar', pp = jasmineUnderTest.makePrettyPrinter(), matchersUtil = new jasmineUnderTest.MatchersUtil({ pp: pp }), message = matchersUtil.buildFailureMessage( name, false, actual, 'quux', 'corge' ); expect(message).toEqual("Expected 'foo' to bar 'quux', 'corge'."); }); it('uses the injected pretty-printer to format the expecteds and actual', function() { const actual = 'foo', expected1 = 'qux', expected2 = 'grault', name = 'toBar', isNot = false, pp = function(value) { return '<' + value + '>'; }, matchersUtil = new jasmineUnderTest.MatchersUtil({ pp: pp }), message = matchersUtil.buildFailureMessage( name, isNot, actual, expected1, expected2 ); expect(message).toEqual('Expected to bar , .'); }); }); }); jasmine-4.5.0/spec/core/matchers/nothingSpec.js000066400000000000000000000003141432731766000214640ustar00rootroot00000000000000describe('nothing', function() { it('should pass', function() { const matcher = jasmineUnderTest.matchers.nothing(), result = matcher.compare(); expect(result.pass).toBe(true); }); }); jasmine-4.5.0/spec/core/matchers/toBeCloseToSpec.js000066400000000000000000000115231432731766000222040ustar00rootroot00000000000000describe('toBeCloseTo', function() { it('passes when within two decimal places by default', function() { const matcher = jasmineUnderTest.matchers.toBeCloseTo(); let result; result = matcher.compare(0, 0); expect(result.pass).toBe(true); result = matcher.compare(0, 0.001); expect(result.pass).toBe(true); result = matcher.compare(0, 0.005); expect(result.pass).toBe(true); }); it('fails when not within two decimal places by default', function() { const matcher = jasmineUnderTest.matchers.toBeCloseTo(); let result; result = matcher.compare(0, 0.01); expect(result.pass).toBe(false); result = matcher.compare(0, 0.05); expect(result.pass).toBe(false); }); it('accepts an optional precision argument', function() { const matcher = jasmineUnderTest.matchers.toBeCloseTo(); let result; result = matcher.compare(0, 0.1, 0); expect(result.pass).toBe(true); result = matcher.compare(0, 0.5, 0); expect(result.pass).toBe(true); result = matcher.compare(0, 0.0001, 3); expect(result.pass).toBe(true); result = matcher.compare(0, 0.0005, 3); expect(result.pass).toBe(true); result = matcher.compare(0, 0.00001, 4); expect(result.pass).toBe(true); result = matcher.compare(0, 0.00005, 4); expect(result.pass).toBe(true); }); it('fails when one of the arguments is null', function() { const matcher = jasmineUnderTest.matchers.toBeCloseTo(); expect(function() { matcher.compare(null, null); }).toThrowError( 'Cannot use toBeCloseTo with null. Arguments evaluated to: expect(null).toBeCloseTo(null).' ); expect(function() { matcher.compare(0, null); }).toThrowError( 'Cannot use toBeCloseTo with null. Arguments evaluated to: expect(0).toBeCloseTo(null).' ); expect(function() { matcher.compare(null, 0); }).toThrowError( 'Cannot use toBeCloseTo with null. Arguments evaluated to: expect(null).toBeCloseTo(0).' ); }); it('rounds expected values', function() { const matcher = jasmineUnderTest.matchers.toBeCloseTo(); let result; result = matcher.compare(1.23, 1.229); expect(result.pass).toBe(true); result = matcher.compare(1.23, 1.226); expect(result.pass).toBe(true); result = matcher.compare(1.23, 1.225); expect(result.pass).toBe(true); result = matcher.compare(1.23, 1.235); expect(result.pass).toBe(true); // 1.2249999 will be rounded to 1.225 result = matcher.compare(1.23, 1.2249999); expect(result.pass).toBe(true); // 1.2249999 will be rounded to 1.224 result = matcher.compare(1.23, 1.2244999); expect(result.pass).toBe(false); result = matcher.compare(1.23, 1.234); expect(result.pass).toBe(true); }); it('handles edge cases with rounding', function() { const matcher = jasmineUnderTest.matchers.toBeCloseTo(); let result; // these cases resulted in false negatives in version of V8 // included in Node.js 12 and Chrome 74 (and Edge Chromium) result = matcher.compare(4.030904708957288, 4.0309, 5); expect(result.pass).toBe(true); result = matcher.compare(4.82665525779431, 4.82666, 5); expect(result.pass).toBe(true); result = matcher.compare(-2.82665525779431, -2.82666, 5); expect(result.pass).toBe(true); }); describe('Infinity handling', function() { it('passes when the actual and expected are both Infinity', function() { const matcher = jasmineUnderTest.matchers.toBeCloseTo(); const result = matcher.compare(Infinity, Infinity, 0); expect(result.pass).toBe(true); }); it('passes when the actual and expected are both -Infinity', function() { const matcher = jasmineUnderTest.matchers.toBeCloseTo(); const result = matcher.compare(-Infinity, -Infinity, 0); expect(result.pass).toBe(true); }); it('fails when the actual is Infinity and the expected is -Infinity', function() { const matcher = jasmineUnderTest.matchers.toBeCloseTo(); const result = matcher.compare(Infinity, -Infinity, 0); expect(result.pass).toBe(false); }); it('fails when the actual is -Infinity and the expected is Infinity', function() { const matcher = jasmineUnderTest.matchers.toBeCloseTo(); const result = matcher.compare(-Infinity, Infinity, 0); expect(result.pass).toBe(false); }); it('fails when the actual is a number and the expected is Infinity', function() { const matcher = jasmineUnderTest.matchers.toBeCloseTo(); const result = matcher.compare(42, Infinity, 0); expect(result.pass).toBe(false); }); it('fails when the actual is a number and the expected is -Infinity', function() { const matcher = jasmineUnderTest.matchers.toBeCloseTo(); const result = matcher.compare(42, -Infinity, 0); expect(result.pass).toBe(false); }); }); }); jasmine-4.5.0/spec/core/matchers/toBeDefinedSpec.js000066400000000000000000000006721432731766000221750ustar00rootroot00000000000000describe('toBeDefined', function() { it('matches for defined values', function() { const matcher = jasmineUnderTest.matchers.toBeDefined(); const result = matcher.compare('foo'); expect(result.pass).toBe(true); }); it('fails when matching undefined values', function() { const matcher = jasmineUnderTest.matchers.toBeDefined(); const result = matcher.compare(void 0); expect(result.pass).toBe(false); }); }); jasmine-4.5.0/spec/core/matchers/toBeFalseSpec.js000066400000000000000000000011231432731766000216610ustar00rootroot00000000000000describe('toBeFalse', function() { it('passes for false', function() { const matcher = jasmineUnderTest.matchers.toBeFalse(); const result = matcher.compare(false); expect(result.pass).toBe(true); }); it('fails for non-false', function() { const matcher = jasmineUnderTest.matchers.toBeFalse(); const result = matcher.compare('foo'); expect(result.pass).toBe(false); }); it('fails for falsy', function() { const matcher = jasmineUnderTest.matchers.toBeFalse(); const result = matcher.compare(undefined); expect(result.pass).toBe(false); }); }); jasmine-4.5.0/spec/core/matchers/toBeFalsySpec.js000066400000000000000000000022401432731766000217060ustar00rootroot00000000000000describe('toBeFalsy', function() { it("passes for 'falsy' values", function() { const matcher = jasmineUnderTest.matchers.toBeFalsy(); let result; result = matcher.compare(false); expect(result.pass).toBe(true); result = matcher.compare(0); expect(result.pass).toBe(true); result = matcher.compare(''); expect(result.pass).toBe(true); result = matcher.compare(null); expect(result.pass).toBe(true); result = matcher.compare(undefined); expect(result.pass).toBe(true); result = matcher.compare(void 0); expect(result.pass).toBe(true); }); it("fails for 'truthy' values", function() { const matcher = jasmineUnderTest.matchers.toBeFalsy(); let result; result = matcher.compare(true); expect(result.pass).toBe(false); result = matcher.compare(1); expect(result.pass).toBe(false); result = matcher.compare('foo'); expect(result.pass).toBe(false); result = matcher.compare({}); expect(result.pass).toBe(false); result = matcher.compare([]); expect(result.pass).toBe(false); result = matcher.compare(function() {}); expect(result.pass).toBe(false); }); }); jasmine-4.5.0/spec/core/matchers/toBeGreaterThanOrEqualSpec.js000066400000000000000000000014431432731766000243310ustar00rootroot00000000000000describe('toBeGreaterThanOrEqual', function() { it('passes when actual >= expected', function() { const matcher = jasmineUnderTest.matchers.toBeGreaterThanOrEqual(); let result; result = matcher.compare(2, 1); expect(result.pass).toBe(true); result = matcher.compare(1, 1); expect(result.pass).toBe(true); result = matcher.compare(1.0000001, 1); expect(result.pass).toBe(true); result = matcher.compare(1.0, 1.0); expect(result.pass).toBe(true); }); it('fails when actual < expected', function() { const matcher = jasmineUnderTest.matchers.toBeGreaterThanOrEqual(); let result; result = matcher.compare(1, 2); expect(result.pass).toBe(false); result = matcher.compare(1, 1.0000001); expect(result.pass).toBe(false); }); }); jasmine-4.5.0/spec/core/matchers/toBeGreaterThanSpec.js000066400000000000000000000010241432731766000230330ustar00rootroot00000000000000describe('toBeGreaterThan', function() { it('passes when actual > expected', function() { const matcher = jasmineUnderTest.matchers.toBeGreaterThan(); const result = matcher.compare(2, 1); expect(result.pass).toBe(true); }); it('fails when actual <= expected', function() { const matcher = jasmineUnderTest.matchers.toBeGreaterThan(); let result; result = matcher.compare(1, 1); expect(result.pass).toBe(false); result = matcher.compare(1, 2); expect(result.pass).toBe(false); }); }); jasmine-4.5.0/spec/core/matchers/toBeInstanceOfSpec.js000066400000000000000000000174601432731766000226730ustar00rootroot00000000000000describe('toBeInstanceOf', function() { describe('when expecting Number', function() { it('passes for literal number', function() { const matcher = jasmineUnderTest.matchers.toBeInstanceOf(); const result = matcher.compare(3, Number); expect(result).toEqual({ pass: true, message: 'Expected instance of Number not to be an instance of Number' }); }); it('passes for NaN', function() { const matcher = jasmineUnderTest.matchers.toBeInstanceOf({ pp: jasmineUnderTest.makePrettyPrinter() }); const result = matcher.compare(NaN, Number); expect(result).toEqual({ pass: true, message: 'Expected instance of NaN not to be an instance of Number' }); }); it('passes for Infinity', function() { const matcher = jasmineUnderTest.matchers.toBeInstanceOf(); const result = matcher.compare(Infinity, Number); expect(result).toEqual({ pass: true, message: 'Expected instance of Number not to be an instance of Number' }); }); it('fails for a non-number', function() { const matcher = jasmineUnderTest.matchers.toBeInstanceOf(); const result = matcher.compare('foo', Number); expect(result).toEqual({ pass: false, message: 'Expected instance of String to be an instance of Number' }); }); }); describe('when expecting String', function() { it('passes for a string', function() { const matcher = jasmineUnderTest.matchers.toBeInstanceOf(); const result = matcher.compare('foo', String); expect(result).toEqual({ pass: true, message: 'Expected instance of String not to be an instance of String' }); }); it('fails for a non-string', function() { const matcher = jasmineUnderTest.matchers.toBeInstanceOf(); const result = matcher.compare({}, String); expect(result).toEqual({ pass: false, message: 'Expected instance of Object to be an instance of String' }); }); }); describe('when expecting Boolean', function() { it('passes for a boolean', function() { const matcher = jasmineUnderTest.matchers.toBeInstanceOf(); const result = matcher.compare(true, Boolean); expect(result).toEqual({ pass: true, message: 'Expected instance of Boolean not to be an instance of Boolean' }); }); it('fails for a non-boolean', function() { const matcher = jasmineUnderTest.matchers.toBeInstanceOf(); const result = matcher.compare('false', Boolean); expect(result).toEqual({ pass: false, message: 'Expected instance of String to be an instance of Boolean' }); }); }); describe('when expecting RegExp', function() { it('passes for a literal regular expression', function() { const matcher = jasmineUnderTest.matchers.toBeInstanceOf(); const result = matcher.compare(/foo/, RegExp); expect(result).toEqual({ pass: true, message: 'Expected instance of RegExp not to be an instance of RegExp' }); }); }); describe('when expecting Function', function() { it('passes for a function', function() { const fn = function() {}; const matcher = jasmineUnderTest.matchers.toBeInstanceOf(); const result = matcher.compare(fn, Function); expect(result).toEqual({ pass: true, message: 'Expected instance of Function not to be an instance of Function' }); }); it('passes for an async function', function() { async function fn() { return 'foo'; } const matcher = jasmineUnderTest.matchers.toBeInstanceOf(); const result = matcher.compare(fn, Function); expect(result).toEqual({ pass: true, message: 'Expected instance of AsyncFunction not to be an instance of Function' }); }); }); describe('when expecting Object', function() { function Animal() {} it('passes for any object', function() { const matcher = jasmineUnderTest.matchers.toBeInstanceOf(); const result = matcher.compare({ foo: 'bar' }, Object); expect(result).toEqual({ pass: true, message: 'Expected instance of Object not to be an instance of Object' }); }); it('passes for an Error object', function() { const matcher = jasmineUnderTest.matchers.toBeInstanceOf(); const result = matcher.compare(new Error('example'), Object); expect(result).toEqual({ pass: true, message: 'Expected instance of Error not to be an instance of Object' }); }); it('passes for a user-defined class', function() { const matcher = jasmineUnderTest.matchers.toBeInstanceOf(); const result = matcher.compare(new Animal(), Object); expect(result).toEqual({ pass: true, message: 'Expected instance of Animal not to be an instance of Object' }); }); it('fails for a non-object', function() { const matcher = jasmineUnderTest.matchers.toBeInstanceOf(); const result = matcher.compare('foo', Object); expect(result).toEqual({ pass: false, message: 'Expected instance of String to be an instance of Object' }); }); it('passes for objects with no constructor', function() { const object = Object.create(null); const matcher = jasmineUnderTest.matchers.toBeInstanceOf({ pp: jasmineUnderTest.makePrettyPrinter() }); const result = matcher.compare(object, Object); expect(result).toEqual({ pass: true, message: 'Expected instance of null({ }) not to be an instance of Object' }); }); }); describe('when expecting a user-defined class', function() { // Base class function Animal() {} // Subclasses, defined using syntax that is as old as possible function Dog() { Animal.call(this); } Dog.prototype = new Animal(); Dog.prototype.constructor = Dog; function Cat() { Animal.call(this); } Cat.prototype = new Animal(); Cat.prototype.constructor = Cat; it('passes for instances of that class', function() { const matcher = jasmineUnderTest.matchers.toBeInstanceOf(); const result = matcher.compare(new Animal(), Animal); expect(result).toEqual({ pass: true, message: 'Expected instance of Animal not to be an instance of Animal' }); }); it('passes for instances of a subclass', function() { const matcher = jasmineUnderTest.matchers.toBeInstanceOf(); const result = matcher.compare(new Cat(), Animal); expect(result).toEqual({ pass: true, message: 'Expected instance of Cat not to be an instance of Animal' }); }); it('does not pass for sibling classes', function() { const matcher = jasmineUnderTest.matchers.toBeInstanceOf(); const result = matcher.compare(new Dog(), Cat); expect(result).toEqual({ pass: false, message: 'Expected instance of Dog to be an instance of Cat' }); }); }); it('raises an error if passed an invalid expected value', function() { const matcher = jasmineUnderTest.matchers.toBeInstanceOf(); expect(function() { matcher.compare({}, 'Error'); }).toThrowError( ' : Expected value is not a constructor function\n' + 'Usage: expect(value).toBeInstanceOf()' ); }); it('raises an error if missing an expected value', function() { const matcher = jasmineUnderTest.matchers.toBeInstanceOf({ pp: jasmineUnderTest.makePrettyPrinter() }); expect(function() { matcher.compare({}, undefined); }).toThrowError( ' : Expected value is not a constructor function\n' + 'Usage: expect(value).toBeInstanceOf()' ); }); }); jasmine-4.5.0/spec/core/matchers/toBeLessThanOrEqualSpec.js000066400000000000000000000014321432731766000236440ustar00rootroot00000000000000describe('toBeLessThanOrEqual', function() { it('passes when actual <= expected', function() { const matcher = jasmineUnderTest.matchers.toBeLessThanOrEqual(); let result; result = matcher.compare(1, 2); expect(result.pass).toBe(true); result = matcher.compare(1, 1); expect(result.pass).toBe(true); result = matcher.compare(1, 1.0000001); expect(result.pass).toBe(true); result = matcher.compare(1.0, 1.0); expect(result.pass).toBe(true); }); it('fails when actual < expected', function() { const matcher = jasmineUnderTest.matchers.toBeLessThanOrEqual(); let result; result = matcher.compare(2, 1); expect(result.pass).toBe(false); result = matcher.compare(1.0000001, 1); expect(result.pass).toBe(false); }); }); jasmine-4.5.0/spec/core/matchers/toBeLessThanSpec.js000066400000000000000000000010131432731766000223460ustar00rootroot00000000000000describe('toBeLessThan', function() { it('passes when actual < expected', function() { const matcher = jasmineUnderTest.matchers.toBeLessThan(); const result = matcher.compare(1, 2); expect(result.pass).toBe(true); }); it('fails when actual <= expected', function() { const matcher = jasmineUnderTest.matchers.toBeLessThan(); let result; result = matcher.compare(1, 1); expect(result.pass).toBe(false); result = matcher.compare(2, 1); expect(result.pass).toBe(false); }); }); jasmine-4.5.0/spec/core/matchers/toBeNaNSpec.js000066400000000000000000000021211432731766000213020ustar00rootroot00000000000000describe('toBeNaN', function() { it('passes for NaN with a custom .not fail', function() { const matcher = jasmineUnderTest.matchers.toBeNaN(); const result = matcher.compare(Number.NaN); expect(result.pass).toBe(true); expect(result.message).toEqual('Expected actual not to be NaN.'); }); it('fails for anything not a NaN', function() { const matcher = jasmineUnderTest.matchers.toBeNaN(); let result; result = matcher.compare(1); expect(result.pass).toBe(false); result = matcher.compare(null); expect(result.pass).toBe(false); result = matcher.compare(void 0); expect(result.pass).toBe(false); result = matcher.compare(''); expect(result.pass).toBe(false); result = matcher.compare(Number.POSITIVE_INFINITY); expect(result.pass).toBe(false); }); it('has a custom message on failure', function() { const matcher = jasmineUnderTest.matchers.toBeNaN({ pp: jasmineUnderTest.makePrettyPrinter() }), result = matcher.compare(0); expect(result.message()).toEqual('Expected 0 to be NaN.'); }); }); jasmine-4.5.0/spec/core/matchers/toBeNegativeInfinitySpec.js000066400000000000000000000017651432731766000241170ustar00rootroot00000000000000describe('toBeNegativeInfinity', function() { it("fails for anything that isn't -Infinity", function() { const matcher = jasmineUnderTest.matchers.toBeNegativeInfinity(); let result; result = matcher.compare(1); expect(result.pass).toBe(false); result = matcher.compare(Number.NaN); expect(result.pass).toBe(false); result = matcher.compare(null); expect(result.pass).toBe(false); }); it('has a custom message on failure', function() { const matcher = jasmineUnderTest.matchers.toBeNegativeInfinity({ pp: jasmineUnderTest.makePrettyPrinter() }), result = matcher.compare(0); expect(result.message()).toEqual('Expected 0 to be -Infinity.'); }); it('succeeds for -Infinity', function() { const matcher = jasmineUnderTest.matchers.toBeNegativeInfinity(), result = matcher.compare(Number.NEGATIVE_INFINITY); expect(result.pass).toBe(true); expect(result.message).toEqual('Expected actual not to be -Infinity.'); }); }); jasmine-4.5.0/spec/core/matchers/toBeNullSpec.js000066400000000000000000000006221432731766000215440ustar00rootroot00000000000000describe('toBeNull', function() { it('passes for null', function() { const matcher = jasmineUnderTest.matchers.toBeNull(); const result = matcher.compare(null); expect(result.pass).toBe(true); }); it('fails for non-null', function() { const matcher = jasmineUnderTest.matchers.toBeNull(); const result = matcher.compare('foo'); expect(result.pass).toBe(false); }); }); jasmine-4.5.0/spec/core/matchers/toBePositiveInfinitySpec.js000066400000000000000000000017611432731766000241530ustar00rootroot00000000000000describe('toBePositiveInfinity', function() { it("fails for anything that isn't Infinity", function() { const matcher = jasmineUnderTest.matchers.toBePositiveInfinity(); let result; result = matcher.compare(1); expect(result.pass).toBe(false); result = matcher.compare(Number.NaN); expect(result.pass).toBe(false); result = matcher.compare(null); expect(result.pass).toBe(false); }); it('has a custom message on failure', function() { const matcher = jasmineUnderTest.matchers.toBePositiveInfinity({ pp: jasmineUnderTest.makePrettyPrinter() }), result = matcher.compare(0); expect(result.message()).toEqual('Expected 0 to be Infinity.'); }); it('succeeds for Infinity', function() { const matcher = jasmineUnderTest.matchers.toBePositiveInfinity(), result = matcher.compare(Number.POSITIVE_INFINITY); expect(result.pass).toBe(true); expect(result.message).toEqual('Expected actual not to be Infinity.'); }); }); jasmine-4.5.0/spec/core/matchers/toBeSpec.js000066400000000000000000000064441432731766000207210ustar00rootroot00000000000000describe('toBe', function() { it('passes with no message when actual === expected', function() { const matchersUtil = new jasmineUnderTest.MatchersUtil(), matcher = jasmineUnderTest.matchers.toBe(matchersUtil), result = matcher.compare(1, 1); expect(result.pass).toBe(true); }); it('passes with a custom message when expected is an array', function() { const matchersUtil = new jasmineUnderTest.MatchersUtil({ pp: jasmineUnderTest.makePrettyPrinter() }), matcher = jasmineUnderTest.matchers.toBe(matchersUtil), array = [1]; const result = matcher.compare(array, array); expect(result.pass).toBe(true); expect(result.message).toBe( 'Expected [ 1 ] not to be [ 1 ]. Tip: To check for deep equality, use .toEqual() instead of .toBe().' ); }); it('passes with a custom message when expected is an object', function() { const matchersUtil = new jasmineUnderTest.MatchersUtil({ pp: jasmineUnderTest.makePrettyPrinter() }), matcher = jasmineUnderTest.matchers.toBe(matchersUtil), obj = { foo: 'bar' }; const result = matcher.compare(obj, obj); expect(result.pass).toBe(true); expect(result.message).toBe( "Expected Object({ foo: 'bar' }) not to be Object({ foo: 'bar' }). Tip: To check for deep equality, use .toEqual() instead of .toBe()." ); }); it('fails with no message when actual !== expected', function() { const matchersUtil = new jasmineUnderTest.MatchersUtil(), matcher = jasmineUnderTest.matchers.toBe(matchersUtil), result = matcher.compare(1, 2); expect(result.pass).toBe(false); expect(result.message).toBeUndefined(); }); it('fails with a custom message when expected is an array', function() { const matchersUtil = new jasmineUnderTest.MatchersUtil({ pp: jasmineUnderTest.makePrettyPrinter() }), matcher = jasmineUnderTest.matchers.toBe(matchersUtil), result = matcher.compare([1], [1]); expect(result.pass).toBe(false); expect(result.message).toBe( 'Expected [ 1 ] to be [ 1 ]. Tip: To check for deep equality, use .toEqual() instead of .toBe().' ); }); it('fails with a custom message when expected is an object', function() { const matchersUtil = new jasmineUnderTest.MatchersUtil({ pp: jasmineUnderTest.makePrettyPrinter() }), matcher = jasmineUnderTest.matchers.toBe(matchersUtil), result = matcher.compare({ foo: 'bar' }, { foo: 'bar' }); expect(result.pass).toBe(false); expect(result.message).toBe( "Expected Object({ foo: 'bar' }) to be Object({ foo: 'bar' }). Tip: To check for deep equality, use .toEqual() instead of .toBe()." ); }); it('works with custom object formatters when expected is an object', function() { const formatter = function(x) { return '<' + x.foo + '>'; }, prettyPrinter = jasmineUnderTest.makePrettyPrinter([formatter]), matchersUtil = new jasmineUnderTest.MatchersUtil({ pp: prettyPrinter }), matcher = jasmineUnderTest.matchers.toBe(matchersUtil), result = matcher.compare({ foo: 'bar' }, { foo: 'bar' }); expect(result.pass).toBe(false); expect(result.message).toBe( 'Expected to be . Tip: To check for deep equality, use .toEqual() instead of .toBe().' ); }); }); jasmine-4.5.0/spec/core/matchers/toBeTrueSpec.js000066400000000000000000000006221432731766000215510ustar00rootroot00000000000000describe('toBeTrue', function() { it('passes for true', function() { const matcher = jasmineUnderTest.matchers.toBeTrue(); const result = matcher.compare(true); expect(result.pass).toBe(true); }); it('fails for non-true', function() { const matcher = jasmineUnderTest.matchers.toBeTrue(); const result = matcher.compare('foo'); expect(result.pass).toBe(false); }); }); jasmine-4.5.0/spec/core/matchers/toBeTruthySpec.js000066400000000000000000000022431432731766000221320ustar00rootroot00000000000000describe('toBeTruthy', function() { it("passes for 'truthy' values", function() { const matcher = jasmineUnderTest.matchers.toBeTruthy(); let result; result = matcher.compare(true); expect(result.pass).toBe(true); result = matcher.compare(1); expect(result.pass).toBe(true); result = matcher.compare('foo'); expect(result.pass).toBe(true); result = matcher.compare({}); expect(result.pass).toBe(true); result = matcher.compare([]); expect(result.pass).toBe(true); result = matcher.compare(function() {}); expect(result.pass).toBe(true); }); it("fails for 'falsy' values", function() { const matcher = jasmineUnderTest.matchers.toBeTruthy(); let result; result = matcher.compare(false); expect(result.pass).toBe(false); result = matcher.compare(0); expect(result.pass).toBe(false); result = matcher.compare(''); expect(result.pass).toBe(false); result = matcher.compare(null); expect(result.pass).toBe(false); result = matcher.compare(undefined); expect(result.pass).toBe(false); result = matcher.compare(void 0); expect(result.pass).toBe(false); }); }); jasmine-4.5.0/spec/core/matchers/toBeUndefinedSpec.js000066400000000000000000000006771432731766000225450ustar00rootroot00000000000000describe('toBeUndefined', function() { it('passes for undefined values', function() { const matcher = jasmineUnderTest.matchers.toBeUndefined(); const result = matcher.compare(void 0); expect(result.pass).toBe(true); }); it('fails when matching defined values', function() { const matcher = jasmineUnderTest.matchers.toBeUndefined(); const result = matcher.compare('foo'); expect(result.pass).toBe(false); }); }); jasmine-4.5.0/spec/core/matchers/toContainSpec.js000066400000000000000000000015361432731766000217630ustar00rootroot00000000000000describe('toContain', function() { it('delegates to jasmineUnderTest.matchersUtil.contains', function() { const matchersUtil = { contains: jasmine.createSpy('delegated-contains').and.returnValue(true) }, matcher = jasmineUnderTest.matchers.toContain(matchersUtil); const result = matcher.compare('ABC', 'B'); expect(matchersUtil.contains).toHaveBeenCalledWith('ABC', 'B'); expect(result.pass).toBe(true); }); it('works with custom equality testers', function() { const tester = function(a, b) { return a.toString() === b.toString(); }, matchersUtil = new jasmineUnderTest.MatchersUtil({ customTesters: [tester] }), matcher = jasmineUnderTest.matchers.toContain(matchersUtil); const result = matcher.compare(['1', '2'], 2); expect(result.pass).toBe(true); }); }); jasmine-4.5.0/spec/core/matchers/toEqualSpec.js000066400000000000000000001141311432731766000214330ustar00rootroot00000000000000/* eslint-disable compat/compat */ describe('toEqual', function() { 'use strict'; function compareEquals(actual, expected) { const matchersUtil = new jasmineUnderTest.MatchersUtil({ pp: jasmineUnderTest.makePrettyPrinter() }), matcher = jasmineUnderTest.matchers.toEqual(matchersUtil); const result = matcher.compare(actual, expected); return result; } it('delegates to equals function', function() { const matchersUtil = { equals: jasmine.createSpy('delegated-equals').and.returnValue(true), buildFailureMessage: function() { return 'does not matter'; }, DiffBuilder: new jasmineUnderTest.DiffBuilder() }, matcher = jasmineUnderTest.matchers.toEqual(matchersUtil); const result = matcher.compare(1, 1); expect(matchersUtil.equals).toHaveBeenCalledWith(1, 1, jasmine.anything()); expect(result.pass).toBe(true); }); it('works with custom equality testers', function() { const tester = function(a, b) { return a.toString() === b.toString(); }, matchersUtil = new jasmineUnderTest.MatchersUtil({ customTesters: [tester] }), matcher = jasmineUnderTest.matchers.toEqual(matchersUtil); const result = matcher.compare(1, '1'); expect(result.pass).toBe(true); }); it('reports the difference between objects that are not equal', function() { const actual = { x: 1, y: 3 }, expected = { x: 2, y: 3 }, message = 'Expected $.x = 1 to equal 2.'; expect(compareEquals(actual, expected).message).toEqual(message); }); it('reports differences between enumerable symbol properties', function() { const x = Symbol('x'), actual = { [x]: 1, y: 3 }, expected = { [x]: 2, y: 3 }, message = 'Expected $[Symbol(x)] = 1 to equal 2.'; expect(compareEquals(actual, expected).message).toEqual(message); }); it('excludes non-enumerable symbol properties from the comparison', function() { const sym = Symbol('foo'); const actual = {}; Object.defineProperty(actual, sym, { value: '', enumerable: false }); const expected = {}; expect(compareEquals(actual, expected).pass).toBeTrue(); }); it('reports the difference between nested objects that are not equal', function() { const actual = { x: { y: 1 } }, expected = { x: { y: 2 } }, message = 'Expected $.x.y = 1 to equal 2.'; expect(compareEquals(actual, expected).message).toEqual(message); }); it("formats property access so that it's valid JavaScript", function() { const actual = { 'my prop': 1 }, expected = { 'my prop': 2 }, message = "Expected $['my prop'] = 1 to equal 2."; expect(compareEquals(actual, expected).message).toEqual(message); }); it('reports missing properties', function() { const actual = { x: {} }, expected = { x: { y: 1 } }, message = 'Expected $.x to have properties\n' + ' y: 1'; expect(compareEquals(actual, expected).message).toEqual(message); }); it('reports missing symbol properties', function() { const actual = { x: {} }, expected = { x: { [Symbol('y')]: 1 } }, message = 'Expected $.x to have properties\n' + ' Symbol(y): 1'; expect(compareEquals(actual, expected).message).toEqual(message); }); it('reports extra symbol properties', function() { const actual = { x: { [Symbol('y')]: 1 } }, expected = { x: {} }, message = 'Expected $.x not to have properties\n' + ' Symbol(y): 1'; expect(compareEquals(actual, expected).message).toEqual(message); }); it('reports extra properties', function() { const actual = { x: { y: 1, z: 2 } }, expected = { x: {} }, message = 'Expected $.x not to have properties\n' + ' y: 1\n' + ' z: 2'; expect(compareEquals(actual, expected).message).toEqual(message); }); it('pretty-prints properties', function() { const actual = { x: { y: 'foo bar' } }, expected = { x: {} }, message = 'Expected $.x not to have properties\n' + " y: 'foo bar'"; expect(compareEquals(actual, expected).message).toEqual(message); }); it('uses custom object formatters to pretty-print simple properties', function() { function formatter(x) { if (typeof x === 'number') { return '|' + x + '|'; } } const actual = { x: { y: 1, z: 2, f: 4 } }, expected = { x: { y: 1, z: 2, g: 3 } }, pp = jasmineUnderTest.makePrettyPrinter([formatter]), matchersUtil = new jasmineUnderTest.MatchersUtil({ pp: pp }), matcher = jasmineUnderTest.matchers.toEqual(matchersUtil), message = 'Expected $.x to have properties\n' + ' g: |3|\n' + 'Expected $.x not to have properties\n' + ' f: |4|'; expect(matcher.compare(actual, expected).message).toEqual(message); }); it('uses custom object formatters to show simple values in diffs', function() { function formatter(x) { if (typeof x === 'number') { return '|' + x + '|'; } } const actual = [{ foo: 4 }], expected = [{ foo: 5 }], prettyPrinter = jasmineUnderTest.makePrettyPrinter([formatter]), matchersUtil = new jasmineUnderTest.MatchersUtil({ pp: prettyPrinter }), matcher = jasmineUnderTest.matchers.toEqual(matchersUtil), message = 'Expected $[0].foo = |4| to equal |5|.'; expect(matcher.compare(actual, expected).message).toEqual(message); }); it('uses custom object formatters to show more complex objects diffs', function() { function formatter(x) { if (x.hasOwnProperty('a')) { return '[thing with a=' + x.a + ', b=' + x.b + ']'; } } const actual = [ { foo: { a: 1, b: 2 }, bar: 'should not be pretty printed' } ], expected = [ { foo: { a: 5, b: 2 }, bar: "shouldn't be pretty printed" } ], prettyPrinter = jasmineUnderTest.makePrettyPrinter([formatter]), matchersUtil = new jasmineUnderTest.MatchersUtil({ pp: prettyPrinter }), matcher = jasmineUnderTest.matchers.toEqual(matchersUtil), message = 'Expected $[0].foo = [thing with a=1, b=2] to equal [thing with a=5, b=2].\n' + "Expected $[0].bar = 'should not be pretty printed' to equal 'shouldn't be pretty printed'."; expect(matcher.compare(actual, expected).message).toEqual(message); }); it('reports extra and missing properties of the root-level object', function() { const actual = { x: 1 }, expected = { a: 1 }, message = 'Expected object to have properties\n' + ' a: 1\n' + 'Expected object not to have properties\n' + ' x: 1'; expect(compareEquals(actual, expected).message).toEqual(message); }); it('reports multiple incorrect values', function() { const actual = { x: 1, y: 2 }, expected = { x: 3, y: 4 }, message = 'Expected $.x = 1 to equal 3.\n' + 'Expected $.y = 2 to equal 4.'; expect(compareEquals(actual, expected).message).toEqual(message); }); it('reports mismatch between actual child object and expected child number', function() { const actual = { x: { y: 2 } }, expected = { x: 1 }, message = 'Expected $.x = Object({ y: 2 }) to equal 1.'; expect(compareEquals(actual, expected).message).toEqual(message); }); it('uses the default failure message if actual is not an object', function() { const actual = 1, expected = { x: {} }, message = 'Expected 1 to equal Object({ x: Object({ }) }).'; expect(compareEquals(actual, expected).message).toEqual(message); }); it('uses the default failure message if expected is not an object', function() { const actual = { x: {} }, expected = 1, message = 'Expected Object({ x: Object({ }) }) to equal 1.'; expect(compareEquals(actual, expected).message).toEqual(message); }); it('uses the default failure message given arrays with different lengths', function() { const actual = [1, 2], expected = [1, 2, 3], message = 'Expected $.length = 2 to equal 3.\n' + 'Expected $[2] = undefined to equal 3.'; expect(compareEquals(actual, expected).message).toEqual(message); }); it('reports a mismatch between elements of equal-length arrays', function() { const actual = [1, 2, 5], expected = [1, 2, 3], message = 'Expected $[2] = 5 to equal 3.'; expect(compareEquals(actual, expected).message).toEqual(message); }); it('reports a mismatch between multiple array elements', function() { const actual = [2, 2, 5], expected = [1, 2, 3], message = 'Expected $[0] = 2 to equal 1.\n' + 'Expected $[2] = 5 to equal 3.'; expect(compareEquals(actual, expected).message).toEqual(message); }); it('reports a mismatch between properties of objects in arrays', function() { const actual = [{ x: 1 }], expected = [{ x: 2 }], message = 'Expected $[0].x = 1 to equal 2.'; expect(compareEquals(actual, expected).message).toEqual(message); }); it('reports a mismatch between arrays in objects', function() { const actual = { x: [1] }, expected = { x: [2] }, message = 'Expected $.x[0] = 1 to equal 2.'; expect(compareEquals(actual, expected).message).toEqual(message); }); it('reports mismatches between nested arrays', function() { const actual = [[1]], expected = [[2]], message = 'Expected $[0][0] = 1 to equal 2.'; expect(compareEquals(actual, expected).message).toEqual(message); }); it('reports mismatches between arrays of different types', function() { const actual = new Uint32Array([1, 2, 3]), expected = new Uint16Array([1, 2, 3]), message = 'Expected Uint32Array [ 1, 2, 3 ] to equal Uint16Array [ 1, 2, 3 ].'; expect(compareEquals(actual, expected).message).toEqual(message); }); it('reports mismatches involving NaN', function() { const actual = { x: 0 }, expected = { x: 0 / 0 }, message = 'Expected $.x = 0 to equal NaN.'; expect(compareEquals(actual, expected).message).toEqual(message); }); it('reports mismatches involving regular expressions', function() { const actual = { x: '1' }, expected = { x: /1/ }, message = "Expected $.x = '1' to equal /1/."; expect(compareEquals(actual, expected).message).toEqual(message); }); it('reports mismatches involving infinities', function() { const actual = { x: 0 }, expected = { x: 1 / 0 }, message = 'Expected $.x = 0 to equal Infinity.'; expect(compareEquals(actual, expected).message).toEqual(message); }); it('reports mismatches involving booleans', function() { const actual = { x: false }, expected = { x: true }, message = 'Expected $.x = false to equal true.'; expect(compareEquals(actual, expected).message).toEqual(message); }); it('reports mismatches involving strings', function() { const actual = { x: 'foo' }, expected = { x: 'bar' }, message = "Expected $.x = 'foo' to equal 'bar'."; expect(compareEquals(actual, expected).message).toEqual(message); }); it('reports mismatches involving undefined', function() { const actual = { x: void 0 }, expected = { x: 0 }, message = 'Expected $.x = undefined to equal 0.'; expect(compareEquals(actual, expected).message).toEqual(message); }); it('reports mismatches involving null', function() { const actual = { x: null }, expected = { x: 0 }, message = 'Expected $.x = null to equal 0.'; expect(compareEquals(actual, expected).message).toEqual(message); }); it('reports mismatches between objects with different constructors', function() { function Foo() {} function Bar() {} const actual = { x: new Foo() }, expected = { x: new Bar() }, message = 'Expected $.x to be a kind of Bar, but was Foo({ }).'; expect(compareEquals(actual, expected).message).toEqual(message); }); it('uses custom object formatters for the value but not the type when reporting objects with different constructors', function() { function Foo() {} function Bar() {} function formatter(x) { if (x instanceof Foo || x instanceof Bar) { return '|' + x + '|'; } } const actual = { x: new Foo() }, expected = { x: new Bar() }, message = 'Expected $.x to be a kind of Bar, but was |[object Object]|.', pp = jasmineUnderTest.makePrettyPrinter([formatter]), matchersUtil = new jasmineUnderTest.MatchersUtil({ pp: pp }), matcher = jasmineUnderTest.matchers.toEqual(matchersUtil); expect(matcher.compare(actual, expected).message).toEqual(message); }); it('reports type mismatches at the root level', function() { function Foo() {} function Bar() {} const actual = new Foo(), expected = new Bar(), message = 'Expected object to be a kind of Bar, but was Foo({ }).'; expect(compareEquals(actual, expected).message).toEqual(message); }); it('reports value mismatches at the root level', function() { expect(compareEquals(1, 2).message).toEqual('Expected 1 to equal 2.'); }); it('reports mismatches between objects with their own constructor property', function() { const actual = { x: { constructor: 'blerf' } }, expected = { x: { constructor: 'ftarrh' } }, message = "Expected $.x.constructor = 'blerf' to equal 'ftarrh'."; expect(compareEquals(actual, expected).message).toEqual(message); }); it('reports mismatches between an object with a real constructor and one with its own constructor property', function() { const actual = { x: {} }, expected = { x: { constructor: 'ftarrh' } }, message = 'Expected $.x to have properties\n' + " constructor: 'ftarrh'"; expect(compareEquals(actual, expected).message).toEqual(message); expect(compareEquals(expected, actual).message).toEqual( "Expected $.x not to have properties\n constructor: 'ftarrh'" ); }); it('reports mismatches between 0 and -0', function() { const actual = { x: 0 }, expected = { x: -0 }, message = 'Expected $.x = 0 to equal -0.'; expect(compareEquals(actual, expected).message).toEqual(message); }); it('reports mismatches between 0 and Number.MIN_VALUE', function() { const actual = { x: 0 }, expected = { x: Number.MIN_VALUE }, message = 'Expected $.x = 0 to equal 5e-324.'; expect(compareEquals(actual, expected).message).toEqual(message); }); it('reports mismatches between Errors', function() { const actual = { x: new Error('the error you got') }, expected = { x: new Error('the error you want') }, message = 'Expected $.x = Error: the error you got to equal Error: the error you want.'; expect(compareEquals(actual, expected).message).toEqual(message); }); it('reports mismatches between Functions', function() { const actual = { x: function() {} }, expected = { x: function() {} }, message = 'Expected $.x = Function to equal Function.'; expect(compareEquals(actual, expected).message).toEqual(message); }); it('reports mismatches between an object and objectContaining', function() { const actual = { a: 1, b: 4, c: 3, extra: 'ignored' }; const expected = jasmineUnderTest.objectContaining({ a: 1, b: 2, c: 3, d: 4 }); expect(compareEquals(actual, expected).message).toEqual( 'Expected $.b = 4 to equal 2.\n' + 'Expected $.d = undefined to equal 4.' ); }); it('reports mismatches between a non-object and objectContaining', function() { const actual = 1; const expected = jasmineUnderTest.objectContaining({ a: 1 }); expect(compareEquals(actual, expected).message).toEqual( "Expected 1 to equal ''." ); }); it('reports mismatches involving a nested objectContaining', function() { const actual = { x: { a: 1, b: 4, c: 3, extra: 'ignored' } }; const expected = { x: jasmineUnderTest.objectContaining({ a: 1, b: 2, c: 3, d: 4 }) }; expect(compareEquals(actual, expected).message).toEqual( 'Expected $.x.b = 4 to equal 2.\n' + 'Expected $.x.d = undefined to equal 4.' ); }); // == Sets == it('reports mismatches between Sets', function() { const actual = new Set(); actual.add(1); const expected = new Set(); expected.add(2); const message = 'Expected Set( 1 ) to equal Set( 2 ).'; expect(compareEquals(actual, expected).message).toEqual(message); }); it('reports deep mismatches within Sets', function() { const actual = new Set(); actual.add({ x: 1 }); const expected = new Set(); expected.add({ x: 2 }); const message = 'Expected Set( Object({ x: 1 }) ) to equal Set( Object({ x: 2 }) ).'; expect(compareEquals(actual, expected).message).toEqual(message); }); it('reports mismatches between Sets nested in objects', function() { const actualSet = new Set(); actualSet.add(1); const expectedSet = new Set(); expectedSet.add(2); const actual = { sets: [actualSet] }; const expected = { sets: [expectedSet] }; const message = 'Expected $.sets[0] = Set( 1 ) to equal Set( 2 ).'; expect(compareEquals(actual, expected).message).toEqual(message); }); it('reports mismatches between Sets of different lengths', function() { const actual = new Set(); actual.add(1); actual.add(2); const expected = new Set(); expected.add(2); const message = 'Expected Set( 1, 2 ) to equal Set( 2 ).'; expect(compareEquals(actual, expected).message).toEqual(message); }); it('reports mismatches between Sets where actual is missing a value from expected', function() { // Use 'duplicate' object in actual so sizes match const actual = new Set(); actual.add({ x: 1 }); actual.add({ x: 1 }); const expected = new Set(); expected.add({ x: 1 }); expected.add({ x: 2 }); const message = 'Expected Set( Object({ x: 1 }), Object({ x: 1 }) ) to equal Set( Object({ x: 1 }), Object({ x: 2 }) ).'; expect(compareEquals(actual, expected).message).toEqual(message); }); it('reports mismatches between Sets where actual has a value missing from expected', function() { // Use 'duplicate' object in expected so sizes match const actual = new Set(); actual.add({ x: 1 }); actual.add({ x: 2 }); const expected = new Set(); expected.add({ x: 1 }); expected.add({ x: 1 }); const message = 'Expected Set( Object({ x: 1 }), Object({ x: 2 }) ) to equal Set( Object({ x: 1 }), Object({ x: 1 }) ).'; expect(compareEquals(actual, expected).message).toEqual(message); }); // == Maps == it('does not report mismatches between deep equal Maps', function() { // values are the same but with different object identity const actual = new Map(); actual.set('a', { x: 1 }); const expected = new Map(); expected.set('a', { x: 1 }); expect(compareEquals(actual, expected).pass).toBe(true); }); it('reports deep mismatches within Maps', function() { const actual = new Map(); actual.set('a', { x: 1 }); const expected = new Map(); expected.set('a', { x: 2 }); const message = "Expected Map( [ 'a', Object({ x: 1 }) ] ) to equal Map( [ 'a', Object({ x: 2 }) ] )."; expect(compareEquals(actual, expected).message).toEqual(message); }); it('reports mismatches between Maps nested in objects', function() { const actual = { Maps: [new Map()] }; actual.Maps[0].set('a', 1); const expected = { Maps: [new Map()] }; expected.Maps[0].set('a', 2); const message = "Expected $.Maps[0] = Map( [ 'a', 1 ] ) to equal Map( [ 'a', 2 ] )."; expect(compareEquals(actual, expected).message).toEqual(message); }); it('reports mismatches between Maps of different lengths', function() { const actual = new Map(); actual.set('a', 1); const expected = new Map(); expected.set('a', 2); expected.set('b', 1); const message = "Expected Map( [ 'a', 1 ] ) to equal Map( [ 'a', 2 ], [ 'b', 1 ] )."; expect(compareEquals(actual, expected).message).toEqual(message); }); it('reports mismatches between Maps with equal values but differing keys', function() { const actual = new Map(); actual.set('a', 1); const expected = new Map(); expected.set('b', 1); const message = "Expected Map( [ 'a', 1 ] ) to equal Map( [ 'b', 1 ] )."; expect(compareEquals(actual, expected).message).toEqual(message); }); it('does not report mismatches between Maps with keys with same object identity', function() { const key = { x: 1 }; const actual = new Map(); actual.set(key, 2); const expected = new Map(); expected.set(key, 2); expect(compareEquals(actual, expected).pass).toBe(true); }); it('reports mismatches between Maps with identical keys with different object identity', function() { const actual = new Map(); actual.set({ x: 1 }, 2); const expected = new Map(); expected.set({ x: 1 }, 2); const message = 'Expected Map( [ Object({ x: 1 }), 2 ] ) to equal Map( [ Object({ x: 1 }), 2 ] ).'; expect(compareEquals(actual, expected).message).toEqual(message); }); it('does not report mismatches when comparing Map key to jasmine.anything()', function() { const actual = new Map(); actual.set('a', 1); const expected = new Map(); expected.set(jasmineUnderTest.anything(), 1); expect(compareEquals(actual, expected).pass).toBe(true); }); it('does not report mismatches when comparing Maps with the same symbol keys', function() { const key = Symbol(); const actual = new Map(); actual.set(key, 1); const expected = new Map(); expected.set(key, 1); expect(compareEquals(actual, expected).pass).toBe(true); }); it('reports mismatches between Maps with different symbol keys', function() { const actual = new Map(); actual.set(Symbol(), 1); const expected = new Map(); expected.set(Symbol(), 1); const message = 'Expected Map( [ Symbol(), 1 ] ) to equal Map( [ Symbol(), 1 ] ).'; expect(compareEquals(actual, expected).message).toBe(message); }); it('does not report mismatches when comparing Map symbol key to jasmine.anything()', function() { const actual = new Map(); actual.set(Symbol(), 1); const expected = new Map(); expected.set(jasmineUnderTest.anything(), 1); expect(compareEquals(actual, expected).pass).toBe(true); }); describe('DOM nodes', function() { function isNotRunningInBrowser() { return typeof document === 'undefined'; } beforeEach(function() { this.nonBrowser = isNotRunningInBrowser(); if (this.nonBrowser) { const JSDOM = require('jsdom').JSDOM; const dom = new JSDOM(); jasmineUnderTest.getGlobal().Node = dom.window.Node; this.doc = dom.window.document; } else { this.doc = document; } }); afterEach(function() { if (this.nonBrowser) { delete jasmineUnderTest.getGlobal().Node; } }); it('reports mismatches between DOM nodes with different tags', function() { const actual = { a: this.doc.createElement('div') }, expected = { a: this.doc.createElement('p') }, message = 'Expected $.a =
to equal

.'; expect(compareEquals(actual, expected).message).toEqual(message); }); it('reports mismatches between DOM nodes with different content', function() { const nodeA = this.doc.createElement('div'), nodeB = this.doc.createElement('div'); nodeA.setAttribute('thing', 'foo'); nodeB.setAttribute('thing', 'bar'); expect(nodeA.isEqualNode(nodeB)).toBe(false); const actual = { a: nodeA }, expected = { a: nodeB }, message = 'Expected $.a =

to equal
.'; expect(compareEquals(actual, expected).message).toEqual(message); }); it('reports mismatches between SVG nodes', function() { const nodeA = this.doc.createElementNS( 'http://www.w3.org/2000/svg', 'svg' ), nodeB = this.doc.createElementNS('http://www.w3.org/2000/svg', 'svg'); nodeA.setAttribute('height', '50'); nodeB.setAttribute('height', '30'); const rect = this.doc.createElementNS( 'http://www.w3.org/2000/svg', 'rect' ); rect.setAttribute('width', '50'); nodeA.appendChild(rect); expect(nodeA.isEqualNode(nodeB)).toBe(false); const actual = { a: nodeA }, expected = { a: nodeB }, message = 'Expected $.a = ... to equal .'; expect(compareEquals(actual, expected).message).toEqual(message); }); it('reports whole DOM node when attribute contains > character', function() { const nodeA = this.doc.createElement('div'), nodeB = this.doc.createElement('div'); nodeA.setAttribute('thing', '>>>'); nodeB.setAttribute('thing', 'bar'); expect(nodeA.isEqualNode(nodeB)).toBe(false); const actual = { a: nodeA }, expected = { a: nodeB }, message = 'Expected $.a =
to equal
.'; expect(compareEquals(actual, expected).message).toEqual(message); }); it('reports no content when DOM node has multiple empty text nodes', function() { const nodeA = this.doc.createElement('div'), nodeB = this.doc.createElement('div'); nodeA.appendChild(this.doc.createTextNode('')); nodeA.appendChild(this.doc.createTextNode('')); nodeA.appendChild(this.doc.createTextNode('')); nodeA.appendChild(this.doc.createTextNode('')); expect(nodeA.isEqualNode(nodeB)).toBe(false); const actual = { a: nodeA }, expected = { a: nodeB }, message = 'Expected $.a =
to equal
.'; expect(compareEquals(actual, expected).message).toEqual(message); }); it('reports content when DOM node has non empty text node', function() { const nodeA = this.doc.createElement('div'), nodeB = this.doc.createElement('div'); nodeA.appendChild(this.doc.createTextNode('Hello Jasmine!')); expect(nodeA.isEqualNode(nodeB)).toBe(false); const actual = { a: nodeA }, expected = { a: nodeB }, message = 'Expected $.a =
...
to equal
.'; expect(compareEquals(actual, expected).message).toEqual(message); }); it('reports empty DOM attributes', function() { const nodeA = this.doc.createElement('div'), nodeB = this.doc.createElement('div'); nodeA.setAttribute('contenteditable', ''); expect(nodeA.isEqualNode(nodeB)).toBe(false); const actual = { a: nodeA }, expected = { a: nodeB }, message = 'Expected $.a =
to equal
.'; expect(compareEquals(actual, expected).message).toEqual(message); }); it('reports 0 attr value as non empty DOM attribute', function() { const nodeA = this.doc.createElement('div'), nodeB = this.doc.createElement('div'); nodeA.setAttribute('contenteditable', 0); expect(nodeA.isEqualNode(nodeB)).toBe(false); const actual = { a: nodeA }, expected = { a: nodeB }, message = 'Expected $.a =
to equal
.'; expect(compareEquals(actual, expected).message).toEqual(message); }); it('reports mismatches between a DOM node and a bare Object', function() { const actual = { a: this.doc.createElement('div') }, expected = { a: {} }, message = 'Expected $.a =
to equal Object({ }).'; expect(compareEquals(actual, expected).message).toEqual(message); }); }); it('reports asymmetric mismatches', function() { const actual = { a: 1 }, expected = { a: jasmineUnderTest.any(String) }, message = 'Expected $.a = 1 to equal .'; expect(compareEquals(actual, expected).message).toEqual(message); expect(compareEquals(actual, expected).pass).toBe(false); }); it('reports asymmetric mismatches when the asymmetric comparand is the actual value', function() { const actual = { a: jasmineUnderTest.any(String) }, expected = { a: 1 }, message = 'Expected $.a = to equal 1.'; expect(compareEquals(actual, expected).message).toEqual(message); expect(compareEquals(actual, expected).pass).toBe(false); }); it('does not report a mismatch when asymmetric matchers are satisfied', function() { const actual = { a: 'a' }, expected = { a: jasmineUnderTest.any(String) }; expect(compareEquals(actual, expected).message).toEqual(''); expect(compareEquals(actual, expected).pass).toBe(true); }); it('works on big complex stuff', function() { const actual = { foo: [{ bar: 1, things: ['a', 'b'] }, { bar: 2, things: ['a', 'b'] }], baz: [{ a: { b: 1 } }], quux: 1, nan: 0, aRegexp: 'hi', inf: -1 / 0, boolean: false, notDefined: 0, aNull: void 0 }; const expected = { foo: [ { bar: 2, things: ['a', 'b', 'c'] }, { bar: 2, things: ['a', 'd'] } ], baz: [{ a: { b: 1, c: 1 } }], quux: [], nan: 0 / 0, aRegexp: /hi/, inf: 1 / 0, boolean: true, notDefined: void 0, aNull: null }; const message = 'Expected $.foo[0].bar = 1 to equal 2.\n' + 'Expected $.foo[0].things.length = 2 to equal 3.\n' + "Expected $.foo[0].things[2] = undefined to equal 'c'.\n" + "Expected $.foo[1].things[1] = 'b' to equal 'd'.\n" + 'Expected $.baz[0].a to have properties\n' + ' c: 1\n' + 'Expected $.quux = 1 to equal [ ].\n' + 'Expected $.nan = 0 to equal NaN.\n' + "Expected $.aRegexp = 'hi' to equal /hi/.\n" + 'Expected $.inf = -Infinity to equal Infinity.\n' + 'Expected $.boolean = false to equal true.\n' + 'Expected $.notDefined = 0 to equal undefined.\n' + 'Expected $.aNull = undefined to equal null.'; expect(compareEquals(actual, expected).message).toEqual(message); }); describe('different length arrays', function() { it('actual array is longer', function() { const actual = [1, 1, 2, 3, 5], expected = [1, 1, 2, 3], message = 'Expected $.length = 5 to equal 4.\n' + 'Unexpected $[4] = 5 in array.'; expect(compareEquals(actual, expected).pass).toBe(false); expect(compareEquals(actual, expected).message).toEqual(message); }); it('uses custom object formatters when the actual array is longer', function() { function formatter(x) { if (typeof x === 'number') { return '|' + x + '|'; } } const actual = [1, 1, 2, 3, 5], expected = [1, 1, 2, 3], pp = jasmineUnderTest.makePrettyPrinter([formatter]), matchersUtil = new jasmineUnderTest.MatchersUtil({ pp: pp }), matcher = jasmineUnderTest.matchers.toEqual(matchersUtil), message = 'Expected $.length = |5| to equal |4|.\n' + 'Unexpected $[4] = |5| in array.'; expect(matcher.compare(actual, expected).message).toEqual(message); }); it('expected array is longer', function() { const actual = [1, 1, 2, 3], expected = [1, 1, 2, 3, 5], message = 'Expected $.length = 4 to equal 5.\n' + 'Expected $[4] = undefined to equal 5.'; expect(compareEquals(actual, expected).pass).toBe(false); expect(compareEquals(actual, expected).message).toEqual(message); }); it('undefined in middle of actual array', function() { const actual = [1, void 0, 3], expected = [1, 2, 3], message = 'Expected $[1] = undefined to equal 2.'; expect(compareEquals(actual, expected).pass).toBe(false); expect(compareEquals(actual, expected).message).toEqual(message); }); it('undefined in middle of expected array', function() { const actual = [1, 2, 3], expected = [1, void 0, 3], message = 'Expected $[1] = 2 to equal undefined.'; expect(compareEquals(actual, expected).pass).toBe(false); expect(compareEquals(actual, expected).message).toEqual(message); }); it('actual array is longer by 4 elements', function() { const actual = [1, 1, 2, 3, 5, 8, 13], expected = [1, 1, 2], message = 'Expected $.length = 7 to equal 3.\n' + 'Unexpected $[3] = 3 in array.\n' + 'Unexpected $[4] = 5 in array.\n' + 'Unexpected $[5] = 8 in array.\n' + 'Unexpected $[6] = 13 in array.'; expect(compareEquals(actual, expected).pass).toBe(false); expect(compareEquals(actual, expected).message).toEqual(message); }); it('expected array is longer by 4 elements', function() { const actual = [1, 1, 2], expected = [1, 1, 2, 3, 5, 8, 13], message = 'Expected $.length = 3 to equal 7.\n' + 'Expected $[3] = undefined to equal 3.\n' + 'Expected $[4] = undefined to equal 5.\n' + 'Expected $[5] = undefined to equal 8.\n' + 'Expected $[6] = undefined to equal 13.'; expect(compareEquals(actual, expected).pass).toBe(false); expect(compareEquals(actual, expected).message).toEqual(message); }); it('different length and different elements', function() { const actual = [1], expected = [2, 3], message = 'Expected $.length = 1 to equal 2.\n' + 'Expected $[0] = 1 to equal 2.\n' + 'Expected $[1] = undefined to equal 3.'; expect(compareEquals(actual, expected).pass).toBe(false); expect(compareEquals(actual, expected).message).toEqual(message); }); it('object with nested array (actual longer than expected)', function() { const actual = { values: [1, 1, 2, 3] }, expected = { values: [1, 1, 2] }, message = 'Expected $.values.length = 4 to equal 3.\n' + 'Unexpected $.values[3] = 3 in array.'; expect(compareEquals(actual, expected).pass).toBe(false); expect(compareEquals(actual, expected).message).toEqual(message); }); it('object with nested array (expected longer than actual)', function() { const actual = { values: [1, 1, 2] }, expected = { values: [1, 1, 2, 3] }, message = 'Expected $.values.length = 3 to equal 4.\n' + 'Expected $.values[3] = undefined to equal 3.'; expect(compareEquals(actual, expected).pass).toBe(false); expect(compareEquals(actual, expected).message).toEqual(message); }); it('array with unexpected nested object', function() { const actual = [1, 1, 2, { value: 3 }], expected = [1, 1, 2], message = 'Expected $.length = 4 to equal 3.\n' + 'Unexpected $[3] = Object({ value: 3 }) in array.'; expect(compareEquals(actual, expected).pass).toBe(false); expect(compareEquals(actual, expected).message).toEqual(message); }); it('array with missing nested object', function() { const actual = [1, 1, 2], expected = [1, 1, 2, { value: 3 }], message = 'Expected $.length = 3 to equal 4.\n' + 'Expected $[3] = undefined to equal Object({ value: 3 }).'; expect(compareEquals(actual, expected).pass).toBe(false); expect(compareEquals(actual, expected).message).toEqual(message); }); it('array with nested different length array', function() { const actual = [[1], [1, 2]], expected = [[1, 1], [2]], message = 'Expected $[0].length = 1 to equal 2.\n' + 'Expected $[0][1] = undefined to equal 1.\n' + 'Expected $[1].length = 2 to equal 1.\n' + 'Expected $[1][0] = 1 to equal 2.\n' + 'Unexpected $[1][1] = 2 in array.'; expect(compareEquals(actual, expected).pass).toBe(false); expect(compareEquals(actual, expected).message).toEqual(message); }); it('last element of longer array is undefined', function() { const actual = [1, 2], expected = [1, 2, void 0], message = 'Expected $.length = 2 to equal 3.'; expect(compareEquals(actual, expected).pass).toBe(false); expect(compareEquals(actual, expected).message).toEqual(message); }); }); // == Symbols == describe('Symbols', function() { it('Fails if Symbol compared to Object', function() { const sym = Symbol('foo'); const obj = {}; expect(sym).not.toEqual(obj); }); it('Passes Symbol with itself', function() { const sym = Symbol('foo'); expect(sym).toEqual(sym); }); it('Fails if two Symbols with same value are compared', function() { const symA = Symbol('foo'); const symB = Symbol('foo'); expect(symA).not.toEqual(symB); }); it('Fails if two Symbols with different value are compared', function() { const symA = Symbol('foo'); const symB = Symbol('bar'); expect(symA).not.toEqual(symB); }); it('Fails if Symbol compared to NaN', function() { const sym = Symbol('foo'); expect(sym).not.toEqual(NaN); }); it('Fails if Symbol compared to Infinity', function() { const sym = Symbol('foo'); expect(sym).not.toEqual(Infinity); }); it('Fails if Symbol compared to String', function() { const sym = Symbol('foo'); const str = 'foo'; expect(sym).not.toEqual(str); }); it('Fails if Symbol compared to Number', function() { const sym = Symbol('foo'); const num = Math.random(); expect(sym).not.toEqual(num); }); it('Fails if Symbol compared to Boolean', function() { const sym = Symbol('foo'); expect(sym).not.toEqual(true); expect(sym).not.toEqual(false); }); it('Fails if Symbol compared to Undefined', function() { const sym = Symbol('foo'); expect(sym).not.toEqual(undefined); }); it('Fails if Symbol compared to null', function() { const sym = Symbol('foo'); expect(sym).not.toEqual(null); }); it('Fails if Symbol compared to []', function() { const sym = Symbol('foo'); const arr = ['foo']; expect(sym).not.toEqual(arr); }); it('Fails if Symbol compared to Function', function() { const sym = Symbol('foo'); const f = function func() {}; expect(sym).not.toEqual(f); }); }); }); jasmine-4.5.0/spec/core/matchers/toHaveBeenCalledBeforeSpec.js000066400000000000000000000076601432731766000243010ustar00rootroot00000000000000describe('toHaveBeenCalledBefore', function() { it('throws an exception when the actual is not a spy', function() { const matcher = jasmineUnderTest.matchers.toHaveBeenCalledBefore({ pp: jasmineUnderTest.makePrettyPrinter() }), fn = function() {}, spy = new jasmineUnderTest.Env().createSpy('a spy'); expect(function() { matcher.compare(fn, spy); }).toThrowError(Error, /Expected a spy, but got Function./); }); it('throws an exception when the expected is not a spy', function() { const matcher = jasmineUnderTest.matchers.toHaveBeenCalledBefore({ pp: jasmineUnderTest.makePrettyPrinter() }), spy = new jasmineUnderTest.Env().createSpy('a spy'), fn = function() {}; expect(function() { matcher.compare(spy, fn); }).toThrowError(Error, /Expected a spy, but got Function./); }); it('fails when the actual was not called', function() { const matcher = jasmineUnderTest.matchers.toHaveBeenCalledBefore(), firstSpy = new jasmineUnderTest.Spy('first spy'), secondSpy = new jasmineUnderTest.Spy('second spy'); secondSpy(); const result = matcher.compare(firstSpy, secondSpy); expect(result.pass).toBe(false); expect(result.message).toMatch( /Expected spy first spy to have been called./ ); }); it('fails when the expected was not called', function() { const matcher = jasmineUnderTest.matchers.toHaveBeenCalledBefore(), firstSpy = new jasmineUnderTest.Spy('first spy'), secondSpy = new jasmineUnderTest.Spy('second spy'); firstSpy(); const result = matcher.compare(firstSpy, secondSpy); expect(result.pass).toBe(false); expect(result.message).toMatch( /Expected spy second spy to have been called./ ); }); it('fails when the actual is called after the expected', function() { const matcher = jasmineUnderTest.matchers.toHaveBeenCalledBefore(), firstSpy = new jasmineUnderTest.Spy('first spy'), secondSpy = new jasmineUnderTest.Spy('second spy'); secondSpy(); firstSpy(); const result = matcher.compare(firstSpy, secondSpy); expect(result.pass).toBe(false); expect(result.message).toEqual( 'Expected spy first spy to have been called before spy second spy' ); }); it('fails when the actual is called before and after the expected', function() { const matcher = jasmineUnderTest.matchers.toHaveBeenCalledBefore(), firstSpy = new jasmineUnderTest.Spy('first spy'), secondSpy = new jasmineUnderTest.Spy('second spy'); firstSpy(); secondSpy(); firstSpy(); const result = matcher.compare(firstSpy, secondSpy); expect(result.pass).toBe(false); expect(result.message).toEqual( 'Expected latest call to spy first spy to have been called before first call to spy second spy (no interleaved calls)' ); }); it('fails when the expected is called before and after the actual', function() { const matcher = jasmineUnderTest.matchers.toHaveBeenCalledBefore(), firstSpy = new jasmineUnderTest.Spy('first spy'), secondSpy = new jasmineUnderTest.Spy('second spy'); secondSpy(); firstSpy(); secondSpy(); const result = matcher.compare(firstSpy, secondSpy); expect(result.pass).toBe(false); expect(result.message).toEqual( 'Expected first call to spy second spy to have been called after latest call to spy first spy (no interleaved calls)' ); }); it('passes when the actual is called before the expected', function() { const matcher = jasmineUnderTest.matchers.toHaveBeenCalledBefore(), firstSpy = new jasmineUnderTest.Spy('first spy'), secondSpy = new jasmineUnderTest.Spy('second spy'); firstSpy(); secondSpy(); const result = matcher.compare(firstSpy, secondSpy); expect(result.pass).toBe(true); expect(result.message).toEqual( 'Expected spy first spy to not have been called before spy second spy, but it was' ); }); }); jasmine-4.5.0/spec/core/matchers/toHaveBeenCalledOnceWithSpec.js000066400000000000000000000104301432731766000246040ustar00rootroot00000000000000describe('toHaveBeenCalledOnceWith', function() { it('passes when the actual was called only once and with matching parameters', function() { const pp = jasmineUnderTest.makePrettyPrinter(), util = new jasmineUnderTest.MatchersUtil({ pp: pp }), matcher = jasmineUnderTest.matchers.toHaveBeenCalledOnceWith(util), calledSpy = new jasmineUnderTest.Spy('called-spy'); calledSpy('a', 'b'); const result = matcher.compare(calledSpy, 'a', 'b'); expect(result.pass).toBe(true); expect(result.message).toEqual( "Expected spy called-spy to have been called 0 times, multiple times, or once, but with arguments different from:\n [ 'a', 'b' ]\nBut the actual call was:\n [ 'a', 'b' ].\n\n" ); }); it('supports custom equality testers', function() { const customEqualityTesters = [ function() { return true; } ], matchersUtil = new jasmineUnderTest.MatchersUtil({ customTesters: customEqualityTesters }), matcher = jasmineUnderTest.matchers.toHaveBeenCalledOnceWith( matchersUtil ), calledSpy = new jasmineUnderTest.Spy('called-spy'); calledSpy('a', 'b'); const result = matcher.compare(calledSpy, 'a', 'a'); expect(result.pass).toBe(true); }); it('fails when the actual was never called', function() { const pp = jasmineUnderTest.makePrettyPrinter(), util = new jasmineUnderTest.MatchersUtil({ pp: pp }), matcher = jasmineUnderTest.matchers.toHaveBeenCalledOnceWith(util), calledSpy = new jasmineUnderTest.Spy('called-spy'); const result = matcher.compare(calledSpy, 'a', 'b'); expect(result.pass).toBe(false); expect(result.message).toEqual( "Expected spy called-spy to have been called only once, and with given args:\n [ 'a', 'b' ]\nBut it was never called.\n\n" ); }); it('fails when the actual was called once with different parameters', function() { const pp = jasmineUnderTest.makePrettyPrinter(), util = new jasmineUnderTest.MatchersUtil({ pp: pp }), matcher = jasmineUnderTest.matchers.toHaveBeenCalledOnceWith(util), calledSpy = new jasmineUnderTest.Spy('called-spy'); calledSpy('a', 'c'); const result = matcher.compare(calledSpy, 'a', 'b'); expect(result.pass).toBe(false); expect(result.message).toEqual( "Expected spy called-spy to have been called only once, and with given args:\n [ 'a', 'b' ]\nBut the actual call was:\n [ 'a', 'c' ].\nExpected $[1] = 'c' to equal 'b'.\n\n" ); }); it('fails when the actual was called multiple times with expected parameters', function() { const pp = jasmineUnderTest.makePrettyPrinter(), util = new jasmineUnderTest.MatchersUtil({ pp: pp }), matcher = jasmineUnderTest.matchers.toHaveBeenCalledOnceWith(util), calledSpy = new jasmineUnderTest.Spy('called-spy'); calledSpy('a', 'b'); calledSpy('a', 'b'); const result = matcher.compare(calledSpy, 'a', 'b'); expect(result.pass).toBe(false); expect(result.message).toEqual( "Expected spy called-spy to have been called only once, and with given args:\n [ 'a', 'b' ]\nBut the actual calls were:\n [ 'a', 'b' ],\n [ 'a', 'b' ].\n\n" ); }); it('fails when the actual was called multiple times (one of them - with expected parameters)', function() { const pp = jasmineUnderTest.makePrettyPrinter(), util = new jasmineUnderTest.MatchersUtil({ pp: pp }), matcher = jasmineUnderTest.matchers.toHaveBeenCalledOnceWith(util), calledSpy = new jasmineUnderTest.Spy('called-spy'); calledSpy('a', 'b'); calledSpy('a', 'c'); const result = matcher.compare(calledSpy, 'a', 'b'); expect(result.pass).toBe(false); expect(result.message).toEqual( "Expected spy called-spy to have been called only once, and with given args:\n [ 'a', 'b' ]\nBut the actual calls were:\n [ 'a', 'b' ],\n [ 'a', 'c' ].\n\n" ); }); it('throws an exception when the actual is not a spy', function() { const pp = jasmineUnderTest.makePrettyPrinter(), util = new jasmineUnderTest.MatchersUtil({ pp: pp }), matcher = jasmineUnderTest.matchers.toHaveBeenCalledOnceWith(util), fn = function() {}; expect(function() { matcher.compare(fn); }).toThrowError(/Expected a spy, but got Function./); }); }); jasmine-4.5.0/spec/core/matchers/toHaveBeenCalledSpec.js000066400000000000000000000033221432731766000231450ustar00rootroot00000000000000describe('toHaveBeenCalled', function() { it('passes when the actual was called, with a custom .not fail message', function() { const matcher = jasmineUnderTest.matchers.toHaveBeenCalled(), calledSpy = new jasmineUnderTest.Spy('called-spy'); calledSpy(); const result = matcher.compare(calledSpy); expect(result.pass).toBe(true); expect(result.message).toEqual( 'Expected spy called-spy not to have been called.' ); }); it('fails when the actual was not called', function() { const matcher = jasmineUnderTest.matchers.toHaveBeenCalled(), uncalledSpy = new jasmineUnderTest.Spy('uncalled spy'); const result = matcher.compare(uncalledSpy); expect(result.pass).toBe(false); }); it('throws an exception when the actual is not a spy', function() { const matcher = jasmineUnderTest.matchers.toHaveBeenCalled({ pp: jasmineUnderTest.makePrettyPrinter() }), fn = function() {}; expect(function() { matcher.compare(fn); }).toThrowError(Error, /Expected a spy, but got Function./); }); it('throws an exception when invoked with any arguments', function() { const matcher = jasmineUnderTest.matchers.toHaveBeenCalled(), spy = new jasmineUnderTest.Spy('sample spy'); expect(function() { matcher.compare(spy, 'foo'); }).toThrowError(Error, /Does not take arguments, use toHaveBeenCalledWith/); }); it('has a custom message on failure', function() { const matcher = jasmineUnderTest.matchers.toHaveBeenCalled(), spy = new jasmineUnderTest.Spy('sample-spy'); const result = matcher.compare(spy); expect(result.message).toEqual( 'Expected spy sample-spy to have been called.' ); }); }); jasmine-4.5.0/spec/core/matchers/toHaveBeenCalledTimesSpec.js000066400000000000000000000055401432731766000241530ustar00rootroot00000000000000describe('toHaveBeenCalledTimes', function() { it('passes when the actual 0 matches the expected 0 ', function() { const matcher = jasmineUnderTest.matchers.toHaveBeenCalledTimes(), calledSpy = new jasmineUnderTest.Spy('called-spy'), result = matcher.compare(calledSpy, 0); expect(result.pass).toBeTruthy(); }); it('passes when the actual matches the expected', function() { const matcher = jasmineUnderTest.matchers.toHaveBeenCalledTimes(), calledSpy = new jasmineUnderTest.Spy('called-spy'); calledSpy(); const result = matcher.compare(calledSpy, 1); expect(result.pass).toBe(true); }); it('fails when expected numbers is not supplied', function() { const matcher = jasmineUnderTest.matchers.toHaveBeenCalledTimes(), spy = new jasmineUnderTest.Spy('spy'); spy(); expect(function() { matcher.compare(spy); }).toThrowError( /The expected times failed is a required argument and must be a number./ ); }); it('fails when the actual was called less than the expected', function() { const matcher = jasmineUnderTest.matchers.toHaveBeenCalledTimes(), uncalledSpy = new jasmineUnderTest.Spy('uncalled spy'); const result = matcher.compare(uncalledSpy, 2); expect(result.pass).toBe(false); }); it('fails when the actual was called more than expected', function() { const matcher = jasmineUnderTest.matchers.toHaveBeenCalledTimes(), uncalledSpy = new jasmineUnderTest.Spy('uncalled spy'); uncalledSpy(); uncalledSpy(); const result = matcher.compare(uncalledSpy, 1); expect(result.pass).toBe(false); }); it('throws an exception when the actual is not a spy', function() { const matcher = jasmineUnderTest.matchers.toHaveBeenCalledTimes({ pp: jasmineUnderTest.makePrettyPrinter() }), fn = function() {}; expect(function() { matcher.compare(fn); }).toThrowError(/Expected a spy, but got Function./); }); it('has a custom message on failure that tells it was called only once', function() { const matcher = jasmineUnderTest.matchers.toHaveBeenCalledTimes(), spy = new jasmineUnderTest.Spy('sample-spy'); spy(); spy(); spy(); spy(); const result = matcher.compare(spy, 1); expect(result.message).toEqual( 'Expected spy sample-spy to have been called once. It was called ' + 4 + ' times.' ); }); it('has a custom message on failure that tells how many times it was called', function() { const matcher = jasmineUnderTest.matchers.toHaveBeenCalledTimes(), spy = new jasmineUnderTest.Spy('sample-spy'); spy(); spy(); spy(); spy(); const result = matcher.compare(spy, 2); expect(result.message).toEqual( 'Expected spy sample-spy to have been called 2 times. It was called ' + 4 + ' times.' ); }); }); jasmine-4.5.0/spec/core/matchers/toHaveBeenCalledWithSpec.js000066400000000000000000000063771432731766000240160ustar00rootroot00000000000000describe('toHaveBeenCalledWith', function() { it('passes when the actual was called with matching parameters', function() { const matchersUtil = { contains: jasmine.createSpy('delegated-contains').and.returnValue(true), pp: jasmineUnderTest.makePrettyPrinter() }, matcher = jasmineUnderTest.matchers.toHaveBeenCalledWith(matchersUtil), calledSpy = new jasmineUnderTest.Spy('called-spy'); calledSpy('a', 'b'); const result = matcher.compare(calledSpy, 'a', 'b'); expect(result.pass).toBe(true); expect(result.message()).toEqual( "Expected spy called-spy not to have been called with:\n [ 'a', 'b' ]\nbut it was." ); }); it('supports custom equality testers', function() { const customEqualityTesters = [ function() { return true; } ], matchersUtil = new jasmineUnderTest.MatchersUtil({ customTesters: customEqualityTesters }), matcher = jasmineUnderTest.matchers.toHaveBeenCalledWith(matchersUtil), calledSpy = new jasmineUnderTest.Spy('called-spy'); calledSpy('a', 'b'); const result = matcher.compare(calledSpy, 'a', 'b'); expect(result.pass).toBe(true); }); it('fails when the actual was not called', function() { const matchersUtil = { contains: jasmine .createSpy('delegated-contains') .and.returnValue(false), pp: jasmineUnderTest.makePrettyPrinter() }, matcher = jasmineUnderTest.matchers.toHaveBeenCalledWith(matchersUtil), uncalledSpy = new jasmineUnderTest.Spy('uncalled spy'); const result = matcher.compare(uncalledSpy); expect(result.pass).toBe(false); expect(result.message()).toEqual( 'Expected spy uncalled spy to have been called with:\n [ ]\nbut it was never called.' ); }); it('fails when the actual was called with different parameters', function() { const matchersUtil = new jasmineUnderTest.MatchersUtil({ pp: jasmineUnderTest.makePrettyPrinter() }), matcher = jasmineUnderTest.matchers.toHaveBeenCalledWith(matchersUtil), calledSpy = new jasmineUnderTest.Spy('called spy'); calledSpy('a'); calledSpy('c', 'd'); calledSpy('a', 'b', 'c'); const result = matcher.compare(calledSpy, 'a', 'b'); expect(result.pass).toBe(false); expect(result.message()).toEqual( 'Expected spy called spy to have been called with:\n' + " [ 'a', 'b' ]\n" + 'but actual calls were:\n' + " [ 'a' ],\n" + " [ 'c', 'd' ],\n" + " [ 'a', 'b', 'c' ].\n\n" + 'Call 0:\n' + ' Expected $.length = 1 to equal 2.\n' + " Expected $[1] = undefined to equal 'b'.\n" + 'Call 1:\n' + " Expected $[0] = 'c' to equal 'a'.\n" + " Expected $[1] = 'd' to equal 'b'.\n" + 'Call 2:\n' + ' Expected $.length = 3 to equal 2.\n' + " Unexpected $[2] = 'c' in array." ); }); it('throws an exception when the actual is not a spy', function() { const matcher = jasmineUnderTest.matchers.toHaveBeenCalledWith({ pp: jasmineUnderTest.makePrettyPrinter() }), fn = function() {}; expect(function() { matcher.compare(fn); }).toThrowError(/Expected a spy, but got Function./); }); }); jasmine-4.5.0/spec/core/matchers/toHaveClassSpec.js000066400000000000000000000034331432731766000222370ustar00rootroot00000000000000describe('toHaveClass', function() { beforeEach(function() { this.domHelpers = jasmine.getEnv().domHelpers(); }); it('fails for a DOM element that lacks the expected class', function() { const matcher = jasmineUnderTest.matchers.toHaveClass(), result = matcher.compare( this.domHelpers.createElementWithClassName(''), 'foo' ); expect(result.pass).toBe(false); }); it('passes for a DOM element that has the expected class', function() { const matcher = jasmineUnderTest.matchers.toHaveClass(), el = this.domHelpers.createElementWithClassName('foo bar baz'); expect(matcher.compare(el, 'foo').pass).toBe(true); expect(matcher.compare(el, 'bar').pass).toBe(true); expect(matcher.compare(el, 'bar').pass).toBe(true); }); it('fails for a DOM element that only has other classes', function() { const matcher = jasmineUnderTest.matchers.toHaveClass(), el = this.domHelpers.createElementWithClassName('foo bar'); expect(matcher.compare(el, 'fo').pass).toBe(false); }); it('throws an exception when actual is not a DOM element', function() { const matcher = jasmineUnderTest.matchers.toHaveClass({ 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 = this.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"); }); }); jasmine-4.5.0/spec/core/matchers/toHaveSizeSpec.js000066400000000000000000000072501432731766000221050ustar00rootroot00000000000000describe('toHaveSize', function() { 'use strict'; it('passes for an array whose length matches', function() { const matcher = jasmineUnderTest.matchers.toHaveSize(), result = matcher.compare([1, 2], 2); expect(result.pass).toBe(true); }); it('fails for an array whose length does not match', function() { const matcher = jasmineUnderTest.matchers.toHaveSize(), result = matcher.compare([1, 2, 3], 2); expect(result.pass).toBe(false); }); 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); expect(result.pass).toBe(true); }); it('fails for an object with a different number of keys', function() { const matcher = jasmineUnderTest.matchers.toHaveSize(), result = matcher.compare({ a: 1, b: 2 }, 1); expect(result.pass).toBe(false); }); it('passes for an object with an explicit `length` property that matches', function() { const matcher = jasmineUnderTest.matchers.toHaveSize(), result = matcher.compare({ a: 1, b: 2, length: 5 }, 5); expect(result.pass).toBe(true); }); it('fails for an object with an explicit `length` property that does not match', function() { const matcher = jasmineUnderTest.matchers.toHaveSize(), result = matcher.compare({ a: 1, b: 2, length: 5 }, 1); expect(result.pass).toBe(false); }); it('passes for a string whose length matches', function() { const matcher = jasmineUnderTest.matchers.toHaveSize(), result = matcher.compare('ab', 2); expect(result.pass).toBe(true); }); it('fails for a string whose length does not match', function() { const matcher = jasmineUnderTest.matchers.toHaveSize(), result = matcher.compare('abc', 2); expect(result.pass).toBe(false); }); it('passes for a Map whose length matches', function() { const map = new Map(); map.set('a', 1); map.set('b', 2); const matcher = jasmineUnderTest.matchers.toHaveSize(), result = matcher.compare(map, 2); expect(result.pass).toBe(true); }); it('fails for a Map whose length does not match', function() { const map = new Map(); map.set('a', 1); map.set('b', 2); const matcher = jasmineUnderTest.matchers.toHaveSize(), result = matcher.compare(map, 1); expect(result.pass).toBe(false); }); it('passes for a Set whose length matches', function() { const set = new Set(); set.add('a'); set.add('b'); const matcher = jasmineUnderTest.matchers.toHaveSize(), result = matcher.compare(set, 2); expect(result.pass).toBe(true); }); it('fails for a Set whose length does not match', function() { const set = new Set(); set.add('a'); set.add('b'); const matcher = jasmineUnderTest.matchers.toHaveSize(), result = matcher.compare(set, 1); expect(result.pass).toBe(false); }); it('throws an error for WeakSet', function() { const matcher = jasmineUnderTest.matchers.toHaveSize(); expect(function() { matcher.compare(new WeakSet(), 2); }).toThrowError('Cannot get size of [object WeakSet].'); }); it('throws an error for WeakMap', function() { const matcher = jasmineUnderTest.matchers.toHaveSize(); expect(function() { matcher.compare(new WeakMap(), 2); }).toThrowError(/Cannot get size of \[object (WeakMap|Object)\]\./); }); it('throws an error for DataView', function() { const matcher = jasmineUnderTest.matchers.toHaveSize(); expect(function() { matcher.compare(new DataView(new ArrayBuffer(128)), 2); }).toThrowError(/Cannot get size of \[object (DataView|Object)\]\./); }); }); jasmine-4.5.0/spec/core/matchers/toHaveSpyInteractionsSpec.js000077500000000000000000000067211432731766000243360ustar00rootroot00000000000000describe('toHaveSpyInteractions', function() { it('passes when there are spy interactions', function() { let matcher = jasmineUnderTest.matchers.toHaveSpyInteractions(); let spyObj = jasmineUnderTest .getEnv() .createSpyObj('NewClass', ['spyA', 'spyB']); spyObj.spyA(); let result = matcher.compare(spyObj); expect(result.pass).toBe(true); }); it('passes when there are multiple spy interactions', function() { let matcher = jasmineUnderTest.matchers.toHaveSpyInteractions(); let spyObj = jasmineUnderTest .getEnv() .createSpyObj('NewClass', ['spyA', 'spyB']); spyObj.spyA(); spyObj.spyB(); spyObj.spyA(); let result = matcher.compare(spyObj); expect(result.pass).toBe(true); }); it('fails when there are no spy interactions', function() { let matcher = jasmineUnderTest.matchers.toHaveSpyInteractions(); let spyObj = jasmineUnderTest .getEnv() .createSpyObj('NewClass', ['spyA', 'spyB']); let result = matcher.compare(spyObj); expect(result.pass).toBe(false); expect(result.message).toContain( 'Expected spy object spies to have been called' ); }); it('shows the right message is negated', function() { let matcher = jasmineUnderTest.matchers.toHaveSpyInteractions(); let spyObj = jasmineUnderTest .getEnv() .createSpyObj('NewClass', ['spyA', 'spyB']); spyObj.spyA(); let result = matcher.compare(spyObj); expect(result.pass).toBe(true); expect(result.message).toContain( // Will be shown only on negate. 'Expected spy object spies not to have been called' ); }); it('fails when only non-observed spy object interactions are interacted', function() { let matcher = jasmineUnderTest.matchers.toHaveSpyInteractions(); let spyObj = jasmineUnderTest .getEnv() .createSpyObj('NewClass', ['spyA', 'spyB']); spyObj.otherMethod = function() {}; spyObj.otherMethod(); let result = matcher.compare(spyObj); expect(result.pass).toBe(false); expect(result.message).toContain( 'Expected spy object spies to have been called' ); }); it(`throws an error if a non-object is passed`, function() { let matcher = jasmineUnderTest.matchers.toHaveSpyInteractions(); expect(function() { matcher.compare(true); }).toThrowError(Error, /Expected a spy object, but got/); expect(function() { matcher.compare(123); }).toThrowError(Error, /Expected a spy object, but got/); expect(function() { matcher.compare('string'); }).toThrowError(Error, /Expected a spy object, but got/); }); it('throws an error if arguments are passed', function() { let matcher = jasmineUnderTest.matchers.toHaveSpyInteractions(); let spyObj = jasmineUnderTest .getEnv() .createSpyObj('NewClass', ['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.toHaveSpyInteractions(); const spyObj = jasmineUnderTest .getEnv() .createSpyObj('NewClass', ['notSpy']); // Removing spy since spy objects cannot be created without spies. spyObj.notSpy = function() {}; expect(function() { matcher.compare(spyObj); }).toThrowError( Error, /Expected a spy object with spies, but object has no spies/ ); }); }); jasmine-4.5.0/spec/core/matchers/toMatchSpec.js000066400000000000000000000022701432731766000214200ustar00rootroot00000000000000describe('toMatch', function() { it('passes when RegExps are equivalent', function() { const matcher = jasmineUnderTest.matchers.toMatch(); const result = matcher.compare(/foo/, /foo/); expect(result.pass).toBe(true); }); it('fails when RegExps are not equivalent', function() { const matcher = jasmineUnderTest.matchers.toMatch(); const result = matcher.compare(/bar/, /foo/); expect(result.pass).toBe(false); }); it('passes when the actual matches the expected string as a pattern', function() { const matcher = jasmineUnderTest.matchers.toMatch(); const result = matcher.compare('foosball', 'foo'); expect(result.pass).toBe(true); }); it('fails when the actual matches the expected string as a pattern', function() { const matcher = jasmineUnderTest.matchers.toMatch(); const result = matcher.compare('bar', 'foo'); expect(result.pass).toBe(false); }); it('throws an Error when the expected is not a String or RegExp', function() { const matcher = jasmineUnderTest.matchers.toMatch(); expect(function() { matcher.compare('foo', { bar: 'baz' }); }).toThrowError(/Expected is not a String or a RegExp/); }); }); jasmine-4.5.0/spec/core/matchers/toThrowErrorSpec.js000066400000000000000000000253051432731766000225050ustar00rootroot00000000000000describe('toThrowError', function() { it('throws an error when the actual is not a function', function() { const matcher = jasmineUnderTest.matchers.toThrowError(); expect(function() { matcher.compare({}); }).toThrowError(/Actual is not a Function/); }); it('throws an error when the expected is not an Error, string, or RegExp', function() { const matcher = jasmineUnderTest.matchers.toThrowError(), fn = function() { throw new Error('foo'); }; expect(function() { matcher.compare(fn, 1); }).toThrowError(/Expected is not an Error, string, or RegExp./); }); it('throws an error when the expected error type is not an Error', function() { const matcher = jasmineUnderTest.matchers.toThrowError(), fn = function() { throw new Error('foo'); }; expect(function() { matcher.compare(fn, void 0, 'foo'); }).toThrowError(/Expected error type is not an Error./); }); it('throws an error when the expected error message is not a string or RegExp', function() { const matcher = jasmineUnderTest.matchers.toThrowError(), fn = function() { throw new Error('foo'); }; expect(function() { matcher.compare(fn, Error, 1); }).toThrowError(/Expected error message is not a string or RegExp./); }); it('fails if actual does not throw at all', function() { const matcher = jasmineUnderTest.matchers.toThrowError(), fn = function() { return true; }; const result = matcher.compare(fn); expect(result.pass).toBe(false); expect(result.message).toEqual('Expected function to throw an Error.'); }); it('fails if thrown is not an instanceof Error', function() { const matcher = jasmineUnderTest.matchers.toThrowError({ pp: jasmineUnderTest.makePrettyPrinter() }), fn = function() { throw 4; }; const result = matcher.compare(fn); expect(result.pass).toBe(false); expect(result.message()).toEqual( 'Expected function to throw an Error, but it threw 4.' ); }); describe('when error is from another frame', function() { function isNotRunningInBrowser() { return typeof document === 'undefined'; } let iframe = null; afterEach(function() { if (iframe !== null) { document.body.removeChild(iframe); } }); it('passes if thrown is an instanceof Error regardless of global that contains its constructor', function() { if (isNotRunningInBrowser()) { return; } const matcher = jasmineUnderTest.matchers.toThrowError(); iframe = document.body.appendChild(document.createElement('iframe')); iframe.src = 'about:blank'; const iframeDocument = iframe.contentWindow.document; iframeDocument.body.appendChild( iframeDocument.createElement('script') ).textContent = "function method() { throw new Error('foo'); }"; const result = matcher.compare(iframe.contentWindow.method); expect(result.pass).toBe(true); expect(result.message).toEqual( 'Expected function not to throw an Error, but it threw Error.' ); }); }); it('fails with the correct message if thrown is a falsy value', function() { const matcher = jasmineUnderTest.matchers.toThrowError({ pp: jasmineUnderTest.makePrettyPrinter() }), fn = function() { throw undefined; }; const result = matcher.compare(fn); expect(result.pass).toBe(false); expect(result.message()).toEqual( 'Expected function to throw an Error, but it threw undefined.' ); }); it('passes if thrown is a type of Error, but there is no expected error', function() { const matcher = jasmineUnderTest.matchers.toThrowError(), fn = function() { throw new TypeError(); }; const result = matcher.compare(fn); expect(result.pass).toBe(true); expect(result.message).toEqual( 'Expected function not to throw an Error, but it threw TypeError.' ); }); it('passes if thrown is an Error and the expected is the same message', function() { const matcher = jasmineUnderTest.matchers.toThrowError({ pp: jasmineUnderTest.makePrettyPrinter() }), fn = function() { throw new Error('foo'); }; const result = matcher.compare(fn, 'foo'); expect(result.pass).toBe(true); expect(result.message()).toEqual( "Expected function not to throw an exception with message 'foo'." ); }); it('fails if thrown is an Error and the expected is not the same message', function() { const matcher = jasmineUnderTest.matchers.toThrowError({ pp: jasmineUnderTest.makePrettyPrinter() }), fn = function() { throw new Error('foo'); }; const result = matcher.compare(fn, 'bar'); expect(result.pass).toBe(false); expect(result.message()).toEqual( "Expected function to throw an exception with message 'bar', but it threw an exception with message 'foo'." ); }); it('passes if thrown is an Error and the expected is a RegExp that matches the message', function() { const matcher = jasmineUnderTest.matchers.toThrowError({ pp: jasmineUnderTest.makePrettyPrinter() }), fn = function() { throw new Error('a long message'); }; const result = matcher.compare(fn, /long/); expect(result.pass).toBe(true); expect(result.message()).toEqual( 'Expected function not to throw an exception with a message matching /long/.' ); }); it('fails if thrown is an Error and the expected is a RegExp that does not match the message', function() { const matcher = jasmineUnderTest.matchers.toThrowError({ pp: jasmineUnderTest.makePrettyPrinter() }), fn = function() { throw new Error('a long message'); }; const result = matcher.compare(fn, /foo/); expect(result.pass).toBe(false); expect(result.message()).toEqual( "Expected function to throw an exception with a message matching /foo/, but it threw an exception with message 'a long message'." ); }); it('passes if thrown is an Error and the expected the same Error', function() { const matcher = jasmineUnderTest.matchers.toThrowError(), fn = function() { throw new Error(); }; const result = matcher.compare(fn, Error); expect(result.pass).toBe(true); expect(result.message()).toEqual('Expected function not to throw Error.'); }); it('passes if thrown is a custom error that takes arguments and the expected is the same error', function() { const matcher = jasmineUnderTest.matchers.toThrowError(), CustomError = function CustomError(arg) { arg.x; }, fn = function() { throw new CustomError({ x: 1 }); }; CustomError.prototype = new Error(); const result = matcher.compare(fn, CustomError); expect(result.pass).toBe(true); expect(result.message()).toEqual( 'Expected function not to throw CustomError.' ); }); it('fails if thrown is an Error and the expected is a different Error', function() { const matcher = jasmineUnderTest.matchers.toThrowError(), fn = function() { throw new Error(); }; const result = matcher.compare(fn, TypeError); expect(result.pass).toBe(false); expect(result.message()).toEqual( 'Expected function to throw TypeError, but it threw Error.' ); }); it('passes if thrown is a type of Error and it is equal to the expected Error and message', function() { const matchersUtil = { equals: jasmine.createSpy('delegated-equal').and.returnValue(true), pp: jasmineUnderTest.makePrettyPrinter() }, matcher = jasmineUnderTest.matchers.toThrowError(matchersUtil), fn = function() { throw new TypeError('foo'); }; const result = matcher.compare(fn, TypeError, 'foo'); expect(result.pass).toBe(true); expect(result.message()).toEqual( "Expected function not to throw TypeError with message 'foo'." ); }); it('passes if thrown is a custom error that takes arguments and it is equal to the expected custom error and message', function() { const matchersUtil = { equals: jasmine.createSpy('delegated-equal').and.returnValue(true), pp: jasmineUnderTest.makePrettyPrinter() }, matcher = jasmineUnderTest.matchers.toThrowError(matchersUtil), CustomError = function CustomError(arg) { this.message = arg.message; }, fn = function() { throw new CustomError({ message: 'foo' }); }; CustomError.prototype = new Error(); const result = matcher.compare(fn, CustomError, 'foo'); expect(result.pass).toBe(true); expect(result.message()).toEqual( "Expected function not to throw CustomError with message 'foo'." ); }); it('fails if thrown is a type of Error and the expected is a different Error', function() { const matchersUtil = { equals: jasmine.createSpy('delegated-equal').and.returnValue(false), pp: jasmineUnderTest.makePrettyPrinter() }, matcher = jasmineUnderTest.matchers.toThrowError(matchersUtil), fn = function() { throw new TypeError('foo'); }; const result = matcher.compare(fn, TypeError, 'bar'); expect(result.pass).toBe(false); expect(result.message()).toEqual( "Expected function to throw TypeError with message 'bar', but it threw TypeError with message 'foo'." ); }); it('passes if thrown is a type of Error and has the same type as the expected Error and the message matches the expected message', function() { const matchersUtil = { equals: jasmine.createSpy('delegated-equal').and.returnValue(true), pp: jasmineUnderTest.makePrettyPrinter() }, matcher = jasmineUnderTest.matchers.toThrowError(matchersUtil), fn = function() { throw new TypeError('foo'); }; const result = matcher.compare(fn, TypeError, /foo/); expect(result.pass).toBe(true); expect(result.message()).toEqual( 'Expected function not to throw TypeError with a message matching /foo/.' ); }); it('fails if thrown is a type of Error and the expected is a different Error', function() { const matchersUtil = { equals: jasmine.createSpy('delegated-equal').and.returnValue(false), pp: jasmineUnderTest.makePrettyPrinter() }, matcher = jasmineUnderTest.matchers.toThrowError(matchersUtil), fn = function() { throw new TypeError('foo'); }; const result = matcher.compare(fn, TypeError, /bar/); expect(result.pass).toBe(false); expect(result.message()).toEqual( "Expected function to throw TypeError with a message matching /bar/, but it threw TypeError with message 'foo'." ); }); }); jasmine-4.5.0/spec/core/matchers/toThrowMatchingSpec.js000066400000000000000000000052331432731766000231440ustar00rootroot00000000000000describe('toThrowMatching', function() { it('throws an error when the actual is not a function', function() { const matcher = jasmineUnderTest.matchers.toThrowMatching(); expect(function() { matcher.compare({}, function() { return true; }); }).toThrowError(/Actual is not a Function/); }); it('throws an error when the expected is not a function', function() { const matcher = jasmineUnderTest.matchers.toThrowMatching(), fn = function() { throw new Error('foo'); }; expect(function() { matcher.compare(fn, 1); }).toThrowError(/Predicate is not a Function/); }); it('fails if actual does not throw at all', function() { const matcher = jasmineUnderTest.matchers.toThrowMatching(), fn = function() { return true; }; const result = matcher.compare(fn, function() { return true; }); expect(result.pass).toBe(false); expect(result.message).toEqual('Expected function to throw an exception.'); }); it('fails with the correct message if thrown is a falsy value', function() { const matcher = jasmineUnderTest.matchers.toThrowMatching({ pp: jasmineUnderTest.makePrettyPrinter() }), fn = function() { throw undefined; }; const result = matcher.compare(fn, function() { return false; }); expect(result.pass).toBe(false); expect(result.message()).toEqual( 'Expected function to throw an exception matching a predicate, but it threw undefined.' ); }); it('passes if the argument is a function that returns true when called with the error', function() { const matcher = jasmineUnderTest.matchers.toThrowMatching(), predicate = function(e) { return e.message === 'nope'; }, fn = function() { throw new TypeError('nope'); }; const result = matcher.compare(fn, predicate); expect(result.pass).toBe(true); expect(result.message).toEqual( 'Expected function not to throw an exception matching a predicate.' ); }); it('fails if the argument is a function that returns false when called with the error', function() { const matcher = jasmineUnderTest.matchers.toThrowMatching({ pp: jasmineUnderTest.makePrettyPrinter() }), predicate = function(e) { return e.message === 'oh no'; }, fn = function() { throw new TypeError('nope'); }; const result = matcher.compare(fn, predicate); expect(result.pass).toBe(false); expect(result.message()).toEqual( "Expected function to throw an exception matching a predicate, but it threw TypeError with message 'nope'." ); }); }); jasmine-4.5.0/spec/core/matchers/toThrowSpec.js000066400000000000000000000062151432731766000214720ustar00rootroot00000000000000describe('toThrow', function() { it('throws an error when the actual is not a function', function() { const matcher = jasmineUnderTest.matchers.toThrow(); expect(function() { matcher.compare({}); matcherComparator({}); }).toThrowError(/Actual is not a Function/); }); it('fails if actual does not throw', function() { const matcher = jasmineUnderTest.matchers.toThrow(), fn = function() { return true; }; const result = matcher.compare(fn); expect(result.pass).toBe(false); expect(result.message).toEqual('Expected function to throw an exception.'); }); it('passes if it throws but there is no expected', function() { const matchersUtil = { equals: jasmine.createSpy('delegated-equal').and.returnValue(true), pp: jasmineUnderTest.makePrettyPrinter() }, matcher = jasmineUnderTest.matchers.toThrow(matchersUtil), fn = function() { throw 5; }; const result = matcher.compare(fn); expect(result.pass).toBe(true); expect(result.message()).toEqual( 'Expected function not to throw, but it threw 5.' ); }); it('passes even if what is thrown is falsy', function() { const matcher = jasmineUnderTest.matchers.toThrow({ pp: jasmineUnderTest.makePrettyPrinter() }), fn = function() { throw undefined; }; const result = matcher.compare(fn); expect(result.pass).toBe(true); expect(result.message()).toEqual( 'Expected function not to throw, but it threw undefined.' ); }); it('passes if what is thrown is equivalent to what is expected', function() { const matchersUtil = { equals: jasmine.createSpy('delegated-equal').and.returnValue(true), pp: jasmineUnderTest.makePrettyPrinter() }, matcher = jasmineUnderTest.matchers.toThrow(matchersUtil), fn = function() { throw 5; }; const result = matcher.compare(fn, 5); expect(result.pass).toBe(true); expect(result.message()).toEqual('Expected function not to throw 5.'); }); it('fails if what is thrown is not equivalent to what is expected', function() { const matchersUtil = { equals: jasmine.createSpy('delegated-equal').and.returnValue(false), pp: jasmineUnderTest.makePrettyPrinter() }, matcher = jasmineUnderTest.matchers.toThrow(matchersUtil), fn = function() { throw 5; }; const result = matcher.compare(fn, 'foo'); expect(result.pass).toBe(false); expect(result.message()).toEqual( "Expected function to throw 'foo', but it threw 5." ); }); it('fails if what is thrown is not equivalent to undefined', function() { const matchersUtil = { equals: jasmine.createSpy('delegated-equal').and.returnValue(false), pp: jasmineUnderTest.makePrettyPrinter() }, matcher = jasmineUnderTest.matchers.toThrow(matchersUtil), fn = function() { throw 5; }; const result = matcher.compare(fn, void 0); expect(result.pass).toBe(false); expect(result.message()).toEqual( 'Expected function to throw undefined, but it threw 5.' ); }); }); jasmine-4.5.0/spec/helpers/000077500000000000000000000000001432731766000155535ustar00rootroot00000000000000jasmine-4.5.0/spec/helpers/BrowserFlags.js000066400000000000000000000006201432731766000205070ustar00rootroot00000000000000(function(env) { function browserVersion(matchFn) { const userAgent = jasmine.getGlobal().navigator.userAgent; if (!userAgent) { return void 0; } const match = matchFn(userAgent); return match ? parseFloat(match[1]) : void 0; } env.firefoxVersion = browserVersion(function(userAgent) { return /Firefox\/([0-9]{0,})/.exec(userAgent); }); })(jasmine.getEnv()); jasmine-4.5.0/spec/helpers/defineJasmineUnderTest.js000066400000000000000000000004201432731766000225040ustar00rootroot00000000000000(function() { // By the time onload is called, jasmineRequire will be redefined to point // to the Jasmine source files (and not jasmine.js). So re-require window.jasmineUnderTest = jasmineRequire.core(jasmineRequire); jasmineRequire.html(jasmineUnderTest); })(); jasmine-4.5.0/spec/helpers/domHelpers.js000066400000000000000000000010111432731766000202040ustar00rootroot00000000000000(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; } return { document: doc, createElementWithClassName: function(className) { const el = this.document.createElement('div'); el.className = className; return el; } }; } env.domHelpers = domHelpers; })(jasmine.getEnv()); jasmine-4.5.0/spec/helpers/integrationMatchers.js000066400000000000000000000035361432731766000221320ustar00rootroot00000000000000(function(env) { env.registerIntegrationMatchers = function() { jasmine.addMatchers({ toHaveFailedExpectationsForRunnable: function() { return { compare: function(actual, fullName, expectedFailures) { let foundRunnable = false, expectations = true, foundFailures = []; for (let i = 0; i < actual.calls.count(); i++) { const args = actual.calls.argsFor(i)[0]; if (args.fullName === fullName) { foundRunnable = true; for (let j = 0; j < args.failedExpectations.length; j++) { foundFailures.push(args.failedExpectations[j].message); } for (let j = 0; j < expectedFailures.length; j++) { const failure = foundFailures[j]; const expectedFailure = expectedFailures[j]; if ( Object.prototype.toString.call(expectedFailure) === '[object RegExp]' ) { expectations = expectations && expectedFailure.test(failure); } else { expectations = expectations && failure === expectedFailure; } } break; } } return { pass: foundRunnable && expectations, message: !foundRunnable ? 'The runnable "' + fullName + '" never finished' : 'Expected runnable "' + fullName + '" to have failures ' + jasmine.basicPrettyPrinter_(expectedFailures) + ' but it had ' + jasmine.basicPrettyPrinter_(foundFailures) }; } }; } }); }; })(jasmine.getEnv()); jasmine-4.5.0/spec/helpers/nodeDefineJasmineUnderTest.js000066400000000000000000000012771432731766000233250ustar00rootroot00000000000000(function() { const path = require('path'), glob = require('glob'); const jasmineUnderTestRequire = require(path.join( __dirname, '../../src/core/requireCore.js' )); global.getJasmineRequireObj = function() { return jasmineUnderTestRequire; }; function getSourceFiles() { const src_files = ['core/**/*.js', 'version.js'].map(function(file) { return path.join(__dirname, '../../', 'src/', file); }); const files = src_files.flatMap(g => glob.sync(g)); files.forEach(function(resolvedFile) { require(resolvedFile); }); } getSourceFiles(); global.jasmineUnderTest = jasmineUnderTestRequire.core( jasmineUnderTestRequire ); })(); jasmine-4.5.0/spec/helpers/resetEnv.js000066400000000000000000000002051432731766000177010ustar00rootroot00000000000000beforeEach(function() { // env is stateful. Ensure that it does not leak between tests. jasmineUnderTest.currentEnv_ = null; }); jasmine-4.5.0/spec/html/000077500000000000000000000000001432731766000150555ustar00rootroot00000000000000jasmine-4.5.0/spec/html/HtmlReporterSpec.js000066400000000000000000001654451432731766000206740ustar00rootroot00000000000000describe('HtmlReporter', function() { let env; beforeEach(function() { env = new jasmineUnderTest.Env(); }); afterEach(function() { env.cleanup_(); }); it('builds the initial DOM elements, including the title banner', function() { const container = document.createElement('div'), getContainer = function() { return container; }, reporter = new jasmineUnderTest.HtmlReporter({ env: env, getContainer: getContainer, createElement: function() { return document.createElement.apply(document, arguments); }, createTextNode: function() { return document.createTextNode.apply(document, arguments); } }); reporter.initialize(); // Main top-level elements expect(container.querySelector('div.jasmine_html-reporter')).toBeTruthy(); expect(container.querySelector('div.jasmine-banner')).toBeTruthy(); expect(container.querySelector('div.jasmine-alert')).toBeTruthy(); expect(container.querySelector('div.jasmine-results')).toBeTruthy(); expect(container.querySelector('ul.jasmine-symbol-summary')).toBeTruthy(); // title banner const banner = container.querySelector('.jasmine-banner'); const title = banner.querySelector('a.jasmine-title'); expect(title.getAttribute('href')).toEqual('http://jasmine.github.io/'); expect(title.getAttribute('target')).toEqual('_blank'); const version = banner.querySelector('.jasmine-version'); expect(version.textContent).toEqual(jasmineUnderTest.version); }); it('builds a single reporter even if initialized multiple times', function() { const container = document.createElement('div'), getContainer = function() { return container; }, reporter = new jasmineUnderTest.HtmlReporter({ env: env, getContainer: getContainer, createElement: function() { return document.createElement.apply(document, arguments); }, createTextNode: function() { return document.createTextNode.apply(document, arguments); } }); reporter.initialize(); reporter.initialize(); reporter.initialize(); expect( container.querySelectorAll('div.jasmine_html-reporter').length ).toEqual(1); }); describe('when a spec is done', function() { describe('and no expectations ran', function() { let container, reporter; beforeEach(function() { container = document.createElement('div'); reporter = new jasmineUnderTest.HtmlReporter({ env: env, getContainer: function() { return container; }, createElement: function() { return document.createElement.apply(document, arguments); }, createTextNode: function() { return document.createTextNode.apply(document, arguments); } }); spyOn(console, 'warn'); spyOn(console, 'error'); reporter.initialize(); }); it('should log warning to the console and print a special symbol when empty spec status is passed', function() { reporter.specDone({ status: 'passed', fullName: 'Some Name', passedExpectations: [], failedExpectations: [] }); /* eslint-disable-next-line no-console */ expect(console.warn).toHaveBeenCalledWith( "Spec 'Some Name' has no expectations." ); const specEl = container.querySelector('.jasmine-symbol-summary li'); expect(specEl.getAttribute('class')).toEqual('jasmine-empty'); }); it('should log error to the console and print a failure symbol when empty spec status is failed', function() { reporter.specDone({ status: 'failed', fullName: 'Some Name', passedExpectations: [], failedExpectations: [] }); /* eslint-disable-next-line no-console */ expect(console.error).toHaveBeenCalledWith( "Spec 'Some Name' has no expectations." ); const specEl = container.querySelector('.jasmine-symbol-summary li'); expect(specEl.getAttribute('class')).toEqual('jasmine-failed'); }); }); it('reports the status symbol of a excluded spec', function() { const container = document.createElement('div'), getContainer = function() { return container; }, reporter = new jasmineUnderTest.HtmlReporter({ env: env, getContainer: getContainer, createElement: function() { return document.createElement.apply(document, arguments); }, createTextNode: function() { return document.createTextNode.apply(document, arguments); } }); reporter.initialize(); reporter.specDone({ id: 789, status: 'excluded', fullName: 'symbols should have titles', passedExpectations: [], failedExpectations: [] }); const specEl = container.querySelector('.jasmine-symbol-summary li'); expect(specEl.getAttribute('class')).toEqual('jasmine-excluded'); expect(specEl.getAttribute('id')).toEqual('spec_789'); expect(specEl.getAttribute('title')).toEqual( 'symbols should have titles' ); }); it('reports the status symbol of a pending spec', function() { const container = document.createElement('div'), getContainer = function() { return container; }, reporter = new jasmineUnderTest.HtmlReporter({ env: env, getContainer: getContainer, createElement: function() { return document.createElement.apply(document, arguments); }, createTextNode: function() { return document.createTextNode.apply(document, arguments); } }); reporter.initialize(); reporter.specDone({ id: 789, status: 'pending', passedExpectations: [], failedExpectations: [] }); const specEl = container.querySelector('.jasmine-symbol-summary li'); expect(specEl.getAttribute('class')).toEqual('jasmine-pending'); expect(specEl.getAttribute('id')).toEqual('spec_789'); }); it('reports the status symbol of a passing spec', function() { const container = document.createElement('div'), getContainer = function() { return container; }, reporter = new jasmineUnderTest.HtmlReporter({ env: env, getContainer: getContainer, createElement: function() { return document.createElement.apply(document, arguments); }, createTextNode: function() { return document.createTextNode.apply(document, arguments); } }); reporter.initialize(); reporter.specDone({ id: 123, status: 'passed', passedExpectations: [{ passed: true }], failedExpectations: [] }); const statuses = container.querySelector('.jasmine-symbol-summary'); const specEl = statuses.querySelector('li'); expect(specEl.getAttribute('class')).toEqual('jasmine-passed'); expect(specEl.getAttribute('id')).toEqual('spec_123'); }); it('reports the status symbol of a failing spec', function() { const container = document.createElement('div'), getContainer = function() { return container; }, reporter = new jasmineUnderTest.HtmlReporter({ env: env, getContainer: getContainer, createElement: function() { return document.createElement.apply(document, arguments); }, createTextNode: function() { return document.createTextNode.apply(document, arguments); } }); reporter.initialize(); reporter.specDone({ id: 345, status: 'failed', failedExpectations: [], passedExpectations: [] }); const specEl = container.querySelector('.jasmine-symbol-summary li'); expect(specEl.getAttribute('class')).toEqual('jasmine-failed'); expect(specEl.getAttribute('id')).toEqual('spec_345'); }); }); describe('when there are deprecation warnings', function() { it('displays the messages in their own alert bars', function() { const container = document.createElement('div'), getContainer = function() { return container; }, reporter = new jasmineUnderTest.HtmlReporter({ env: env, getContainer: getContainer, createElement: function() { return document.createElement.apply(document, arguments); }, createTextNode: function() { return document.createTextNode.apply(document, arguments); } }); reporter.initialize(); reporter.jasmineStarted({}); reporter.specDone({ status: 'passed', fullName: 'a spec with a deprecation', deprecationWarnings: [{ message: 'spec deprecation' }], failedExpectations: [], passedExpectations: [] }); reporter.suiteDone({ status: 'passed', fullName: 'a suite with a deprecation', deprecationWarnings: [{ message: 'suite deprecation' }], failedExpectations: [] }); reporter.jasmineDone({ deprecationWarnings: [{ message: 'global deprecation' }], failedExpectations: [] }); const alertBars = container.querySelectorAll( '.jasmine-alert .jasmine-bar' ); expect(alertBars.length).toEqual(4); expect(alertBars[1].innerHTML).toMatch( /spec deprecation.*\(in spec: a spec with a deprecation\)/ ); expect(alertBars[1].getAttribute('class')).toEqual( 'jasmine-bar jasmine-warning' ); expect(alertBars[2].innerHTML).toMatch( /suite deprecation.*\(in suite: a suite with a deprecation\)/ ); expect(alertBars[3].innerHTML).toMatch(/global deprecation/); expect(alertBars[3].innerHTML).not.toMatch(/in /); }); it('displays expandable stack traces', function() { const container = document.createElement('div'), getContainer = function() { return container; }, reporter = new jasmineUnderTest.HtmlReporter({ env: env, getContainer: getContainer, createElement: function() { return document.createElement.apply(document, arguments); }, createTextNode: function() { return document.createTextNode.apply(document, arguments); } }); reporter.initialize(); reporter.jasmineStarted({}); reporter.jasmineDone({ deprecationWarnings: [ { message: 'a deprecation', stack: 'a stack trace' } ], failedExpectations: [] }); const expander = container.querySelector( '.jasmine-alert .jasmine-bar .jasmine-expander' ); const expanderContents = expander.querySelector( '.jasmine-expander-contents' ); expect(expanderContents.textContent).toMatch(/a stack trace/); const expanderLink = expander.querySelector('a'); expect(expander).not.toHaveClass('jasmine-expanded'); expect(expanderLink.textContent).toMatch(/Show stack trace/); expanderLink.click(); expect(expander).toHaveClass('jasmine-expanded'); expect(expanderLink.textContent).toMatch(/Hide stack trace/); expanderLink.click(); expect(expander).not.toHaveClass('jasmine-expanded'); expect(expanderLink.textContent).toMatch(/Show stack trace/); }); it('omits the expander when there is no stack trace', function() { const container = document.createElement('div'), getContainer = function() { return container; }, reporter = new jasmineUnderTest.HtmlReporter({ env: env, getContainer: getContainer, createElement: function() { return document.createElement.apply(document, arguments); }, createTextNode: function() { return document.createTextNode.apply(document, arguments); } }); reporter.initialize(); reporter.jasmineStarted({}); reporter.jasmineDone({ deprecationWarnings: [ { message: 'a deprecation', stack: '' } ], failedExpectations: [] }); const warningBar = container.querySelector('.jasmine-warning'); expect(warningBar.querySelector('.jasmine-expander')).toBeFalsy(); }); it('nicely formats the verboseDeprecations note', function() { const container = document.createElement('div'), getContainer = function() { return container; }, reporter = new jasmineUnderTest.HtmlReporter({ env: env, getContainer: getContainer, createElement: function() { return document.createElement.apply(document, arguments); }, createTextNode: function() { return document.createTextNode.apply(document, arguments); } }); reporter.initialize(); reporter.jasmineStarted({}); reporter.jasmineDone({ deprecationWarnings: [ { message: 'a deprecation\nNote: This message will be shown only once. Set config.verboseDeprecations to true to see every occurrence.' } ], failedExpectations: [] }); const alertBar = container.querySelector('.jasmine-warning'); expect(alertBar.innerHTML).toMatch( /a deprecation
Note: This message will be shown only once/ ); }); }); describe('when Jasmine is done', function() { it('adds a warning to the link title of specs that have no expectations', function() { if (!window.console) { window.console = { error: function() {} }; } const container = document.createElement('div'), getContainer = function() { return container; }, reporter = new jasmineUnderTest.HtmlReporter({ env: env, getContainer: getContainer, createElement: function() { return document.createElement.apply(document, arguments); }, createTextNode: function() { return document.createTextNode.apply(document, arguments); } }); spyOn(console, 'error'); reporter.initialize(); reporter.jasmineStarted({}); reporter.suiteStarted({ id: 1 }); reporter.specStarted({ id: 1, passedExpectations: [], failedExpectations: [] }); reporter.specDone({ id: 1, status: 'passed', description: 'Spec Description', passedExpectations: [], failedExpectations: [] }); reporter.suiteDone({ id: 1 }); reporter.jasmineDone({}); const summary = container.querySelector('.jasmine-summary'); const suite = summary.childNodes[0]; const specs = suite.childNodes[1]; const spec = specs.childNodes[0]; const specLink = spec.childNodes[0]; expect(specLink.innerHTML).toMatch(/SPEC HAS NO EXPECTATIONS/); }); it('reports the run time', function() { const container = document.createElement('div'), getContainer = function() { return container; }, reporter = new jasmineUnderTest.HtmlReporter({ env: env, getContainer: getContainer, createElement: function() { return document.createElement.apply(document, arguments); }, createTextNode: function() { return document.createTextNode.apply(document, arguments); } }); reporter.initialize(); reporter.jasmineStarted({}); reporter.jasmineDone({ totalTime: 100 }); const duration = container.querySelector( '.jasmine-alert .jasmine-duration' ); expect(duration.innerHTML).toMatch(/finished in 0.1s/); }); it('reports the suite and spec names with status', function() { const container = document.createElement('div'), getContainer = function() { return container; }, reporter = new jasmineUnderTest.HtmlReporter({ env: env, getContainer: getContainer, createElement: function() { return document.createElement.apply(document, arguments); }, createTextNode: function() { return document.createTextNode.apply(document, arguments); }, addToExistingQueryString: function(key, value) { return '?foo=bar&' + key + '=' + value; } }); reporter.initialize(); reporter.jasmineStarted({}); reporter.suiteStarted({ id: 1, description: 'A Suite', fullName: 'A Suite' }); let specResult = { id: 123, description: 'with a spec', fullName: 'A Suite with a spec', status: 'passed', failedExpectations: [], passedExpectations: [{ passed: true }] }; reporter.specStarted(specResult); reporter.specDone(specResult); reporter.suiteStarted({ id: 2, description: 'inner suite', fullName: 'A Suite inner suite' }); specResult = { id: 124, description: 'with another spec', fullName: 'A Suite inner suite with another spec', status: 'passed', failedExpectations: [], passedExpectations: [{ passed: true }] }; reporter.specStarted(specResult); reporter.specDone(specResult); reporter.suiteDone({ id: 2, status: 'things', description: 'inner suite', fullName: 'A Suite inner suite' }); specResult = { id: 209, description: 'with a failing spec', fullName: 'A Suite inner with a failing spec', status: 'failed', failedExpectations: [{}], passedExpectations: [] }; reporter.specStarted(specResult); reporter.specDone(specResult); reporter.suiteDone({ id: 1, status: 'things', description: 'A Suite', fullName: 'A Suite' }); reporter.jasmineDone({}); const summary = container.querySelector('.jasmine-summary'); expect(summary.childNodes.length).toEqual(1); const outerSuite = summary.childNodes[0]; expect(outerSuite.childNodes.length).toEqual(4); const classes = []; for (let i = 0; i < outerSuite.childNodes.length; i++) { const node = outerSuite.childNodes[i]; classes.push(node.getAttribute('class')); } expect(classes).toEqual([ 'jasmine-suite-detail jasmine-things', 'jasmine-specs', 'jasmine-suite', 'jasmine-specs' ]); const suiteDetail = outerSuite.childNodes[0]; const suiteLink = suiteDetail.childNodes[0]; expect(suiteLink.innerHTML).toEqual('A Suite'); expect(suiteLink.getAttribute('href')).toEqual('/?foo=bar&spec=A Suite'); const specs = outerSuite.childNodes[1]; const spec = specs.childNodes[0]; expect(spec.getAttribute('class')).toEqual('jasmine-passed'); expect(spec.getAttribute('id')).toEqual('spec-123'); const specLink = spec.childNodes[0]; expect(specLink.innerHTML).toEqual('with a spec'); expect(specLink.getAttribute('href')).toEqual( '/?foo=bar&spec=A Suite with a spec' ); }); it('has an options menu', function() { const container = document.createElement('div'), getContainer = function() { return container; }, reporter = new jasmineUnderTest.HtmlReporter({ env: env, getContainer: getContainer, createElement: function() { return document.createElement.apply(document, arguments); }, createTextNode: function() { return document.createTextNode.apply(document, arguments); } }); reporter.initialize(); reporter.jasmineDone({}); const trigger = container.querySelector( '.jasmine-run-options .jasmine-trigger' ), payload = container.querySelector( '.jasmine-run-options .jasmine-payload' ); expect(payload).not.toHaveClass('jasmine-open'); trigger.onclick(); expect(payload).toHaveClass('jasmine-open'); trigger.onclick(); expect(payload).not.toHaveClass('jasmine-open'); }); describe('when there are global errors', function() { it('displays the exceptions in their own alert bars', function() { const container = document.createElement('div'), getContainer = function() { return container; }, reporter = new jasmineUnderTest.HtmlReporter({ env: env, getContainer: getContainer, createElement: function() { return document.createElement.apply(document, arguments); }, createTextNode: function() { return document.createTextNode.apply(document, arguments); } }); reporter.initialize(); reporter.jasmineStarted({}); reporter.jasmineDone({ failedExpectations: [ { message: 'Global After All Failure', globalErrorType: 'afterAll' }, { message: 'Your JS is borken', globalErrorType: 'load' } ] }); const alertBars = container.querySelectorAll( '.jasmine-alert .jasmine-bar' ); expect(alertBars.length).toEqual(3); expect(alertBars[1].getAttribute('class')).toEqual( 'jasmine-bar jasmine-errored' ); expect(alertBars[1].innerHTML).toMatch( /AfterAll Global After All Failure/ ); expect(alertBars[2].innerHTML).toMatch( /Error during loading: Your JS is borken/ ); expect(alertBars[2].innerHTML).not.toMatch(/line/); }); it('does not display the "AfterAll" prefix for other error types', function() { const container = document.createElement('div'); const getContainer = function() { return container; }; const reporter = new jasmineUnderTest.HtmlReporter({ env: env, getContainer: getContainer, createElement: function() { return document.createElement.apply(document, arguments); }, createTextNode: function() { return document.createTextNode.apply(document, arguments); } }); reporter.initialize(); reporter.jasmineStarted({}); reporter.jasmineDone({ failedExpectations: [ { message: 'load error', globalErrorType: 'load' }, { message: 'lateExpectation error', globalErrorType: 'lateExpectation' }, { message: 'lateError error', globalErrorType: 'lateError' } ] }); const alertBars = container.querySelectorAll( '.jasmine-alert .jasmine-bar' ); expect(alertBars.length).toEqual(4); expect(alertBars[1].textContent).toContain('load error'); expect(alertBars[2].textContent).toContain('lateExpectation error'); expect(alertBars[3].textContent).toContain('lateError error'); for (let bar of alertBars) { expect(bar.textContent).not.toContain('AfterAll'); } }); it('displays file and line information if available', function() { const container = document.createElement('div'), getContainer = function() { return container; }, reporter = new jasmineUnderTest.HtmlReporter({ env: env, getContainer: getContainer, createElement: function() { return document.createElement.apply(document, arguments); }, createTextNode: function() { return document.createTextNode.apply(document, arguments); } }); reporter.initialize(); reporter.jasmineStarted({}); reporter.jasmineDone({ failedExpectations: [ { message: 'Your JS is borken', globalErrorType: 'load', filename: 'some/file.js', lineno: 42 } ] }); const alertBars = container.querySelectorAll( '.jasmine-alert .jasmine-bar' ); expect(alertBars.length).toEqual(2); expect(alertBars[1].innerHTML).toMatch( /Error during loading: Your JS is borken in some\/file.js line 42/ ); }); }); describe('UI for stop on spec failure', function() { it('should be unchecked for full execution', function() { const container = document.createElement('div'), getContainer = function() { return container; }, reporter = new jasmineUnderTest.HtmlReporter({ env: env, getContainer: getContainer, createElement: function() { return document.createElement.apply(document, arguments); }, createTextNode: function() { return document.createTextNode.apply(document, arguments); } }); reporter.initialize(); reporter.jasmineDone({}); const stopOnFailureUI = container.querySelector('.jasmine-fail-fast'); expect(stopOnFailureUI.checked).toBe(false); }); it('should be checked if stopping short', function() { const container = document.createElement('div'), getContainer = function() { return container; }, reporter = new jasmineUnderTest.HtmlReporter({ env: env, getContainer: getContainer, createElement: function() { return document.createElement.apply(document, arguments); }, createTextNode: function() { return document.createTextNode.apply(document, arguments); } }); env.configure({ stopOnSpecFailure: true }); reporter.initialize(); reporter.jasmineDone({}); const stopOnFailureUI = container.querySelector('.jasmine-fail-fast'); expect(stopOnFailureUI.checked).toBe(true); }); it('should navigate and turn the setting on', function() { const container = document.createElement('div'), navigationHandler = jasmine.createSpy('navigate'), getContainer = function() { return container; }, reporter = new jasmineUnderTest.HtmlReporter({ env: env, navigateWithNewParam: navigationHandler, getContainer: getContainer, createElement: function() { return document.createElement.apply(document, arguments); }, createTextNode: function() { return document.createTextNode.apply(document, arguments); } }); reporter.initialize(); reporter.jasmineDone({}); const stopOnFailureUI = container.querySelector('.jasmine-fail-fast'); stopOnFailureUI.click(); expect(navigationHandler).toHaveBeenCalledWith( 'stopOnSpecFailure', true ); }); it('should navigate and turn the setting off', function() { const container = document.createElement('div'), navigationHandler = jasmine.createSpy('navigate'), getContainer = function() { return container; }, reporter = new jasmineUnderTest.HtmlReporter({ env: env, navigateWithNewParam: navigationHandler, getContainer: getContainer, createElement: function() { return document.createElement.apply(document, arguments); }, createTextNode: function() { return document.createTextNode.apply(document, arguments); } }); env.configure({ stopOnSpecFailure: true }); reporter.initialize(); reporter.jasmineDone({}); const stopOnFailureUI = container.querySelector('.jasmine-fail-fast'); stopOnFailureUI.click(); expect(navigationHandler).toHaveBeenCalledWith( 'stopOnSpecFailure', false ); }); }); describe('UI for throwing errors on expectation failures', function() { it('should be unchecked if not throwing', function() { const container = document.createElement('div'), getContainer = function() { return container; }, reporter = new jasmineUnderTest.HtmlReporter({ env: env, getContainer: getContainer, createElement: function() { return document.createElement.apply(document, arguments); }, createTextNode: function() { return document.createTextNode.apply(document, arguments); } }); reporter.initialize(); reporter.jasmineDone({}); const throwingExpectationsUI = container.querySelector( '.jasmine-throw' ); expect(throwingExpectationsUI.checked).toBe(false); }); it('should be checked if throwing', function() { const container = document.createElement('div'), getContainer = function() { return container; }, reporter = new jasmineUnderTest.HtmlReporter({ env: env, getContainer: getContainer, createElement: function() { return document.createElement.apply(document, arguments); }, createTextNode: function() { return document.createTextNode.apply(document, arguments); } }); env.configure({ stopSpecOnExpectationFailure: true }); reporter.initialize(); reporter.jasmineDone({}); const throwingExpectationsUI = container.querySelector( '.jasmine-throw' ); expect(throwingExpectationsUI.checked).toBe(true); }); it('should navigate and change the setting to on', function() { const container = document.createElement('div'), navigateHandler = jasmine.createSpy('navigate'), getContainer = function() { return container; }, reporter = new jasmineUnderTest.HtmlReporter({ env: env, getContainer: getContainer, navigateWithNewParam: navigateHandler, createElement: function() { return document.createElement.apply(document, arguments); }, createTextNode: function() { return document.createTextNode.apply(document, arguments); } }); reporter.initialize(); reporter.jasmineDone({}); const throwingExpectationsUI = container.querySelector( '.jasmine-throw' ); throwingExpectationsUI.click(); expect(navigateHandler).toHaveBeenCalledWith( 'stopSpecOnExpectationFailure', true ); }); it('should navigate and change the setting to off', function() { const container = document.createElement('div'), navigateHandler = jasmine.createSpy('navigate'), getContainer = function() { return container; }, reporter = new jasmineUnderTest.HtmlReporter({ env: env, getContainer: getContainer, navigateWithNewParam: navigateHandler, createElement: function() { return document.createElement.apply(document, arguments); }, createTextNode: function() { return document.createTextNode.apply(document, arguments); } }); env.configure({ stopSpecOnExpectationFailure: true }); reporter.initialize(); reporter.jasmineDone({}); const throwingExpectationsUI = container.querySelector( '.jasmine-throw' ); throwingExpectationsUI.click(); expect(navigateHandler).toHaveBeenCalledWith( 'stopSpecOnExpectationFailure', false ); }); }); describe('UI for hiding disabled specs', function() { it('should be unchecked if not hiding disabled specs', function() { const container = document.createElement('div'), getContainer = function() { return container; }, reporter = new jasmineUnderTest.HtmlReporter({ env: env, getContainer: getContainer, createElement: function() { return document.createElement.apply(document, arguments); }, createTextNode: function() { return document.createTextNode.apply(document, arguments); } }); env.configure({ hideDisabled: false }); reporter.initialize(); reporter.jasmineDone({}); const disabledUI = container.querySelector('.jasmine-disabled'); expect(disabledUI.checked).toBe(false); }); it('should be checked if hiding disabled', function() { const container = document.createElement('div'), getContainer = function() { return container; }, reporter = new jasmineUnderTest.HtmlReporter({ env: env, getContainer: getContainer, createElement: function() { return document.createElement.apply(document, arguments); }, createTextNode: function() { return document.createTextNode.apply(document, arguments); } }); env.configure({ hideDisabled: true }); reporter.initialize(); reporter.jasmineDone({}); const disabledUI = container.querySelector('.jasmine-disabled'); expect(disabledUI.checked).toBe(true); }); it('should not display specs that have been disabled', function() { const container = document.createElement('div'), getContainer = function() { return container; }, reporter = new jasmineUnderTest.HtmlReporter({ env: env, getContainer: getContainer, createElement: function() { return document.createElement.apply(document, arguments); }, createTextNode: function() { return document.createTextNode.apply(document, arguments); } }); env.configure({ hideDisabled: true }); reporter.initialize(); reporter.specDone({ id: 789, status: 'excluded', fullName: 'symbols should have titles', passedExpectations: [], failedExpectations: [] }); const specEl = container.querySelector('.jasmine-symbol-summary li'); expect(specEl.getAttribute('class')).toEqual( 'jasmine-excluded-no-display' ); }); }); describe('UI for running tests in random order', function() { it('should be unchecked if not randomizing', function() { const container = document.createElement('div'), getContainer = function() { return container; }, reporter = new jasmineUnderTest.HtmlReporter({ env: env, getContainer: getContainer, createElement: function() { return document.createElement.apply(document, arguments); }, createTextNode: function() { return document.createTextNode.apply(document, arguments); } }); env.configure({ random: false }); reporter.initialize(); reporter.jasmineDone({}); const randomUI = container.querySelector('.jasmine-random'); expect(randomUI.checked).toBe(false); }); it('should be checked if randomizing', function() { const container = document.createElement('div'), getContainer = function() { return container; }, reporter = new jasmineUnderTest.HtmlReporter({ env: env, getContainer: getContainer, createElement: function() { return document.createElement.apply(document, arguments); }, createTextNode: function() { return document.createTextNode.apply(document, arguments); } }); env.configure({ random: true }); reporter.initialize(); reporter.jasmineDone({}); const randomUI = container.querySelector('.jasmine-random'); expect(randomUI.checked).toBe(true); }); it('should navigate and change the setting to on', function() { const container = document.createElement('div'), navigateHandler = jasmine.createSpy('navigate'), getContainer = function() { return container; }, reporter = new jasmineUnderTest.HtmlReporter({ env: env, getContainer: getContainer, navigateWithNewParam: navigateHandler, createElement: function() { return document.createElement.apply(document, arguments); }, createTextNode: function() { return document.createTextNode.apply(document, arguments); } }); env.configure({ random: false }); reporter.initialize(); reporter.jasmineDone({}); const randomUI = container.querySelector('.jasmine-random'); randomUI.click(); expect(navigateHandler).toHaveBeenCalledWith('random', true); }); it('should navigate and change the setting to off', function() { const container = document.createElement('div'), navigateHandler = jasmine.createSpy('navigate'), getContainer = function() { return container; }, reporter = new jasmineUnderTest.HtmlReporter({ env: env, getContainer: getContainer, navigateWithNewParam: navigateHandler, createElement: function() { return document.createElement.apply(document, arguments); }, createTextNode: function() { return document.createTextNode.apply(document, arguments); } }); env.configure({ random: true }); reporter.initialize(); reporter.jasmineDone({}); const randomUI = container.querySelector('.jasmine-random'); randomUI.click(); expect(navigateHandler).toHaveBeenCalledWith('random', false); }); it('should show the seed bar if randomizing', function() { const container = document.createElement('div'), getContainer = function() { return container; }, reporter = new jasmineUnderTest.HtmlReporter({ env: env, getContainer: getContainer, createElement: function() { return document.createElement.apply(document, arguments); }, createTextNode: function() { return document.createTextNode.apply(document, arguments); } }); reporter.initialize(); reporter.jasmineDone({ order: { random: true, seed: '424242' } }); const seedBar = container.querySelector('.jasmine-seed-bar'); expect(seedBar.textContent).toBe(', randomized with seed 424242'); const seedLink = container.querySelector('.jasmine-seed-bar a'); expect(seedLink.getAttribute('href')).toBe('/?seed=424242'); }); it('should not show the current seed bar if not randomizing', function() { const container = document.createElement('div'), getContainer = function() { return container; }, reporter = new jasmineUnderTest.HtmlReporter({ env: env, getContainer: getContainer, createElement: function() { return document.createElement.apply(document, arguments); }, createTextNode: function() { return document.createTextNode.apply(document, arguments); } }); reporter.initialize(); reporter.jasmineDone({}); const seedBar = container.querySelector('.jasmine-seed-bar'); expect(seedBar).toBeNull(); }); it('should include non-spec query params in the jasmine-skipped link when present', function() { const container = document.createElement('div'), reporter = new jasmineUnderTest.HtmlReporter({ env: env, getContainer: function() { return container; }, createElement: function() { return document.createElement.apply(document, arguments); }, createTextNode: function() { return document.createTextNode.apply(document, arguments); }, addToExistingQueryString: function(key, value) { return '?foo=bar&' + key + '=' + value; } }); reporter.initialize(); reporter.jasmineStarted({ totalSpecsDefined: 1 }); reporter.jasmineDone({ order: { random: true } }); const skippedLink = container.querySelector('.jasmine-skipped a'); expect(skippedLink.getAttribute('href')).toEqual('/?foo=bar&spec='); }); }); describe('and all specs pass', function() { let container; beforeEach(function() { container = document.createElement('div'); const getContainer = function() { return container; }, reporter = new jasmineUnderTest.HtmlReporter({ env: env, getContainer: getContainer, createElement: function() { return document.createElement.apply(document, arguments); }, createTextNode: function() { return document.createTextNode.apply(document, arguments); } }); reporter.initialize(); reporter.jasmineStarted({ totalSpecsDefined: 2 }); reporter.specDone({ id: 123, description: 'with a spec', fullName: 'A Suite with a spec', status: 'passed', passedExpectations: [{ passed: true }], failedExpectations: [] }); reporter.specDone({ id: 124, description: 'with another spec', fullName: 'A Suite inner suite with another spec', status: 'passed', passedExpectations: [{ passed: true }], failedExpectations: [] }); reporter.jasmineDone({}); }); it('reports the specs counts', function() { const alertBars = container.querySelectorAll( '.jasmine-alert .jasmine-bar' ); expect(alertBars.length).toEqual(1); expect(alertBars[0].innerHTML).toMatch(/2 specs, 0 failures/); }); it('reports no failure details', function() { const specFailure = container.querySelector('.jasmine-failures'); expect(specFailure.childNodes.length).toEqual(0); }); it('reports no pending specs', function() { const alertBar = container.querySelector('.jasmine-alert .jasmine-bar'); expect(alertBar.innerHTML).not.toMatch(/pending spec[s]/); }); }); describe('and there are excluded specs', function() { let container, reporter, reporterConfig, specStatus; beforeEach(function() { container = document.createElement('div'); reporterConfig = { env: env, getContainer: function() { return container; }, createElement: function() { return document.createElement.apply(document, arguments); }, createTextNode: function() { return document.createTextNode.apply(document, arguments); } }; specStatus = { id: 123, description: 'with a excluded spec', fullName: 'A Suite with a excluded spec', status: 'excluded', passedExpectations: [], failedExpectations: [] }; }); describe('when the specs are not filtered', function() { beforeEach(function() { reporterConfig.filterSpecs = false; reporter = new jasmineUnderTest.HtmlReporter(reporterConfig); reporter.initialize(); reporter.jasmineStarted({ totalSpecsDefined: 1 }); reporter.specStarted(specStatus); reporter.specDone(specStatus); reporter.jasmineDone({}); }); it('shows the excluded spec in the spec list', function() { const specList = container.querySelector('.jasmine-summary'); expect(specList.innerHTML).toContain('with a excluded spec'); }); }); describe('when the specs are filtered', function() { beforeEach(function() { reporterConfig.filterSpecs = true; reporter = new jasmineUnderTest.HtmlReporter(reporterConfig); reporter.initialize(); reporter.jasmineStarted({ totalSpecsDefined: 1 }); reporter.specStarted(specStatus); reporter.specDone(specStatus); reporter.jasmineDone({}); }); it("doesn't show the excluded spec in the spec list", function() { const specList = container.querySelector('.jasmine-summary'); expect(specList.innerHTML).toEqual(''); }); }); }); describe('and there are pending specs', function() { let container, reporter; beforeEach(function() { container = document.createElement('div'); const getContainer = function() { return container; }; reporter = new jasmineUnderTest.HtmlReporter({ env: env, getContainer: getContainer, createElement: function() { return document.createElement.apply(document, arguments); }, createTextNode: function() { return document.createTextNode.apply(document, arguments); } }); reporter.initialize(); reporter.jasmineStarted({ totalSpecsDefined: 1 }); const specStatus = { id: 123, description: 'with a spec', fullName: 'A Suite with a spec', status: 'pending', passedExpectations: [], failedExpectations: [], pendingReason: 'my custom pending reason' }; reporter.specStarted(specStatus); reporter.specDone(specStatus); reporter.jasmineDone({}); }); it('reports the pending specs count', function() { const alertBar = container.querySelector('.jasmine-alert .jasmine-bar'); expect(alertBar.innerHTML).toMatch( /1 spec, 0 failures, 1 pending spec/ ); }); it('reports no failure details', function() { const specFailure = container.querySelector('.jasmine-failures'); expect(specFailure.childNodes.length).toEqual(0); }); it('displays the custom pending reason', function() { const pendingDetails = container.querySelector( '.jasmine-summary .jasmine-pending' ); expect(pendingDetails.innerHTML).toContain('my custom pending reason'); }); }); describe('and some tests fail', function() { let container, reporter; beforeEach(function() { container = document.createElement('div'); const getContainer = function() { return container; }; reporter = new jasmineUnderTest.HtmlReporter({ env: env, getContainer: getContainer, createElement: function() { return document.createElement.apply(document, arguments); }, createTextNode: function() { return document.createTextNode.apply(document, arguments); }, addToExistingQueryString: function(key, value) { return '?foo=bar&' + key + '=' + value; } }); reporter.initialize(); reporter.jasmineStarted({ totalSpecsDefined: 1 }); reporter.suiteStarted({ id: 1, description: 'A suite' }); reporter.suiteStarted({ id: 2, description: 'inner suite' }); const passingSpecResult = { id: 123, status: 'passed', passedExpectations: [{ passed: true }], failedExpectations: [] }; reporter.specStarted(passingSpecResult); reporter.specDone(passingSpecResult); const failingSpecResult = { id: 124, status: 'failed', description: 'a failing spec', fullName: 'a suite inner suite a failing spec', passedExpectations: [], failedExpectations: [ { message: 'a failure message', stack: 'a stack trace' } ] }; const failingSpecResultWithDebugLogs = { id: 567, status: 'failed', description: 'a failing spec', fullName: 'a suite inner suite a failing spec', passedExpectations: [], failedExpectations: [ { message: 'a failure message', stack: 'a stack trace' } ], debugLogs: [ { timestamp: 123, message: 'msg 1' }, { timestamp: 456, message: 'msg 1' } ] }; const passingSuiteResult = { id: 1, description: 'A suite' }; const failingSuiteResult = { id: 2, description: 'a suite', fullName: 'a suite', status: 'failed', failedExpectations: [{ message: 'My After All Exception' }] }; reporter.specStarted(failingSpecResult); reporter.specDone(failingSpecResult); reporter.suiteDone(passingSuiteResult); reporter.suiteDone(failingSuiteResult); reporter.suiteDone(passingSuiteResult); reporter.specStarted(failingSpecResultWithDebugLogs); reporter.specDone(failingSpecResultWithDebugLogs); reporter.jasmineDone({}); }); it('reports the specs counts', function() { const alertBar = container.querySelector('.jasmine-alert .jasmine-bar'); expect(alertBar.innerHTML).toMatch(/3 specs, 3 failures/); }); it('reports failure messages and stack traces', function() { const specFailures = container.querySelector('.jasmine-failures'); expect(specFailures.childNodes.length).toEqual(3); const specFailure = specFailures.childNodes[0]; expect(specFailure.getAttribute('class')).toMatch(/jasmine-failed/); expect(specFailure.getAttribute('class')).toMatch( /jasmine-spec-detail/ ); const specDiv = specFailure.childNodes[0]; expect(specDiv.getAttribute('class')).toEqual('jasmine-description'); const message = specFailure.childNodes[1].childNodes[0]; expect(message.getAttribute('class')).toEqual('jasmine-result-message'); expect(message.innerHTML).toEqual('a failure message'); const stackTrace = specFailure.childNodes[1].childNodes[1]; expect(stackTrace.getAttribute('class')).toEqual('jasmine-stack-trace'); expect(stackTrace.innerHTML).toEqual('a stack trace'); const suiteFailure = specFailures.childNodes[0]; expect(suiteFailure.getAttribute('class')).toMatch(/jasmine-failed/); expect(suiteFailure.getAttribute('class')).toMatch( /jasmine-spec-detail/ ); const suiteDiv = suiteFailure.childNodes[0]; expect(suiteDiv.getAttribute('class')).toEqual('jasmine-description'); const suiteMessage = suiteFailure.childNodes[1].childNodes[0]; expect(suiteMessage.getAttribute('class')).toEqual( 'jasmine-result-message' ); expect(suiteMessage.innerHTML).toEqual('a failure message'); const suiteStackTrace = suiteFailure.childNodes[1].childNodes[1]; expect(suiteStackTrace.getAttribute('class')).toEqual( 'jasmine-stack-trace' ); expect(suiteStackTrace.innerHTML).toEqual('a stack trace'); }); it('reports traces when present', function() { const specFailure = container.querySelectorAll( '.jasmine-spec-detail.jasmine-failed' )[2], debugLogs = specFailure.querySelector('.jasmine-debug-log table'); expect(debugLogs).toBeTruthy(); const rows = debugLogs.querySelectorAll('tbody tr'); expect(rows.length).toEqual(2); }); it('provides links to focus on a failure and each containing suite', function() { const description = container.querySelector( '.jasmine-failures .jasmine-description' ); const links = description.querySelectorAll('a'); expect(description.textContent).toEqual( 'A suite > inner suite > a failing spec' ); expect(links.length).toEqual(3); expect(links[0].textContent).toEqual('A suite'); expect(links[0].getAttribute('href')).toMatch(/\?foo=bar&spec=A suite/); expect(links[1].textContent).toEqual('inner suite'); expect(links[1].getAttribute('href')).toMatch( /\?foo=bar&spec=A suite inner suite/ ); expect(links[2].textContent).toEqual('a failing spec'); expect(links[2].getAttribute('href')).toMatch( /\?foo=bar&spec=a suite inner suite a failing spec/ ); }); it('allows switching between failure details and the spec summary', function() { const menuBar = container.querySelectorAll('.jasmine-bar')[1]; expect(menuBar.getAttribute('class')).not.toMatch(/hidden/); const link = menuBar.querySelector('a'); expect(link.innerHTML).toEqual('Failures'); expect(link.getAttribute('href')).toEqual('#'); }); it("sets the reporter to 'Failures List' mode", function() { const reporterNode = container.querySelector('.jasmine_html-reporter'); expect(reporterNode.getAttribute('class')).toMatch( 'jasmine-failure-list' ); }); }); it('counts failures that are reported in the jasmineDone event', function() { const container = document.createElement('div'); function getContainer() { return container; } const reporter = new jasmineUnderTest.HtmlReporter({ env: env, getContainer: getContainer, createElement: function() { return document.createElement.apply(document, arguments); }, createTextNode: function() { return document.createTextNode.apply(document, arguments); }, addToExistingQueryString: function(key, value) { return '?' + key + '=' + value; } }); reporter.initialize(); reporter.jasmineStarted({ totalSpecsDefined: 1 }); const failingSpecResult = { id: 124, status: 'failed', description: 'a failing spec', fullName: 'a suite inner suite a failing spec', passedExpectations: [], failedExpectations: [ { message: 'a failure message', stack: 'a stack trace' } ] }; reporter.specStarted(failingSpecResult); reporter.specDone(failingSpecResult); reporter.jasmineDone({ failedExpectations: [ { message: 'a failure message', stack: 'a stack trace' }, { message: 'a failure message', stack: 'a stack trace' } ] }); const alertBar = container.querySelector('.jasmine-alert .jasmine-bar'); expect(alertBar.innerHTML).toMatch(/1 spec, 3 failures/); }); }); describe('The overall result bar', function() { describe("When the jasmineDone event's overallStatus is 'passed'", function() { it('has class jasmine-passed', function() { const container = document.createElement('div'), getContainer = function() { return container; }, reporter = new jasmineUnderTest.HtmlReporter({ env: env, getContainer: getContainer, createElement: function() { return document.createElement.apply(document, arguments); }, createTextNode: function() { return document.createTextNode.apply(document, arguments); } }); reporter.initialize(); reporter.jasmineStarted({}); reporter.jasmineDone({ overallStatus: 'passed', failedExpectations: [] }); const alertBar = container.querySelector('.jasmine-overall-result'); expect(alertBar).toHaveClass('jasmine-passed'); }); }); describe("When the jasmineDone event's overallStatus is 'failed'", function() { it('has class jasmine-failed', function() { const container = document.createElement('div'), getContainer = function() { return container; }, reporter = new jasmineUnderTest.HtmlReporter({ env: env, getContainer: getContainer, createElement: function() { return document.createElement.apply(document, arguments); }, createTextNode: function() { return document.createTextNode.apply(document, arguments); } }); reporter.initialize(); reporter.jasmineStarted({}); reporter.jasmineDone({ overallStatus: 'failed', failedExpectations: [] }); const alertBar = container.querySelector('.jasmine-overall-result'); expect(alertBar).toHaveClass('jasmine-failed'); }); }); describe("When the jasmineDone event's overallStatus is 'incomplete'", function() { it('has class jasmine-incomplete', function() { const container = document.createElement('div'), getContainer = function() { return container; }, reporter = new jasmineUnderTest.HtmlReporter({ env: env, getContainer: getContainer, createElement: function() { return document.createElement.apply(document, arguments); }, createTextNode: function() { return document.createTextNode.apply(document, arguments); } }); reporter.initialize(); reporter.jasmineStarted({}); reporter.jasmineDone({ overallStatus: 'incomplete', incompleteReason: 'because nope', failedExpectations: [] }); const alertBar = container.querySelector('.jasmine-overall-result'); expect(alertBar).toHaveClass('jasmine-incomplete'); expect(alertBar.textContent).toContain('Incomplete: because nope'); }); }); }); }); jasmine-4.5.0/spec/html/HtmlSpecFilterSpec.js000066400000000000000000000011201432731766000211050ustar00rootroot00000000000000describe('jasmineUnderTest.HtmlSpecFilter', function() { it('should match when no string is provided', function() { const specFilter = new jasmineUnderTest.HtmlSpecFilter(); expect(specFilter.matches('foo')).toBe(true); expect(specFilter.matches('*bar')).toBe(true); }); it('should only match the provided string', function() { const specFilter = new jasmineUnderTest.HtmlSpecFilter({ filterString: function() { return 'foo'; } }); expect(specFilter.matches('foo')).toBe(true); expect(specFilter.matches('bar')).toBe(false); }); }); jasmine-4.5.0/spec/html/MatchersHtmlSpec.js000066400000000000000000000016131432731766000206220ustar00rootroot00000000000000describe('MatchersSpec - HTML Dependent', function() { let env, spec; beforeEach(function() { env = new jasmineUnderTest.Env(); env.describe('suite', function() { spec = env.it('spec', function() {}); }); spyOn(spec, 'addExpectationResult'); addMatchers({ toPass: function() { return lastResult().passed; }, toFail: function() { return !lastResult().passed; } }); }); afterEach(function() { env.cleanup_(); }); function match(value) { return spec.expect(value); } function lastResult() { return spec.addExpectationResult.mostRecentCall.args[1]; } xit('toEqual with DOM nodes', function() { const nodeA = document.createElement('div'); const nodeB = document.createElement('div'); expect(match(nodeA).toEqual(nodeA)).toPass(); expect(match(nodeA).toEqual(nodeB)).toFail(); }); }); jasmine-4.5.0/spec/html/PrettyPrintHtmlSpec.js000066400000000000000000000033111432731766000213550ustar00rootroot00000000000000describe('PrettyPrinter (HTML Dependent)', function() { it('should stringify non-element HTML nodes properly', function() { const sampleNode = document.createTextNode(''); const pp = jasmineUnderTest.makePrettyPrinter(); expect(pp(sampleNode)).toEqual('HTMLNode'); expect(pp({ foo: sampleNode })).toEqual('Object({ foo: HTMLNode })'); }); it('should stringify empty HTML elements as their opening tags', function() { const simple = document.createElement('div'); const pp = jasmineUnderTest.makePrettyPrinter(); simple.className = 'foo'; expect(pp(simple)).toEqual('
'); }); it('should stringify non-empty HTML elements as tags with placeholders', function() { const pp = jasmineUnderTest.makePrettyPrinter(); const nonEmpty = document.createElement('div'); nonEmpty.className = 'foo'; nonEmpty.innerHTML = '

Irrelevant

'; expect(pp(nonEmpty)).toEqual('
...
'); }); it("should print Firefox's wrapped native objects correctly", function() { if (jasmine.getEnv().firefoxVersion) { const pp = jasmineUnderTest.makePrettyPrinter(); let err; try { new CustomEvent(); } catch (e) { err = e; } // Different versions of FF produce different error messages. expect(pp(err)).toMatch( /Not enough arguments|CustomEvent.*only 0.*passed/ ); } }); it('should stringify HTML element with text and attributes', function() { const pp = jasmineUnderTest.makePrettyPrinter(); const el = document.createElement('div'); el.setAttribute('things', 'foo'); el.innerHTML = 'foo'; expect(pp(el)).toEqual('
...
'); }); }); jasmine-4.5.0/spec/html/QueryStringSpec.js000066400000000000000000000043561432731766000205320ustar00rootroot00000000000000describe('QueryString', function() { describe('#navigateWithNewParam', function() { it('sets the query string to include the given key/value pair', function() { const windowLocation = { search: '' }, queryString = new jasmineUnderTest.QueryString({ getWindowLocation: function() { return windowLocation; } }); queryString.navigateWithNewParam('foo', 'bar baz'); expect(windowLocation.search).toMatch(/foo=bar%20baz/); }); it('leaves existing params alone', function() { const windowLocation = { search: '?foo=bar' }, queryString = new jasmineUnderTest.QueryString({ getWindowLocation: function() { return windowLocation; } }); queryString.navigateWithNewParam('baz', 'quux'); expect(windowLocation.search).toMatch(/foo=bar/); expect(windowLocation.search).toMatch(/baz=quux/); }); }); describe('#fullStringWithNewParam', function() { it('gets the query string including the given key/value pair', function() { const windowLocation = { search: '?foo=bar' }, queryString = new jasmineUnderTest.QueryString({ getWindowLocation: function() { return windowLocation; } }); const result = queryString.fullStringWithNewParam('baz', 'quux'); expect(result).toMatch(/foo=bar/); expect(result).toMatch(/baz=quux/); }); }); describe('#getParam', function() { it('returns the value of the requested key', function() { const windowLocation = { search: '?baz=quux%20corge' }, queryString = new jasmineUnderTest.QueryString({ getWindowLocation: function() { return windowLocation; } }); expect(queryString.getParam('baz')).toEqual('quux corge'); }); it('returns null if the key is not present', function() { const windowLocation = { search: '' }, queryString = new jasmineUnderTest.QueryString({ getWindowLocation: function() { return windowLocation; } }); expect(queryString.getParam('baz')).toBeFalsy(); }); }); }); jasmine-4.5.0/spec/html/ResultsNodeSpec.js000066400000000000000000000030651432731766000205010ustar00rootroot00000000000000describe('ResultsNode', function() { it('wraps a result', function() { const fakeResult = { id: 123, message: 'foo' }, node = new jasmineUnderTest.ResultsNode(fakeResult, 'suite', null); expect(node.result).toBe(fakeResult); expect(node.type).toEqual('suite'); }); it('can add children with a type', function() { const fakeResult = { id: 123, message: 'foo' }, fakeChildResult = { id: 456, message: 'bar' }, node = new jasmineUnderTest.ResultsNode(fakeResult, 'suite', null); node.addChild(fakeChildResult, 'spec'); expect(node.children.length).toEqual(1); expect(node.children[0].result).toEqual(fakeChildResult); expect(node.children[0].type).toEqual('spec'); }); it('has a pointer back to its parent ResultNode', function() { const fakeResult = { id: 123, message: 'foo' }, fakeChildResult = { id: 456, message: 'bar' }, node = new jasmineUnderTest.ResultsNode(fakeResult, 'suite', null); node.addChild(fakeChildResult, 'spec'); expect(node.children[0].parent).toBe(node); }); it('can provide the most recent child', function() { const fakeResult = { id: 123, message: 'foo' }, fakeChildResult = { id: 456, message: 'bar' }, node = new jasmineUnderTest.ResultsNode(fakeResult, 'suite', null); node.addChild(fakeChildResult, 'spec'); expect(node.last()).toBe(node.children[node.children.length - 1]); }); }); jasmine-4.5.0/spec/html/SpyRegistryHtmlSpec.js000066400000000000000000000012471432731766000213630ustar00rootroot00000000000000describe('Spy Registry browser-specific behavior', function() { function createSpy(name, originalFn) { return jasmineUnderTest.Spy(name, originalFn); } it('can spy on and unspy window.onerror', function() { const spies = [], spyRegistry = new jasmineUnderTest.SpyRegistry({ currentSpies: function() { return spies; }, createSpy: createSpy, global: window }), originalHandler = window.onerror; try { spyRegistry.spyOn(window, 'onerror'); spyRegistry.clearSpies(); expect(window.onerror).toBe(originalHandler); } finally { window.onerror = originalHandler; } }); }); jasmine-4.5.0/spec/javascripts/000077500000000000000000000000001432731766000164425ustar00rootroot00000000000000jasmine-4.5.0/spec/javascripts/support/000077500000000000000000000000001432731766000201565ustar00rootroot00000000000000jasmine-4.5.0/spec/javascripts/support/jasmine_selenium_runner.yml000066400000000000000000000011171432731766000256210ustar00rootroot00000000000000--- use_sauce: <%= ENV['USE_SAUCE'] %> browser: <%= ENV['JASMINE_BROWSER'] %> sauce: sauce_connect_path: <%= ENV['SAUCE_CONNECT_PATH'].inspect %> name: jasmine-core <%= Time.now.to_s %> username: <%= ENV['SAUCE_USERNAME'] %> access_key: <%= ENV['SAUCE_ACCESS_KEY'] %> build: Core <%= ENV['TRAVIS_BUILD_NUMBER'] || 'Ran locally' %> tags: - Jasmine-Core - "<%= ENV['TRAVIS_JOB_NUMBER'] %>" tunnel_identifier: <%= ENV['TRAVIS_JOB_NUMBER'] ? %Q("#{ENV['TRAVIS_JOB_NUMBER']}") : nil %> os: <%= ENV['SAUCE_OS'] %> browser_version: "<%= ENV['SAUCE_BROWSER_VERSION'] %>" jasmine-4.5.0/spec/npmPackage/000077500000000000000000000000001432731766000161575ustar00rootroot00000000000000jasmine-4.5.0/spec/npmPackage/npmPackageSpec.js000066400000000000000000000106631432731766000214040ustar00rootroot00000000000000describe('npm package', function() { const path = require('path'), temp = require('temp').track(), fs = require('fs'); beforeAll(function() { const shell = require('shelljs'), pack = shell.exec('npm pack', { silent: true }); this.tarball = pack.stdout.split('\n')[0]; this.tmpDir = temp.mkdirSync(); // automatically deleted on exit const untar = shell.exec( 'tar -xzf ' + this.tarball + ' -C ' + this.tmpDir, { silent: true } ); expect(untar.code).toBe(0); this.packagedCore = require(path.join( this.tmpDir, 'package/lib/jasmine-core.js' )); }); beforeEach(function() { jasmine.addMatchers({ toExistInPath: function() { return { compare: function(actual, expected) { const fullPath = path.resolve(expected, actual); return { pass: fs.existsSync(fullPath) }; } }; } }); }); afterAll(function() { fs.unlinkSync(this.tarball); }); it('has a root path', function() { expect(this.packagedCore.files.path).toEqual( fs.realpathSync(path.resolve(this.tmpDir, 'package/lib/jasmine-core')) ); }); it('has a bootDir', function() { expect(this.packagedCore.files.bootDir).toEqual( fs.realpathSync(path.resolve(this.tmpDir, 'package/lib/jasmine-core')) ); }); it('has jsFiles', function() { expect(this.packagedCore.files.jsFiles).toEqual([ 'jasmine.js', 'jasmine-html.js' ]); const packagedCore = this.packagedCore; this.packagedCore.files.jsFiles.forEach(function(fileName) { expect(fileName).toExistInPath(packagedCore.files.path); }); }); it('has cssFiles', function() { expect(this.packagedCore.files.cssFiles).toEqual(['jasmine.css']); const packagedCore = this.packagedCore; this.packagedCore.files.cssFiles.forEach(function(fileName) { expect(fileName).toExistInPath(packagedCore.files.path); }); }); it('has bootFiles', function() { expect(this.packagedCore.files.bootFiles).toEqual(['boot0.js', 'boot1.js']); expect(this.packagedCore.files.nodeBootFiles).toEqual(['node_boot.js']); for (const fileName of this.packagedCore.files.bootFiles) { expect(fileName).toExistInPath(this.packagedCore.files.bootDir); } for (const fileName of this.packagedCore.files.nodeBootFiles) { expect(fileName).toExistInPath(this.packagedCore.files.bootDir); } }); it('has an imagesDir', function() { expect(this.packagedCore.files.imagesDir).toEqual( fs.realpathSync(path.resolve(this.tmpDir, 'package/images')) ); const images = fs.readdirSync(path.resolve(this.tmpDir, 'package/images')); expect(images).toContain('jasmine-horizontal.png'); expect(images).toContain('jasmine-horizontal.svg'); expect(images).toContain('jasmine_favicon.png'); }); it('does not have CI config files and scripts', function() { expect(fs.existsSync(path.resolve(this.tmpDir, 'package/.circleci'))).toBe( false ); expect(fs.existsSync(path.resolve(this.tmpDir, 'package/scripts'))).toBe( false ); }); it('does not have any unexpected files in the root directory', function() { const files = fs.readdirSync(this.tmpDir); expect(files).toEqual(['package']); }); it('does not have any unexpected files in the package directory', function() { const files = fs.readdirSync(path.resolve(this.tmpDir, 'package')); files.sort(); expect(files).toEqual([ 'MIT.LICENSE', 'README.md', 'images', 'lib', 'package.json' ]); }); it('only has images in the images dir', function() { const files = fs.readdirSync(path.resolve(this.tmpDir, 'package/images')); for (let i = 0; i < files.length; i++) { expect(files[i]).toMatch(/\.(svg|png)$/); } }); it('only has JS and CSS files in the lib dir', function() { const files = []; function getFiles(dir) { const dirents = fs.readdirSync(dir, { withFileTypes: true }); for (let j = 0; j < dirents.length; j++) { const dirent = dirents[j]; if (dirent.isDirectory()) { getFiles(path.resolve(dir, dirent.name)); } else { files.push(path.resolve(dir, dirent.name)); } } } getFiles(path.resolve(this.tmpDir, 'package/lib')); for (let i = 0; i < files.length; i++) { expect(files[i]).toMatch(/\.(js|css)$/); } }); }); jasmine-4.5.0/spec/performance/000077500000000000000000000000001432731766000164125ustar00rootroot00000000000000jasmine-4.5.0/spec/performance/large_object_test.js000066400000000000000000000013721432731766000224320ustar00rootroot00000000000000describe('Printing a big object', function() { function rand(upper) { return Math.round(upper * Math.random()); } function generateObject(level) { const object = {}; for (let i = 0; i < 50; i++) { const decide = rand(2); switch (decide) { case 0: object['cycle' + i] = object; break; case 1: object['number' + i] = rand(100); break; case 2: if (level < 3) { object['nesting' + i] = generateObject(level + 1); } break; } } return object; } it('takes a reasonable amount of time', function() { const bigObject = generateObject(0); expect(jasmineUnderTest.pp(bigObject)).toMatch(/cycle/); }); }); jasmine-4.5.0/spec/performance/performance_test.js000066400000000000000000000003461432731766000223130ustar00rootroot00000000000000describe('performance', function() { for (let i = 0; i < 10000; i++) { it('should pass', function() { expect(true).toBe(true); }); it('should fail', function() { expect(true).toBe(false); }); } }); jasmine-4.5.0/spec/support/000077500000000000000000000000001432731766000156255ustar00rootroot00000000000000jasmine-4.5.0/spec/support/ci.js000066400000000000000000000006241432731766000165600ustar00rootroot00000000000000/* eslint-env node, es6 */ const path = require('path'), jasmineBrowser = require('jasmine-browser-runner'), jasmineCore = require('../../lib/jasmine-core'); const config = require(path.resolve('spec/support/jasmine-browser.js')); config.clearReporters = true; config.jasmineCore = jasmineCore; jasmineBrowser.runSpecs(config).catch(function(error) { console.error(error); process.exit(1); }); jasmine-4.5.0/spec/support/jasmine-browser-performance.json000066400000000000000000000002651432731766000241310ustar00rootroot00000000000000{ "srcDir": "src", "specDir": "spec", "specFiles": [ "performance/performance_test.js" ], "helpers": [ "helpers/defineJasmineUnderTest.js" ], "random": true } jasmine-4.5.0/spec/support/jasmine-browser.js000066400000000000000000000022311432731766000212700ustar00rootroot00000000000000/* eslint-env node, es6 */ module.exports = { srcDir: 'src', srcFiles: [ 'core/requireCore.js', 'core/base.js', 'core/util.js', 'core/Spec.js', 'core/Env.js', 'core/JsApiReporter.js', 'core/PrettyPrinter.js', 'core/Suite.js', 'core/**/*.js', 'html/**/*.js', '**/*.js', '!boot/**.js' ], specDir: 'spec', specFiles: ['**/*[Ss]pec.js', '!npmPackage/**/*'], helpers: [ 'helpers/generator.js', 'helpers/BrowserFlags.js', 'helpers/domHelpers.js', 'helpers/integrationMatchers.js', 'helpers/defineJasmineUnderTest.js', 'helpers/resetEnv.js' ], random: true, browser: { name: process.env.JASMINE_BROWSER || 'firefox', useSauce: process.env.USE_SAUCE === 'true', sauce: { name: `jasmine-core ${new Date().toISOString()}`, os: process.env.SAUCE_OS, browserVersion: process.env.SAUCE_BROWSER_VERSION, build: `Core ${process.env.TRAVIS_BUILD_NUMBER || 'Ran locally'}`, tags: ['Jasmine-Core'], tunnelIdentifier: process.env.SAUCE_TUNNEL_IDENTIFIER, username: process.env.SAUCE_USERNAME, accessKey: process.env.SAUCE_ACCESS_KEY } } }; jasmine-4.5.0/spec/support/jasmine-performance.json000066400000000000000000000002551432731766000224470ustar00rootroot00000000000000{ "spec_dir": "spec", "spec_files": [ "performance/performance_test.js" ], "helper_files": [ "helpers/nodeDefineJasmineUnderTest.js" ], "random": true } jasmine-4.5.0/spec/support/jasmine-performance.yml000066400000000000000000000002311432731766000222710ustar00rootroot00000000000000src_dir: - 'src' src_files: - '**/*.js' helpers: - 'helpers/**/*.js' spec_files: - 'performance/performance_test.js' spec_dir: spec random: true jasmine-4.5.0/spec/support/jasmine.json000066400000000000000000000005131432731766000201450ustar00rootroot00000000000000{ "spec_dir": "spec", "spec_files": [ "core/**/*[Ss]pec.js", "npmPackage/**/*[Ss]pec.js" ], "helpers": [ "helpers/domHelpers.js", "helpers/integrationMatchers.js", "helpers/overrideConsoleLogForCircleCi.js", "helpers/nodeDefineJasmineUnderTest.js", "helpers/resetEnv.js" ], "random": true } jasmine-4.5.0/spec/support/localJasmineBrowser.js000066400000000000000000000005561432731766000221360ustar00rootroot00000000000000const path = require('path'), jasmineBrowser = require('jasmine-browser-runner'), jasmineCore = require('../../lib/jasmine-core.js'); const configFile = process.argv[2] || 'jasmine-browser.js'; const config = require(path.resolve('spec/support', configFile)); config.jasmineCore = jasmineCore; config.batchReporter = true; jasmineBrowser.startServer(config); jasmine-4.5.0/src/000077500000000000000000000000001432731766000137465ustar00rootroot00000000000000jasmine-4.5.0/src/boot/000077500000000000000000000000001432731766000147115ustar00rootroot00000000000000jasmine-4.5.0/src/boot/boot0.js000066400000000000000000000031521432731766000162730ustar00rootroot00000000000000/** This file starts the process of "booting" Jasmine. It initializes Jasmine, makes its globals available, and creates the env. This file should be loaded after `jasmine.js` and `jasmine_html.js`, but before `boot1.js` or any project source files or spec files are loaded. */ (function() { const jasmineRequire = window.jasmineRequire || require('./jasmine.js'); /** * ## Require & Instantiate * * Require Jasmine's core files. Specifically, this requires and attaches all of Jasmine's code to the `jasmine` reference. */ const jasmine = jasmineRequire.core(jasmineRequire), global = jasmine.getGlobal(); global.jasmine = jasmine; /** * Since this is being run in a browser and the results should populate to an HTML page, require the HTML-specific Jasmine code, injecting the same reference. */ jasmineRequire.html(jasmine); /** * Create the Jasmine environment. This is used to run all specs in a project. */ const env = jasmine.getEnv(); /** * ## The Global Interface * * Build up the functions that will be exposed as the Jasmine public interface. A project can customize, rename or alias any of these functions as desired, provided the implementation remains unchanged. */ const jasmineInterface = jasmineRequire.interface(jasmine, env); /** * Add all of the Jasmine global/public interface to the global scope, so a project can use the public interface directly. For example, calling `describe` in specs instead of `jasmine.getEnv().describe`. */ for (const property in jasmineInterface) { global[property] = jasmineInterface[property]; } })(); jasmine-4.5.0/src/boot/boot1.js000066400000000000000000000066071432731766000163040ustar00rootroot00000000000000/** This file finishes 'booting' Jasmine, performing all of the necessary initialization before executing the loaded environment and all of a project's specs. This file should be loaded after `boot0.js` but before any project source files or spec files are loaded. Thus this file can also be used to customize Jasmine for a project. If a project is using Jasmine via the standalone distribution, this file can be customized directly. If you only wish to configure the Jasmine env, you can load another file that calls `jasmine.getEnv().configure({...})` after `boot0.js` is loaded and before this file is loaded. */ (function() { const env = jasmine.getEnv(); /** * ## Runner Parameters * * More browser specific code - wrap the query string in an object and to allow for getting/setting parameters from the runner user interface. */ const queryString = new jasmine.QueryString({ getWindowLocation: function() { return window.location; } }); const filterSpecs = !!queryString.getParam('spec'); const config = { stopOnSpecFailure: queryString.getParam('stopOnSpecFailure'), stopSpecOnExpectationFailure: queryString.getParam( 'stopSpecOnExpectationFailure' ), hideDisabled: queryString.getParam('hideDisabled') }; const random = queryString.getParam('random'); if (random !== undefined && random !== '') { config.random = random; } const seed = queryString.getParam('seed'); if (seed) { config.seed = seed; } /** * ## Reporters * The `HtmlReporter` builds all of the HTML UI for the runner page. This reporter paints the dots, stars, and x's for specs, as well as all spec names and all failures (if any). */ const htmlReporter = new jasmine.HtmlReporter({ env: env, navigateWithNewParam: function(key, value) { return queryString.navigateWithNewParam(key, value); }, addToExistingQueryString: function(key, value) { return queryString.fullStringWithNewParam(key, value); }, getContainer: function() { return document.body; }, createElement: function() { return document.createElement.apply(document, arguments); }, createTextNode: function() { return document.createTextNode.apply(document, arguments); }, timer: new jasmine.Timer(), filterSpecs: filterSpecs }); /** * The `jsApiReporter` also receives spec results, and is used by any environment that needs to extract the results from JavaScript. */ env.addReporter(jsApiReporter); env.addReporter(htmlReporter); /** * Filter which specs will be run by matching the start of the full name against the `spec` query param. */ const specFilter = new jasmine.HtmlSpecFilter({ filterString: function() { return queryString.getParam('spec'); } }); config.specFilter = function(spec) { return specFilter.matches(spec.getFullName()); }; env.configure(config); /** * ## Execution * * Replace the browser window's `onload`, ensure it's called, and then run all of the loaded specs. This includes initializing the `HtmlReporter` instance and then executing the loaded Jasmine environment. All of this will happen after all of the specs are loaded. */ const currentWindowOnload = window.onload; window.onload = function() { if (currentWindowOnload) { currentWindowOnload(); } htmlReporter.initialize(); env.execute(); }; })(); jasmine-4.5.0/src/boot/node_boot.js000066400000000000000000000006621432731766000172230ustar00rootroot00000000000000module.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; }; jasmine-4.5.0/src/core/000077500000000000000000000000001432731766000146765ustar00rootroot00000000000000jasmine-4.5.0/src/core/CallTracker.js000066400000000000000000000056321432731766000174310ustar00rootroot00000000000000getJasmineRequireObj().CallTracker = function(j$) { /** * @namespace Spy#calls * @since 2.0.0 */ function CallTracker() { let calls = []; const opts = {}; this.track = function(context) { if (opts.cloneArgs) { context.args = j$.util.cloneArgs(context.args); } calls.push(context); }; /** * Check whether this spy has been invoked. * @name Spy#calls#any * @since 2.0.0 * @function * @return {Boolean} */ this.any = function() { return !!calls.length; }; /** * Get the number of invocations of this spy. * @name Spy#calls#count * @since 2.0.0 * @function * @return {Integer} */ this.count = function() { return calls.length; }; /** * Get the arguments that were passed to a specific invocation of this spy. * @name Spy#calls#argsFor * @since 2.0.0 * @function * @param {Integer} index The 0-based invocation index. * @return {Array} */ this.argsFor = function(index) { const call = calls[index]; return call ? call.args : []; }; /** * Get the "this" object that was passed to a specific invocation of this spy. * @name Spy#calls#thisFor * @since 3.8.0 * @function * @param {Integer} index The 0-based invocation index. * @return {Object?} */ this.thisFor = function(index) { const call = calls[index]; return call ? call.object : undefined; }; /** * Get the raw calls array for this spy. * @name Spy#calls#all * @since 2.0.0 * @function * @return {Spy.callData[]} */ this.all = function() { return calls; }; /** * Get all of the arguments for each invocation of this spy in the order they were received. * @name Spy#calls#allArgs * @since 2.0.0 * @function * @return {Array} */ this.allArgs = function() { return calls.map(c => c.args); }; /** * Get the first invocation of this spy. * @name Spy#calls#first * @since 2.0.0 * @function * @return {ObjecSpy.callData} */ this.first = function() { return calls[0]; }; /** * Get the most recent invocation of this spy. * @name Spy#calls#mostRecent * @since 2.0.0 * @function * @return {ObjecSpy.callData} */ this.mostRecent = function() { return calls[calls.length - 1]; }; /** * Reset this spy as if it has never been called. * @name Spy#calls#reset * @since 2.0.0 * @function */ this.reset = function() { calls = []; }; /** * Set this spy to do a shallow clone of arguments passed to each invocation. * @name Spy#calls#saveArgumentsByValue * @since 2.5.0 * @function */ this.saveArgumentsByValue = function() { opts.cloneArgs = true; }; } return CallTracker; }; jasmine-4.5.0/src/core/ClearStack.js000066400000000000000000000047251432731766000172600ustar00rootroot00000000000000getJasmineRequireObj().clearStack = function(j$) { const maxInlineCallCount = 10; function browserQueueMicrotaskImpl(global) { const { setTimeout, queueMicrotask } = global; let currentCallCount = 0; return function clearStack(fn) { currentCallCount++; if (currentCallCount < maxInlineCallCount) { queueMicrotask(fn); } else { currentCallCount = 0; setTimeout(fn); } }; } function nodeQueueMicrotaskImpl(global) { const { queueMicrotask } = global; return function(fn) { queueMicrotask(fn); }; } function messageChannelImpl(global) { const { MessageChannel, setTimeout } = global; const channel = new MessageChannel(); let head = {}; let tail = head; let taskRunning = false; channel.port1.onmessage = function() { head = head.next; const task = head.task; delete head.task; if (taskRunning) { setTimeout(task, 0); } else { try { taskRunning = true; task(); } finally { taskRunning = false; } } }; let currentCallCount = 0; return function clearStack(fn) { currentCallCount++; if (currentCallCount < maxInlineCallCount) { tail = tail.next = { task: fn }; channel.port2.postMessage(0); } else { currentCallCount = 0; setTimeout(fn); } }; } function getClearStack(global) { const NODE_JS = global.process && global.process.versions && typeof global.process.versions.node === 'string'; const SAFARI = global.navigator && /^((?!chrome|android).)*safari/i.test(global.navigator.userAgent); if (NODE_JS) { // Unlike browsers, Node doesn't require us to do a periodic setTimeout // so we avoid the overhead. return nodeQueueMicrotaskImpl(global); } else if ( SAFARI || j$.util.isUndefined(global.MessageChannel) /* tests */ ) { // queueMicrotask is dramatically faster than MessageChannel in Safari, // at least through version 16. // Some of our own integration tests provide a mock queueMicrotask in all // environments because it's simpler to mock than MessageChannel. return browserQueueMicrotaskImpl(global); } else { // MessageChannel is faster than queueMicrotask in supported browsers // other than Safari. return messageChannelImpl(global); } } return getClearStack; }; jasmine-4.5.0/src/core/Clock.js000066400000000000000000000132671432731766000163000ustar00rootroot00000000000000getJasmineRequireObj().Clock = function() { /* global process */ const NODE_JS = typeof process !== 'undefined' && process.versions && typeof process.versions.node === 'string'; /** * @class Clock * @since 1.3.0 * @classdesc Jasmine's mock clock is used when testing time dependent code.
* _Note:_ Do not construct this directly. You can get the current clock with * {@link jasmine.clock}. * @hideconstructor */ function Clock(global, delayedFunctionSchedulerFactory, mockDate) { const realTimingFunctions = { setTimeout: global.setTimeout, clearTimeout: global.clearTimeout, setInterval: global.setInterval, clearInterval: global.clearInterval }; const fakeTimingFunctions = { setTimeout: setTimeout, clearTimeout: clearTimeout, setInterval: setInterval, clearInterval: clearInterval }; let installed = false; let delayedFunctionScheduler; let timer; this.FakeTimeout = FakeTimeout; /** * Install the mock clock over the built-in methods. * @name Clock#install * @since 2.0.0 * @function * @return {Clock} */ this.install = function() { if (!originalTimingFunctionsIntact()) { throw new Error( 'Jasmine Clock was unable to install over custom global timer functions. Is the clock already installed?' ); } replace(global, fakeTimingFunctions); timer = fakeTimingFunctions; delayedFunctionScheduler = delayedFunctionSchedulerFactory(); installed = true; return this; }; /** * Uninstall the mock clock, returning the built-in methods to their places. * @name Clock#uninstall * @since 2.0.0 * @function */ this.uninstall = function() { delayedFunctionScheduler = null; mockDate.uninstall(); replace(global, realTimingFunctions); timer = realTimingFunctions; installed = false; }; /** * Execute a function with a mocked Clock * * The clock will be {@link Clock#install|install}ed before the function is called and {@link Clock#uninstall|uninstall}ed in a `finally` after the function completes. * @name Clock#withMock * @since 2.3.0 * @function * @param {Function} closure The function to be called. */ this.withMock = function(closure) { this.install(); try { closure(); } finally { this.uninstall(); } }; /** * Instruct the installed Clock to also mock the date returned by `new Date()` * @name Clock#mockDate * @since 2.1.0 * @function * @param {Date} [initialDate=now] The `Date` to provide. */ this.mockDate = function(initialDate) { mockDate.install(initialDate); }; this.setTimeout = function(fn, delay, params) { return Function.prototype.apply.apply(timer.setTimeout, [ global, arguments ]); }; this.setInterval = function(fn, delay, params) { return Function.prototype.apply.apply(timer.setInterval, [ global, arguments ]); }; this.clearTimeout = function(id) { return Function.prototype.call.apply(timer.clearTimeout, [global, id]); }; this.clearInterval = function(id) { return Function.prototype.call.apply(timer.clearInterval, [global, id]); }; /** * Tick the Clock forward, running any enqueued timeouts along the way * @name Clock#tick * @since 1.3.0 * @function * @param {int} millis The number of milliseconds to tick. */ this.tick = function(millis) { if (installed) { delayedFunctionScheduler.tick(millis, function(millis) { mockDate.tick(millis); }); } else { throw new Error( 'Mock clock is not installed, use jasmine.clock().install()' ); } }; return this; function originalTimingFunctionsIntact() { return ( global.setTimeout === realTimingFunctions.setTimeout && global.clearTimeout === realTimingFunctions.clearTimeout && global.setInterval === realTimingFunctions.setInterval && global.clearInterval === realTimingFunctions.clearInterval ); } function replace(dest, source) { for (const prop in source) { dest[prop] = source[prop]; } } function setTimeout(fn, delay) { if (!NODE_JS) { return delayedFunctionScheduler.scheduleFunction( fn, delay, argSlice(arguments, 2) ); } const timeout = new FakeTimeout(); delayedFunctionScheduler.scheduleFunction( fn, delay, argSlice(arguments, 2), false, timeout ); return timeout; } function clearTimeout(id) { return delayedFunctionScheduler.removeFunctionWithId(id); } function setInterval(fn, interval) { if (!NODE_JS) { return delayedFunctionScheduler.scheduleFunction( fn, interval, argSlice(arguments, 2), true ); } const timeout = new FakeTimeout(); delayedFunctionScheduler.scheduleFunction( fn, interval, argSlice(arguments, 2), true, timeout ); return timeout; } function clearInterval(id) { return delayedFunctionScheduler.removeFunctionWithId(id); } function argSlice(argsObj, n) { return Array.prototype.slice.call(argsObj, n); } } /** * Mocks Node.js Timeout class */ function FakeTimeout() {} FakeTimeout.prototype.ref = function() { return this; }; FakeTimeout.prototype.unref = function() { return this; }; return Clock; }; jasmine-4.5.0/src/core/CompleteOnFirstErrorSkipPolicy.js000066400000000000000000000025201432731766000233310ustar00rootroot00000000000000getJasmineRequireObj().CompleteOnFirstErrorSkipPolicy = function(j$) { function CompleteOnFirstErrorSkipPolicy(queueableFns) { this.queueableFns_ = queueableFns; this.erroredFnIx_ = null; } CompleteOnFirstErrorSkipPolicy.prototype.skipTo = function(lastRanFnIx) { let i; for ( i = lastRanFnIx + 1; i < this.queueableFns_.length && this.shouldSkip_(i); i++ ) {} return i; }; CompleteOnFirstErrorSkipPolicy.prototype.fnErrored = function(fnIx) { this.erroredFnIx_ = fnIx; }; CompleteOnFirstErrorSkipPolicy.prototype.shouldSkip_ = function(fnIx) { if (this.erroredFnIx_ === null) { return false; } const fn = this.queueableFns_[fnIx]; const candidateSuite = fn.suite; const errorSuite = this.queueableFns_[this.erroredFnIx_].suite; const wasCleanupFn = fn.type === 'afterEach' || fn.type === 'afterAll' || fn.type === 'specCleanup'; return ( !wasCleanupFn || (candidateSuite && isDescendent(candidateSuite, errorSuite)) ); }; function isDescendent(candidate, ancestor) { if (!candidate.parentSuite) { return false; } else if (candidate.parentSuite === ancestor) { return true; } else { return isDescendent(candidate.parentSuite, ancestor); } } return CompleteOnFirstErrorSkipPolicy; }; jasmine-4.5.0/src/core/DelayedFunctionScheduler.js000066400000000000000000000113271432731766000221540ustar00rootroot00000000000000getJasmineRequireObj().DelayedFunctionScheduler = function(j$) { function DelayedFunctionScheduler() { this.scheduledLookup_ = []; this.scheduledFunctions_ = {}; this.currentTime_ = 0; this.delayedFnCount_ = 0; this.deletedKeys_ = []; this.tick = function(millis, tickDate) { millis = millis || 0; const endTime = this.currentTime_ + millis; this.runScheduledFunctions_(endTime, tickDate); }; this.scheduleFunction = function( funcToCall, millis, params, recurring, timeoutKey, runAtMillis ) { let f; if (typeof funcToCall === 'string') { f = function() { // eslint-disable-next-line no-eval return eval(funcToCall); }; } else { f = funcToCall; } millis = millis || 0; timeoutKey = timeoutKey || ++this.delayedFnCount_; runAtMillis = runAtMillis || this.currentTime_ + millis; const funcToSchedule = { runAtMillis: runAtMillis, funcToCall: f, recurring: recurring, params: params, timeoutKey: timeoutKey, millis: millis }; if (runAtMillis in this.scheduledFunctions_) { this.scheduledFunctions_[runAtMillis].push(funcToSchedule); } else { this.scheduledFunctions_[runAtMillis] = [funcToSchedule]; this.scheduledLookup_.push(runAtMillis); this.scheduledLookup_.sort(function(a, b) { return a - b; }); } return timeoutKey; }; this.removeFunctionWithId = function(timeoutKey) { this.deletedKeys_.push(timeoutKey); for (const runAtMillis in this.scheduledFunctions_) { const funcs = this.scheduledFunctions_[runAtMillis]; const i = indexOfFirstToPass(funcs, function(func) { return func.timeoutKey === timeoutKey; }); if (i > -1) { if (funcs.length === 1) { delete this.scheduledFunctions_[runAtMillis]; this.deleteFromLookup_(runAtMillis); } else { funcs.splice(i, 1); } // intervals get rescheduled when executed, so there's never more // than a single scheduled function with a given timeoutKey break; } } }; return this; } DelayedFunctionScheduler.prototype.runScheduledFunctions_ = function( endTime, tickDate ) { tickDate = tickDate || function() {}; if ( this.scheduledLookup_.length === 0 || this.scheduledLookup_[0] > endTime ) { if (endTime >= this.currentTime_) { tickDate(endTime - this.currentTime_); this.currentTime_ = endTime; } return; } do { this.deletedKeys_ = []; const newCurrentTime = this.scheduledLookup_.shift(); if (newCurrentTime >= this.currentTime_) { tickDate(newCurrentTime - this.currentTime_); this.currentTime_ = newCurrentTime; } const funcsToRun = this.scheduledFunctions_[this.currentTime_]; delete this.scheduledFunctions_[this.currentTime_]; for (const fn of funcsToRun) { if (fn.recurring) { this.reschedule_(fn); } } for (const fn of funcsToRun) { if (this.deletedKeys_.includes(fn.timeoutKey)) { // skip a timeoutKey deleted whilst we were running return; } fn.funcToCall.apply(null, fn.params || []); } this.deletedKeys_ = []; } while ( this.scheduledLookup_.length > 0 && // checking first if we're out of time prevents setTimeout(0) // scheduled in a funcToRun from forcing an extra iteration this.currentTime_ !== endTime && this.scheduledLookup_[0] <= endTime ); // ran out of functions to call, but still time left on the clock if (endTime >= this.currentTime_) { tickDate(endTime - this.currentTime_); this.currentTime_ = endTime; } }; DelayedFunctionScheduler.prototype.reschedule_ = function(scheduledFn) { this.scheduleFunction( scheduledFn.funcToCall, scheduledFn.millis, scheduledFn.params, true, scheduledFn.timeoutKey, scheduledFn.runAtMillis + scheduledFn.millis ); }; DelayedFunctionScheduler.prototype.deleteFromLookup_ = function(key) { const value = Number(key); const i = indexOfFirstToPass(this.scheduledLookup_, function(millis) { return millis === value; }); if (i > -1) { this.scheduledLookup_.splice(i, 1); } }; function indexOfFirstToPass(array, testFn) { let index = -1; for (let i = 0; i < array.length; ++i) { if (testFn(array[i])) { index = i; break; } } return index; } return DelayedFunctionScheduler; }; jasmine-4.5.0/src/core/Deprecator.js000066400000000000000000000044561432731766000173350ustar00rootroot00000000000000getJasmineRequireObj().Deprecator = function(j$) { function Deprecator(topSuite) { this.topSuite_ = topSuite; this.verbose_ = false; this.toSuppress_ = []; } const verboseNote = 'Note: This message will be shown only once. Set the verboseDeprecations ' + 'config property to true to see every occurrence.'; Deprecator.prototype.verboseDeprecations = function(enabled) { this.verbose_ = enabled; }; // runnable is a spec or a suite. // deprecation is a string or an Error. // See Env#deprecated for a description of the options argument. Deprecator.prototype.addDeprecationWarning = function( runnable, deprecation, options ) { options = options || {}; if (!this.verbose_ && !j$.isError_(deprecation)) { if (this.toSuppress_.indexOf(deprecation) !== -1) { return; } this.toSuppress_.push(deprecation); } this.log_(runnable, deprecation, options); this.report_(runnable, deprecation, options); }; Deprecator.prototype.log_ = function(runnable, deprecation, options) { if (j$.isError_(deprecation)) { console.error(deprecation); return; } let context; if (runnable === this.topSuite_ || options.ignoreRunnable) { context = ''; } else if (runnable.children) { context = ' (in suite: ' + runnable.getFullName() + ')'; } else { context = ' (in spec: ' + runnable.getFullName() + ')'; } if (!options.omitStackTrace) { context += '\n' + this.stackTrace_(); } if (!this.verbose_) { context += '\n' + verboseNote; } console.error('DEPRECATION: ' + deprecation + context); }; Deprecator.prototype.stackTrace_ = function() { const formatter = new j$.ExceptionFormatter(); return formatter.stack(j$.util.errorWithStack()).replace(/^Error\n/m, ''); }; Deprecator.prototype.report_ = function(runnable, deprecation, options) { if (options.ignoreRunnable) { runnable = this.topSuite_; } if (j$.isError_(deprecation)) { runnable.addDeprecationWarning(deprecation); return; } if (!this.verbose_) { deprecation += '\n' + verboseNote; } runnable.addDeprecationWarning({ message: deprecation, omitStackTrace: options.omitStackTrace || false }); }; return Deprecator; }; jasmine-4.5.0/src/core/Env.js000066400000000000000000000675651432731766000160070ustar00rootroot00000000000000getJasmineRequireObj().Env = function(j$) { /** * @class Env * @since 2.0.0 * @classdesc The Jasmine environment.
* _Note:_ Do not construct this directly. You can obtain the Env instance by * calling {@link jasmine.getEnv}. * @hideconstructor */ function Env(options) { options = options || {}; const self = this; const global = options.global || j$.getGlobal(); const realSetTimeout = global.setTimeout; const realClearTimeout = global.clearTimeout; const clearStack = j$.getClearStack(global); this.clock = new j$.Clock( global, function() { return new j$.DelayedFunctionScheduler(); }, new j$.MockDate(global) ); const globalErrors = new j$.GlobalErrors(); const installGlobalErrors = (function() { let installed = false; return function() { if (!installed) { globalErrors.install(); installed = true; } }; })(); const runableResources = new j$.RunableResources({ getCurrentRunableId: function() { const r = runner.currentRunable(); return r ? r.id : null; }, globalErrors }); let reporter; let topSuite; let runner; /** * This represents the available options to configure Jasmine. * Options that are not provided will use their default values. * @see Env#configure * @interface Configuration * @since 3.3.0 */ const config = { /** * Whether to randomize spec execution order * @name Configuration#random * @since 3.3.0 * @type Boolean * @default true */ random: true, /** * Seed to use as the basis of randomization. * Null causes the seed to be determined randomly at the start of execution. * @name Configuration#seed * @since 3.3.0 * @type (number|string) * @default null */ seed: null, /** * Whether to stop execution of the suite after the first spec failure * @name Configuration#stopOnSpecFailure * @since 3.9.0 * @type Boolean * @default false */ stopOnSpecFailure: false, /** * Whether to fail the spec if it ran no expectations. By default * a spec that ran no expectations is reported as passed. Setting this * to true will report such spec as a failure. * @name Configuration#failSpecWithNoExpectations * @since 3.5.0 * @type Boolean * @default false */ failSpecWithNoExpectations: false, /** * Whether to cause specs to only have one expectation failure. * @name Configuration#stopSpecOnExpectationFailure * @since 3.3.0 * @type Boolean * @default false */ stopSpecOnExpectationFailure: false, /** * A function that takes a spec and returns true if it should be executed * or false if it should be skipped. * @callback SpecFilter * @param {Spec} spec - The spec that the filter is being applied to. * @return boolean */ /** * Function to use to filter specs * @name Configuration#specFilter * @since 3.3.0 * @type SpecFilter * @default A function that always returns true. */ specFilter: function() { return true; }, /** * Whether or not reporters should hide disabled specs from their output. * Currently only supported by Jasmine's HTMLReporter * @name Configuration#hideDisabled * @since 3.3.0 * @type Boolean * @default false */ hideDisabled: false, /** * Clean closures when a suite is done running (done by clearing the stored function reference). * This prevents memory leaks, but you won't be able to run jasmine multiple times. * @name Configuration#autoCleanClosures * @since 3.10.0 * @type boolean * @default true */ autoCleanClosures: true, /** * Whether or not to issue warnings for certain deprecated functionality * every time it's used. If not set or set to false, deprecation warnings * for methods that tend to be called frequently will be issued only once * or otherwise throttled to to prevent the suite output from being flooded * with warnings. * @name Configuration#verboseDeprecations * @since 3.6.0 * @type Boolean * @default false */ verboseDeprecations: false }; if (!options.suppressLoadErrors) { installGlobalErrors(); globalErrors.pushListener(function loadtimeErrorHandler( message, filename, lineno, colNo, err ) { topSuite.result.failedExpectations.push({ passed: false, globalErrorType: 'load', message: message, stack: err && err.stack, filename: filename, lineno: lineno }); }); } /** * Configure your jasmine environment * @name Env#configure * @since 3.3.0 * @argument {Configuration} configuration * @function */ this.configure = function(configuration) { const booleanProps = [ 'random', 'failSpecWithNoExpectations', 'hideDisabled', 'stopOnSpecFailure', 'stopSpecOnExpectationFailure', 'autoCleanClosures' ]; booleanProps.forEach(function(prop) { if (typeof configuration[prop] !== 'undefined') { config[prop] = !!configuration[prop]; } }); if (configuration.specFilter) { config.specFilter = configuration.specFilter; } if (typeof configuration.seed !== 'undefined') { config.seed = configuration.seed; } if (configuration.hasOwnProperty('verboseDeprecations')) { config.verboseDeprecations = configuration.verboseDeprecations; deprecator.verboseDeprecations(config.verboseDeprecations); } }; /** * Get the current configuration for your jasmine environment * @name Env#configuration * @since 3.3.0 * @function * @returns {Configuration} */ this.configuration = function() { const result = {}; for (const property in config) { result[property] = config[property]; } return result; }; this.setDefaultSpyStrategy = function(defaultStrategyFn) { runableResources.setDefaultSpyStrategy(defaultStrategyFn); }; this.addSpyStrategy = function(name, fn) { runableResources.customSpyStrategies()[name] = fn; }; this.addCustomEqualityTester = function(tester) { runableResources.customEqualityTesters().push(tester); }; this.addMatchers = function(matchersToAdd) { runableResources.addCustomMatchers(matchersToAdd); }; this.addAsyncMatchers = function(matchersToAdd) { runableResources.addCustomAsyncMatchers(matchersToAdd); }; this.addCustomObjectFormatter = function(formatter) { runableResources.customObjectFormatters().push(formatter); }; j$.Expectation.addCoreMatchers(j$.matchers); j$.Expectation.addAsyncCoreMatchers(j$.asyncMatchers); const expectationFactory = function(actual, spec) { return j$.Expectation.factory({ matchersUtil: runableResources.makeMatchersUtil(), customMatchers: runableResources.customMatchers(), actual: actual, addExpectationResult: addExpectationResult }); function addExpectationResult(passed, result) { return spec.addExpectationResult(passed, result); } }; // TODO: Unify recordLateError with recordLateExpectation? The extra // diagnostic info added by the latter is probably useful in most cases. function recordLateError(error) { const isExpectationResult = error.matcherName !== undefined && error.passed !== undefined; const result = isExpectationResult ? error : j$.buildExpectationResult({ error, passed: false, matcherName: '', expected: '', actual: '' }); routeLateFailure(result); } function recordLateExpectation(runable, runableType, result) { const delayedExpectationResult = {}; Object.keys(result).forEach(function(k) { delayedExpectationResult[k] = result[k]; }); delayedExpectationResult.passed = false; delayedExpectationResult.globalErrorType = 'lateExpectation'; delayedExpectationResult.message = runableType + ' "' + runable.getFullName() + '" ran a "' + result.matcherName + '" expectation after it finished.\n'; if (result.message) { delayedExpectationResult.message += 'Message: "' + result.message + '"\n'; } delayedExpectationResult.message += '1. Did you forget to return or await the result of expectAsync?\n' + '2. Was done() invoked before an async operation completed?\n' + '3. Did an expectation follow a call to done()?'; topSuite.result.failedExpectations.push(delayedExpectationResult); } function routeLateFailure(expectationResult) { // Report the result on the nearest ancestor suite that hasn't already // been reported done. for (let r = runner.currentRunable(); r; r = r.parentSuite) { if (!r.reportedDone) { if (r === topSuite) { expectationResult.globalErrorType = 'lateError'; } r.result.failedExpectations.push(expectationResult); return; } } // If we get here, all results have been reported and there's nothing we // can do except log the result and hope the user sees it. console.error('Jasmine received a result after the suite finished:'); console.error(expectationResult); } const asyncExpectationFactory = function(actual, spec, runableType) { return j$.Expectation.asyncFactory({ matchersUtil: runableResources.makeMatchersUtil(), customAsyncMatchers: runableResources.customAsyncMatchers(), actual: actual, addExpectationResult: addExpectationResult }); function addExpectationResult(passed, result) { if (runner.currentRunable() !== spec) { recordLateExpectation(spec, runableType, result); } return spec.addExpectationResult(passed, result); } }; /** * Causes a deprecation warning to be logged to the console and reported to * reporters. * * The optional second parameter is an object that can have either of the * following properties: * * omitStackTrace: Whether to omit the stack trace. Optional. Defaults to * false. This option is ignored if the deprecation is an Error. Set this * when the stack trace will not contain anything that helps the user find * the source of the deprecation. * * ignoreRunnable: Whether to log the deprecation on the root suite, ignoring * the spec or suite that's running when it happens. Optional. Defaults to * false. * * @name Env#deprecated * @since 2.99 * @function * @param {String|Error} deprecation The deprecation message * @param {Object} [options] Optional extra options, as described above */ this.deprecated = function(deprecation, options) { const runable = runner.currentRunable() || topSuite; deprecator.addDeprecationWarning(runable, deprecation, options); }; function queueRunnerFactory(options) { options.clearStack = options.clearStack || clearStack; options.timeout = { setTimeout: realSetTimeout, clearTimeout: realClearTimeout }; options.fail = self.fail; options.globalErrors = globalErrors; options.onException = options.onException || function(e) { (runner.currentRunable() || topSuite).handleException(e); }; options.deprecated = self.deprecated; new j$.QueueRunner(options).execute(); } const suiteBuilder = new j$.SuiteBuilder({ env: this, expectationFactory, asyncExpectationFactory, onLateError: recordLateError, specResultCallback, specStarted, queueRunnerFactory }); topSuite = suiteBuilder.topSuite; const deprecator = new j$.Deprecator(topSuite); /** * Provides the root suite, through which all suites and specs can be * accessed. * @function * @name Env#topSuite * @return {Suite} the root suite * @since 2.0.0 */ this.topSuite = function() { return topSuite.metadata; }; /** * This represents the available reporter callback for an object passed to {@link Env#addReporter}. * @interface Reporter * @see custom_reporter */ reporter = new j$.ReportDispatcher( [ /** * `jasmineStarted` is called after all of the specs have been loaded, but just before execution starts. * @function * @name Reporter#jasmineStarted * @param {JasmineStartedInfo} suiteInfo Information about the full Jasmine suite that is being run * @param {Function} [done] Used to specify to Jasmine that this callback is asynchronous and Jasmine should wait until it has been called before moving on. * @returns {} Optionally return a Promise instead of using `done` to cause Jasmine to wait for completion. * @see async */ 'jasmineStarted', /** * When the entire suite has finished execution `jasmineDone` is called * @function * @name Reporter#jasmineDone * @param {JasmineDoneInfo} suiteInfo Information about the full Jasmine suite that just finished running. * @param {Function} [done] Used to specify to Jasmine that this callback is asynchronous and Jasmine should wait until it has been called before moving on. * @returns {} Optionally return a Promise instead of using `done` to cause Jasmine to wait for completion. * @see async */ 'jasmineDone', /** * `suiteStarted` is invoked when a `describe` starts to run * @function * @name Reporter#suiteStarted * @param {SuiteResult} result Information about the individual {@link describe} being run * @param {Function} [done] Used to specify to Jasmine that this callback is asynchronous and Jasmine should wait until it has been called before moving on. * @returns {} Optionally return a Promise instead of using `done` to cause Jasmine to wait for completion. * @see async */ 'suiteStarted', /** * `suiteDone` is invoked when all of the child specs and suites for a given suite have been run * * While jasmine doesn't require any specific functions, not defining a `suiteDone` will make it impossible for a reporter to know when a suite has failures in an `afterAll`. * @function * @name Reporter#suiteDone * @param {SuiteResult} result * @param {Function} [done] Used to specify to Jasmine that this callback is asynchronous and Jasmine should wait until it has been called before moving on. * @returns {} Optionally return a Promise instead of using `done` to cause Jasmine to wait for completion. * @see async */ 'suiteDone', /** * `specStarted` is invoked when an `it` starts to run (including associated `beforeEach` functions) * @function * @name Reporter#specStarted * @param {SpecResult} result Information about the individual {@link it} being run * @param {Function} [done] Used to specify to Jasmine that this callback is asynchronous and Jasmine should wait until it has been called before moving on. * @returns {} Optionally return a Promise instead of using `done` to cause Jasmine to wait for completion. * @see async */ 'specStarted', /** * `specDone` is invoked when an `it` and its associated `beforeEach` and `afterEach` functions have been run. * * While jasmine doesn't require any specific functions, not defining a `specDone` will make it impossible for a reporter to know when a spec has failed. * @function * @name Reporter#specDone * @param {SpecResult} result * @param {Function} [done] Used to specify to Jasmine that this callback is asynchronous and Jasmine should wait until it has been called before moving on. * @returns {} Optionally return a Promise instead of using `done` to cause Jasmine to wait for completion. * @see async */ 'specDone' ], function(options) { options.SkipPolicy = j$.NeverSkipPolicy; return queueRunnerFactory(options); }, recordLateError ); runner = new j$.Runner({ topSuite, totalSpecsDefined: () => suiteBuilder.totalSpecsDefined, focusedRunables: () => suiteBuilder.focusedRunables, runableResources, reporter, queueRunnerFactory, getConfig: () => config, reportSpecDone }); /** * Executes the specs. * * If called with no parameters or with a falsy value as the first parameter, * all specs will be executed except those that are excluded by a * [spec filter]{@link Configuration#specFilter} or other mechanism. If the * first parameter is a list of spec/suite IDs, only those specs/suites will * be run. * * Both parameters are optional, but a completion callback is only valid as * the second parameter. To specify a completion callback but not a list of * specs/suites to run, pass null or undefined as the first parameter. The * completion callback is supported for backward compatibility. In most * cases it will be more convenient to use the returned promise instead. * * execute should not be called more than once unless the env has been * configured with `{autoCleanClosures: false}`. * * execute returns a promise. The promise will be resolved to the same * {@link JasmineDoneInfo|overall result} that's passed to a reporter's * `jasmineDone` method, even if the suite did not pass. To determine * whether the suite passed, check the value that the promise resolves to * or use a {@link Reporter}. * * @name Env#execute * @since 2.0.0 * @function * @param {(string[])=} runablesToRun IDs of suites and/or specs to run * @param {Function=} onComplete Function that will be called after all specs have run * @return {Promise} */ this.execute = function(runablesToRun, onComplete) { installGlobalErrors(); return runner.execute(runablesToRun).then(function(jasmineDoneInfo) { if (onComplete) { onComplete(); } return jasmineDoneInfo; }); }; /** * Add a custom reporter to the Jasmine environment. * @name Env#addReporter * @since 2.0.0 * @function * @param {Reporter} reporterToAdd The reporter to be added. * @see custom_reporter */ this.addReporter = function(reporterToAdd) { reporter.addReporter(reporterToAdd); }; /** * Provide a fallback reporter if no other reporters have been specified. * @name Env#provideFallbackReporter * @since 2.5.0 * @function * @param {Reporter} reporterToAdd The reporter * @see custom_reporter */ this.provideFallbackReporter = function(reporterToAdd) { reporter.provideFallbackReporter(reporterToAdd); }; /** * Clear all registered reporters * @name Env#clearReporters * @since 2.5.2 * @function */ this.clearReporters = function() { reporter.clearReporters(); }; /** * Configures whether Jasmine should allow the same function to be spied on * more than once during the execution of a spec. By default, spying on * a function that is already a spy will cause an error. * @name Env#allowRespy * @function * @since 2.5.0 * @param {boolean} allow Whether to allow respying */ this.allowRespy = function(allow) { runableResources.spyRegistry.allowRespy(allow); }; this.spyOn = function() { return runableResources.spyRegistry.spyOn.apply( runableResources.spyRegistry, arguments ); }; this.spyOnProperty = function() { return runableResources.spyRegistry.spyOnProperty.apply( runableResources.spyRegistry, arguments ); }; this.spyOnAllFunctions = function() { return runableResources.spyRegistry.spyOnAllFunctions.apply( runableResources.spyRegistry, arguments ); }; this.createSpy = function(name, originalFn) { return runableResources.spyFactory.createSpy(name, originalFn); }; this.createSpyObj = function(baseName, methodNames, propertyNames) { return runableResources.spyFactory.createSpyObj( baseName, methodNames, propertyNames ); }; this.spyOnGlobalErrorsAsync = async function(fn) { const spy = this.createSpy('global error handler'); const associatedRunable = runner.currentRunable(); let cleanedUp = false; globalErrors.setOverrideListener(spy, () => { if (!cleanedUp) { const message = 'Global error spy was not uninstalled. (Did you ' + 'forget to await the return value of spyOnGlobalErrorsAsync?)'; associatedRunable.addExpectationResult(false, { matcherName: '', passed: false, expected: '', actual: '', message, error: null }); } cleanedUp = true; }); try { const maybePromise = fn(spy); if (!j$.isPromiseLike(maybePromise)) { throw new Error( 'The callback to spyOnGlobalErrorsAsync must be an async or promise-returning function' ); } await maybePromise; } finally { if (!cleanedUp) { cleanedUp = true; globalErrors.removeOverrideListener(); } } }; function ensureIsNotNested(method) { const runable = runner.currentRunable(); if (runable !== null && runable !== undefined) { throw new Error( "'" + method + "' should only be used in 'describe' function" ); } } this.describe = function(description, definitionFn) { ensureIsNotNested('describe'); return suiteBuilder.describe(description, definitionFn).metadata; }; this.xdescribe = function(description, definitionFn) { ensureIsNotNested('xdescribe'); return suiteBuilder.xdescribe(description, definitionFn).metadata; }; this.fdescribe = function(description, definitionFn) { ensureIsNotNested('fdescribe'); return suiteBuilder.fdescribe(description, definitionFn).metadata; }; function specResultCallback(spec, result, next) { runableResources.clearForRunable(spec.id); runner.currentSpec = null; if (result.status === 'failed') { runner.hasFailures = true; } reportSpecDone(spec, result, next); } function specStarted(spec, suite, next) { runner.currentSpec = spec; runableResources.initForRunable(spec.id, suite.id); reporter.specStarted(spec.result).then(next); } function reportSpecDone(spec, result, next) { spec.reportedDone = true; reporter.specDone(result).then(next); } this.it = function(description, fn, timeout) { ensureIsNotNested('it'); return suiteBuilder.it(description, fn, timeout).metadata; }; this.xit = function(description, fn, timeout) { ensureIsNotNested('xit'); return suiteBuilder.xit(description, fn, timeout).metadata; }; this.fit = function(description, fn, timeout) { ensureIsNotNested('fit'); return suiteBuilder.fit(description, fn, timeout).metadata; }; /** * Sets a user-defined property that will be provided to reporters as part of the properties field of {@link SpecResult} * @name Env#setSpecProperty * @since 3.6.0 * @function * @param {String} key The name of the property * @param {*} value The value of the property */ this.setSpecProperty = function(key, value) { if ( !runner.currentRunable() || runner.currentRunable() == runner.currentSuite() ) { throw new Error( "'setSpecProperty' was used when there was no current spec" ); } runner.currentRunable().setSpecProperty(key, value); }; /** * Sets a user-defined property that will be provided to reporters as part of the properties field of {@link SuiteResult} * @name Env#setSuiteProperty * @since 3.6.0 * @function * @param {String} key The name of the property * @param {*} value The value of the property */ this.setSuiteProperty = function(key, value) { if (!runner.currentSuite()) { throw new Error( "'setSuiteProperty' was used when there was no current suite" ); } runner.currentSuite().setSuiteProperty(key, value); }; this.debugLog = function(msg) { const maybeSpec = runner.currentRunable(); if (!maybeSpec || !maybeSpec.debugLog) { throw new Error("'debugLog' was called when there was no current spec"); } maybeSpec.debugLog(msg); }; this.expect = function(actual) { if (!runner.currentRunable()) { throw new Error( "'expect' was used when there was no current spec, this could be because an asynchronous test timed out" ); } return runner.currentRunable().expect(actual); }; this.expectAsync = function(actual) { if (!runner.currentRunable()) { throw new Error( "'expectAsync' was used when there was no current spec, this could be because an asynchronous test timed out" ); } return runner.currentRunable().expectAsync(actual); }; this.beforeEach = function(beforeEachFunction, timeout) { ensureIsNotNested('beforeEach'); suiteBuilder.beforeEach(beforeEachFunction, timeout); }; this.beforeAll = function(beforeAllFunction, timeout) { ensureIsNotNested('beforeAll'); suiteBuilder.beforeAll(beforeAllFunction, timeout); }; this.afterEach = function(afterEachFunction, timeout) { ensureIsNotNested('afterEach'); suiteBuilder.afterEach(afterEachFunction, timeout); }; this.afterAll = function(afterAllFunction, timeout) { ensureIsNotNested('afterAll'); suiteBuilder.afterAll(afterAllFunction, timeout); }; this.pending = function(message) { let fullMessage = j$.Spec.pendingSpecExceptionMessage; if (message) { fullMessage += message; } throw fullMessage; }; this.fail = function(error) { if (!runner.currentRunable()) { throw new Error( "'fail' was used when there was no current spec, this could be because an asynchronous test timed out" ); } let message = 'Failed'; if (error) { message += ': '; if (error.message) { message += error.message; } else if (j$.isString_(error)) { message += error; } else { // pretty print all kind of objects. This includes arrays. const pp = runableResources.makePrettyPrinter(); message += pp(error); } } runner.currentRunable().addExpectationResult(false, { matcherName: '', passed: false, expected: '', actual: '', message: message, error: error && error.message ? error : null }); if (config.stopSpecOnExpectationFailure) { throw new Error(message); } }; this.cleanup_ = function() { if (globalErrors) { globalErrors.uninstall(); } }; } return Env; }; jasmine-4.5.0/src/core/ExceptionFormatter.js000066400000000000000000000060501432731766000210570ustar00rootroot00000000000000getJasmineRequireObj().ExceptionFormatter = function(j$) { const ignoredProperties = [ 'name', 'message', 'stack', 'fileName', 'sourceURL', 'line', 'lineNumber', 'column', 'description', 'jasmineMessage' ]; function ExceptionFormatter(options) { const jasmineFile = (options && options.jasmineFile) || j$.util.jasmineFile(); this.message = function(error) { let message = ''; if (error.jasmineMessage) { message += error.jasmineMessage; } else if (error.name && error.message) { message += error.name + ': ' + error.message; } else if (error.message) { message += error.message; } else { message += error.toString() + ' thrown'; } if (error.fileName || error.sourceURL) { message += ' in ' + (error.fileName || error.sourceURL); } if (error.line || error.lineNumber) { message += ' (line ' + (error.line || error.lineNumber) + ')'; } return message; }; this.stack = function(error, { omitMessage } = {}) { if (!error || !error.stack) { return null; } const lines = this.stack_(error, { messageHandling: omitMessage ? 'omit' : undefined }); return lines.join('\n'); }; // messageHandling can be falsy (unspecified), 'omit', or 'require' this.stack_ = function(error, { messageHandling }) { let lines = formatProperties(error).split('\n'); if (lines[lines.length - 1] === '') { lines.pop(); } const stackTrace = new j$.StackTrace(error); lines = lines.concat(filterJasmine(stackTrace)); if (messageHandling === 'require') { lines.unshift(stackTrace.message || 'Error: ' + error.message); } else if (messageHandling !== 'omit' && stackTrace.message) { lines.unshift(stackTrace.message); } if (error.cause) { const substack = this.stack_(error.cause, { messageHandling: 'require' }); substack[0] = 'Caused by: ' + substack[0]; lines = lines.concat(substack); } return lines; }; function filterJasmine(stackTrace) { const result = []; const jasmineMarker = stackTrace.style === 'webkit' ? '' : ' at '; stackTrace.frames.forEach(function(frame) { if (frame.file !== jasmineFile) { result.push(frame.raw); } else if (result[result.length - 1] !== jasmineMarker) { result.push(jasmineMarker); } }); return result; } function formatProperties(error) { if (!(error instanceof Object)) { return; } const result = {}; let empty = true; for (const prop in error) { if (ignoredProperties.includes(prop)) { continue; } result[prop] = error[prop]; empty = false; } if (!empty) { return 'error properties: ' + j$.basicPrettyPrinter_(result) + '\n'; } return ''; } } return ExceptionFormatter; }; jasmine-4.5.0/src/core/Expectation.js000066400000000000000000000165711432731766000175310ustar00rootroot00000000000000getJasmineRequireObj().Expectation = function(j$) { /** * Matchers that come with Jasmine out of the box. * @namespace matchers */ function Expectation(options) { this.expector = new j$.Expector(options); const customMatchers = options.customMatchers || {}; for (const matcherName in customMatchers) { this[matcherName] = wrapSyncCompare( matcherName, customMatchers[matcherName] ); } } /** * Add some context for an {@link expect} * @function * @name matchers#withContext * @since 3.3.0 * @param {String} message - Additional context to show when the matcher fails * @return {matchers} */ Expectation.prototype.withContext = function withContext(message) { return addFilter(this, new ContextAddingFilter(message)); }; /** * Invert the matcher following this {@link expect} * @member * @name matchers#not * @since 1.3.0 * @type {matchers} * @example * expect(something).not.toBe(true); */ Object.defineProperty(Expectation.prototype, 'not', { get: function() { return addFilter(this, syncNegatingFilter); } }); /** * Asynchronous matchers that operate on an actual value which is a promise, * and return a promise. * * Most async matchers will wait indefinitely for the promise to be resolved * or rejected, resulting in a spec timeout if that never happens. If you * expect that the promise will already be resolved or rejected at the time * the matcher is called, you can use the {@link async-matchers#already} * modifier to get a faster failure with a more helpful message. * * Note: Specs must await the result of each async matcher, return the * promise returned by the matcher, or return a promise that's derived from * the one returned by the matcher. Otherwise the matcher will not be * evaluated before the spec completes. * * @example * // Good * await expectAsync(aPromise).toBeResolved(); * @example * // Good * return expectAsync(aPromise).toBeResolved(); * @example * // Good * return expectAsync(aPromise).toBeResolved() * .then(function() { * // more spec code * }); * @example * // Bad * expectAsync(aPromise).toBeResolved(); * @namespace async-matchers */ function AsyncExpectation(options) { this.expector = new j$.Expector(options); const customAsyncMatchers = options.customAsyncMatchers || {}; for (const matcherName in customAsyncMatchers) { this[matcherName] = wrapAsyncCompare( matcherName, customAsyncMatchers[matcherName] ); } } /** * Add some context for an {@link expectAsync} * @function * @name async-matchers#withContext * @since 3.3.0 * @param {String} message - Additional context to show when the async matcher fails * @return {async-matchers} */ AsyncExpectation.prototype.withContext = function withContext(message) { return addFilter(this, new ContextAddingFilter(message)); }; /** * Invert the matcher following this {@link expectAsync} * @member * @name async-matchers#not * @type {async-matchers} * @example * await expectAsync(myPromise).not.toBeResolved(); * @example * return expectAsync(myPromise).not.toBeResolved(); */ Object.defineProperty(AsyncExpectation.prototype, 'not', { get: function() { return addFilter(this, asyncNegatingFilter); } }); /** * Fail as soon as possible if the actual is pending. * Otherwise evaluate the matcher. * @member * @name async-matchers#already * @since 3.8.0 * @type {async-matchers} * @example * await expectAsync(myPromise).already.toBeResolved(); * @example * return expectAsync(myPromise).already.toBeResolved(); */ Object.defineProperty(AsyncExpectation.prototype, 'already', { get: function() { return addFilter(this, expectSettledPromiseFilter); } }); function wrapSyncCompare(name, matcherFactory) { return function() { const result = this.expector.compare(name, matcherFactory, arguments); this.expector.processResult(result); }; } function wrapAsyncCompare(name, matcherFactory) { return function() { // Capture the call stack here, before we go async, so that it will contain // frames that are relevant to the user instead of just parts of Jasmine. const errorForStack = j$.util.errorWithStack(); return this.expector .compare(name, matcherFactory, arguments) .then(result => { this.expector.processResult(result, errorForStack); }); }; } function addCoreMatchers(prototype, matchers, wrapper) { for (const matcherName in matchers) { const matcher = matchers[matcherName]; prototype[matcherName] = wrapper(matcherName, matcher); } } function addFilter(source, filter) { const result = Object.create(source); result.expector = source.expector.addFilter(filter); return result; } function negatedFailureMessage(result, matcherName, args, matchersUtil) { if (result.message) { if (j$.isFunction_(result.message)) { return result.message(); } else { return result.message; } } args = args.slice(); args.unshift(true); args.unshift(matcherName); return matchersUtil.buildFailureMessage.apply(matchersUtil, args); } function negate(result) { result.pass = !result.pass; return result; } const syncNegatingFilter = { selectComparisonFunc: function(matcher) { function defaultNegativeCompare() { return negate(matcher.compare.apply(null, arguments)); } return matcher.negativeCompare || defaultNegativeCompare; }, buildFailureMessage: negatedFailureMessage }; const asyncNegatingFilter = { selectComparisonFunc: function(matcher) { function defaultNegativeCompare() { return matcher.compare.apply(this, arguments).then(negate); } return matcher.negativeCompare || defaultNegativeCompare; }, buildFailureMessage: negatedFailureMessage }; const expectSettledPromiseFilter = { selectComparisonFunc: function(matcher) { return function(actual) { const matcherArgs = arguments; return j$.isPending_(actual).then(function(isPending) { if (isPending) { return { pass: false, message: 'Expected a promise to be settled (via ' + 'expectAsync(...).already) but it was pending.' }; } else { return matcher.compare.apply(null, matcherArgs); } }); }; } }; function ContextAddingFilter(message) { this.message = message; } ContextAddingFilter.prototype.modifyFailureMessage = function(msg) { if (msg.indexOf('\n') === -1) { return this.message + ': ' + msg; } else { return this.message + ':\n' + indent(msg); } }; function indent(s) { return s.replace(/^/gm, ' '); } return { factory: function(options) { return new Expectation(options || {}); }, addCoreMatchers: function(matchers) { addCoreMatchers(Expectation.prototype, matchers, wrapSyncCompare); }, asyncFactory: function(options) { return new AsyncExpectation(options || {}); }, addAsyncCoreMatchers: function(matchers) { addCoreMatchers(AsyncExpectation.prototype, matchers, wrapAsyncCompare); } }; }; jasmine-4.5.0/src/core/ExpectationFilterChain.js000066400000000000000000000024271432731766000216350ustar00rootroot00000000000000getJasmineRequireObj().ExpectationFilterChain = function() { function ExpectationFilterChain(maybeFilter, prev) { this.filter_ = maybeFilter; this.prev_ = prev; } ExpectationFilterChain.prototype.addFilter = function(filter) { return new ExpectationFilterChain(filter, this); }; ExpectationFilterChain.prototype.selectComparisonFunc = function(matcher) { return this.callFirst_('selectComparisonFunc', arguments).result; }; ExpectationFilterChain.prototype.buildFailureMessage = function( result, matcherName, args, matchersUtil ) { return this.callFirst_('buildFailureMessage', arguments).result; }; ExpectationFilterChain.prototype.modifyFailureMessage = function(msg) { const result = this.callFirst_('modifyFailureMessage', arguments).result; return result || msg; }; ExpectationFilterChain.prototype.callFirst_ = function(fname, args) { if (this.prev_) { const prevResult = this.prev_.callFirst_(fname, args); if (prevResult.found) { return prevResult; } } if (this.filter_ && this.filter_[fname]) { return { found: true, result: this.filter_[fname].apply(this.filter_, args) }; } return { found: false }; }; return ExpectationFilterChain; }; jasmine-4.5.0/src/core/Expector.js000066400000000000000000000050041432731766000170240ustar00rootroot00000000000000getJasmineRequireObj().Expector = function(j$) { function Expector(options) { this.matchersUtil = options.matchersUtil || { buildFailureMessage: function() {} }; this.actual = options.actual; this.addExpectationResult = options.addExpectationResult || function() {}; this.filters = new j$.ExpectationFilterChain(); } Expector.prototype.instantiateMatcher = function( matcherName, matcherFactory, args ) { this.matcherName = matcherName; this.args = Array.prototype.slice.call(args, 0); this.expected = this.args.slice(0); this.args.unshift(this.actual); const matcher = matcherFactory(this.matchersUtil); const comparisonFunc = this.filters.selectComparisonFunc(matcher); return comparisonFunc || matcher.compare; }; Expector.prototype.buildMessage = function(result) { if (result.pass) { return ''; } const defaultMessage = () => { if (!result.message) { const args = this.args.slice(); args.unshift(false); args.unshift(this.matcherName); return this.matchersUtil.buildFailureMessage.apply( this.matchersUtil, args ); } else if (j$.isFunction_(result.message)) { return result.message(); } else { return result.message; } }; const msg = this.filters.buildFailureMessage( result, this.matcherName, this.args, this.matchersUtil, defaultMessage ); return this.filters.modifyFailureMessage(msg || defaultMessage()); }; Expector.prototype.compare = function(matcherName, matcherFactory, args) { const matcherCompare = this.instantiateMatcher( matcherName, matcherFactory, args ); return matcherCompare.apply(null, this.args); }; Expector.prototype.addFilter = function(filter) { const result = Object.create(this); result.filters = this.filters.addFilter(filter); return result; }; Expector.prototype.processResult = function(result, errorForStack) { const message = this.buildMessage(result); if (this.expected.length === 1) { this.expected = this.expected[0]; } this.addExpectationResult(result.pass, { matcherName: this.matcherName, passed: result.pass, message: message, error: errorForStack ? undefined : result.error, errorForStack: errorForStack || undefined, actual: this.actual, expected: this.expected // TODO: this may need to be arrayified/sliced }); }; return Expector; }; jasmine-4.5.0/src/core/GlobalErrors.js000066400000000000000000000105271432731766000176360ustar00rootroot00000000000000getJasmineRequireObj().GlobalErrors = function(j$) { function GlobalErrors(global) { global = global || j$.getGlobal(); const handlers = []; let overrideHandler = null, onRemoveOverrideHandler = null; function onerror(message, source, lineno, colno, error) { if (overrideHandler) { overrideHandler(error || message); return; } const handler = handlers[handlers.length - 1]; if (handler) { handler.apply(null, Array.prototype.slice.call(arguments, 0)); } else { throw arguments[0]; } } this.originalHandlers = {}; this.jasmineHandlers = {}; this.installOne_ = function installOne_(errorType, jasmineMessage) { function taggedOnError(error) { if (j$.isError_(error)) { error.jasmineMessage = jasmineMessage + ': ' + error; } else { let substituteMsg; if (error) { substituteMsg = jasmineMessage + ': ' + error; } else { substituteMsg = jasmineMessage + ' with no error or message'; } if (errorType === 'unhandledRejection') { substituteMsg += '\n' + '(Tip: to get a useful stack trace, use ' + 'Promise.reject(new Error(...)) instead of Promise.reject(' + (error ? '...' : '') + ').)'; } error = new Error(substituteMsg); } const handler = handlers[handlers.length - 1]; if (overrideHandler) { overrideHandler(error); return; } if (handler) { handler(error); } else { throw error; } } this.originalHandlers[errorType] = global.process.listeners(errorType); this.jasmineHandlers[errorType] = taggedOnError; global.process.removeAllListeners(errorType); global.process.on(errorType, taggedOnError); this.uninstall = function uninstall() { const errorTypes = Object.keys(this.originalHandlers); for (const errorType of errorTypes) { global.process.removeListener( errorType, this.jasmineHandlers[errorType] ); for (let i = 0; i < this.originalHandlers[errorType].length; i++) { global.process.on(errorType, this.originalHandlers[errorType][i]); } delete this.originalHandlers[errorType]; delete this.jasmineHandlers[errorType]; } }; }; this.install = function install() { if ( global.process && global.process.listeners && j$.isFunction_(global.process.on) ) { this.installOne_('uncaughtException', 'Uncaught exception'); this.installOne_('unhandledRejection', 'Unhandled promise rejection'); } else { const originalHandler = global.onerror; global.onerror = onerror; const browserRejectionHandler = function browserRejectionHandler( event ) { if (j$.isError_(event.reason)) { event.reason.jasmineMessage = 'Unhandled promise rejection: ' + event.reason; global.onerror(event.reason); } else { global.onerror('Unhandled promise rejection: ' + event.reason); } }; global.addEventListener('unhandledrejection', browserRejectionHandler); this.uninstall = function uninstall() { global.onerror = originalHandler; global.removeEventListener( 'unhandledrejection', browserRejectionHandler ); }; } }; this.pushListener = function pushListener(listener) { handlers.push(listener); }; this.popListener = function popListener(listener) { if (!listener) { throw new Error('popListener expects a listener'); } handlers.pop(); }; this.setOverrideListener = function(listener, onRemove) { if (overrideHandler) { throw new Error("Can't set more than one override listener at a time"); } overrideHandler = listener; onRemoveOverrideHandler = onRemove; }; this.removeOverrideListener = function() { if (onRemoveOverrideHandler) { onRemoveOverrideHandler(); } overrideHandler = null; onRemoveOverrideHandler = null; }; } return GlobalErrors; }; jasmine-4.5.0/src/core/JsApiReporter.js000066400000000000000000000063351432731766000177740ustar00rootroot00000000000000getJasmineRequireObj().JsApiReporter = function(j$) { /** * @name jsApiReporter * @classdesc {@link Reporter} added by default in `boot.js` to record results for retrieval in javascript code. An instance is made available as `jsApiReporter` on the global object. * @class * @hideconstructor */ function JsApiReporter(options) { const timer = options.timer || new j$.Timer(); let status = 'loaded'; this.started = false; this.finished = false; this.runDetails = {}; this.jasmineStarted = function() { this.started = true; status = 'started'; timer.start(); }; let executionTime; this.jasmineDone = function(runDetails) { this.finished = true; this.runDetails = runDetails; executionTime = timer.elapsed(); status = 'done'; }; /** * Get the current status for the Jasmine environment. * @name jsApiReporter#status * @since 2.0.0 * @function * @return {String} - One of `loaded`, `started`, or `done` */ this.status = function() { return status; }; const suites = [], suites_hash = {}; this.suiteStarted = function(result) { suites_hash[result.id] = result; }; this.suiteDone = function(result) { storeSuite(result); }; /** * Get the results for a set of suites. * * Retrievable in slices for easier serialization. * @name jsApiReporter#suiteResults * @since 2.1.0 * @function * @param {Number} index - The position in the suites list to start from. * @param {Number} length - Maximum number of suite results to return. * @return {SuiteResult[]} */ this.suiteResults = function(index, length) { return suites.slice(index, index + length); }; function storeSuite(result) { suites.push(result); suites_hash[result.id] = result; } /** * Get all of the suites in a single object, with their `id` as the key. * @name jsApiReporter#suites * @since 2.0.0 * @function * @return {Object} - Map of suite id to {@link SuiteResult} */ this.suites = function() { return suites_hash; }; const specs = []; this.specDone = function(result) { specs.push(result); }; /** * Get the results for a set of specs. * * Retrievable in slices for easier serialization. * @name jsApiReporter#specResults * @since 2.0.0 * @function * @param {Number} index - The position in the specs list to start from. * @param {Number} length - Maximum number of specs results to return. * @return {SpecResult[]} */ this.specResults = function(index, length) { return specs.slice(index, index + length); }; /** * Get all spec results. * @name jsApiReporter#specs * @since 2.0.0 * @function * @return {SpecResult[]} */ this.specs = function() { return specs; }; /** * Get the number of milliseconds it took for the full Jasmine suite to run. * @name jsApiReporter#executionTime * @since 2.0.0 * @function * @return {Number} */ this.executionTime = function() { return executionTime; }; } return JsApiReporter; }; jasmine-4.5.0/src/core/MockDate.js000066400000000000000000000050121432731766000167210ustar00rootroot00000000000000getJasmineRequireObj().MockDate = function(j$) { function MockDate(global) { let currentTime = 0; if (!global || !global.Date) { this.install = function() {}; this.tick = function() {}; this.uninstall = function() {}; return this; } const GlobalDate = global.Date; this.install = function(mockDate) { if (mockDate instanceof GlobalDate) { currentTime = mockDate.getTime(); } else { if (!j$.util.isUndefined(mockDate)) { throw new Error( 'The argument to jasmine.clock().mockDate(), if specified, ' + 'should be a Date instance.' ); } currentTime = new GlobalDate().getTime(); } global.Date = FakeDate; }; this.tick = function(millis) { millis = millis || 0; currentTime = currentTime + millis; }; this.uninstall = function() { currentTime = 0; global.Date = GlobalDate; }; createDateProperties(); return this; function FakeDate() { switch (arguments.length) { case 0: return new GlobalDate(currentTime); case 1: return new GlobalDate(arguments[0]); case 2: return new GlobalDate(arguments[0], arguments[1]); case 3: return new GlobalDate(arguments[0], arguments[1], arguments[2]); case 4: return new GlobalDate( arguments[0], arguments[1], arguments[2], arguments[3] ); case 5: return new GlobalDate( arguments[0], arguments[1], arguments[2], arguments[3], arguments[4] ); case 6: return new GlobalDate( arguments[0], arguments[1], arguments[2], arguments[3], arguments[4], arguments[5] ); default: return new GlobalDate( arguments[0], arguments[1], arguments[2], arguments[3], arguments[4], arguments[5], arguments[6] ); } } function createDateProperties() { FakeDate.prototype = GlobalDate.prototype; FakeDate.now = function() { return currentTime; }; FakeDate.toSource = GlobalDate.toSource; FakeDate.toString = GlobalDate.toString; FakeDate.parse = GlobalDate.parse; FakeDate.UTC = GlobalDate.UTC; } } return MockDate; }; jasmine-4.5.0/src/core/NeverSkipPolicy.js000066400000000000000000000004351432731766000203240ustar00rootroot00000000000000getJasmineRequireObj().NeverSkipPolicy = function(j$) { function NeverSkipPolicy(queueableFns) {} NeverSkipPolicy.prototype.skipTo = function(lastRanFnIx) { return lastRanFnIx + 1; }; NeverSkipPolicy.prototype.fnErrored = function(fnIx) {}; return NeverSkipPolicy; }; jasmine-4.5.0/src/core/Order.js000066400000000000000000000022651432731766000163140ustar00rootroot00000000000000getJasmineRequireObj().Order = function() { function Order(options) { this.random = 'random' in options ? options.random : true; const seed = (this.seed = options.seed || generateSeed()); this.sort = this.random ? randomOrder : naturalOrder; function naturalOrder(items) { return items; } function randomOrder(items) { const copy = items.slice(); copy.sort(function(a, b) { return jenkinsHash(seed + a.id) - jenkinsHash(seed + b.id); }); return copy; } function generateSeed() { return String(Math.random()).slice(-5); } // Bob Jenkins One-at-a-Time Hash algorithm is a non-cryptographic hash function // used to get a different output when the key changes slightly. // We use your return to sort the children randomly in a consistent way when // used in conjunction with a seed function jenkinsHash(key) { let hash, i; for (hash = i = 0; i < key.length; ++i) { hash += key.charCodeAt(i); hash += hash << 10; hash ^= hash >> 6; } hash += hash << 3; hash ^= hash >> 11; hash += hash << 15; return hash; } } return Order; }; jasmine-4.5.0/src/core/PrettyPrinter.js000066400000000000000000000223271432731766000200750ustar00rootroot00000000000000getJasmineRequireObj().makePrettyPrinter = function(j$) { class SinglePrettyPrintRun { constructor(customObjectFormatters, pp) { this.customObjectFormatters_ = customObjectFormatters; this.ppNestLevel_ = 0; this.seen = []; this.length = 0; this.stringParts = []; this.pp_ = pp; } format(value) { this.ppNestLevel_++; try { const customFormatResult = this.applyCustomFormatters_(value); if (customFormatResult) { this.emitScalar(customFormatResult); } else if (j$.util.isUndefined(value)) { this.emitScalar('undefined'); } else if (value === null) { this.emitScalar('null'); } else if (value === 0 && 1 / value === -Infinity) { this.emitScalar('-0'); } else if (value === j$.getGlobal()) { this.emitScalar(''); } else if (value.jasmineToString) { this.emitScalar(value.jasmineToString(this.pp_)); } else if (j$.isString_(value)) { this.emitString(value); } else if (j$.isSpy(value)) { this.emitScalar('spy on ' + value.and.identity); } else if (j$.isSpy(value.toString)) { this.emitScalar('spy on ' + value.toString.and.identity); } else if (value instanceof RegExp) { this.emitScalar(value.toString()); } else if (typeof value === 'function') { this.emitScalar('Function'); } else if (j$.isDomNode(value)) { if (value.tagName) { this.emitDomElement(value); } else { this.emitScalar('HTMLNode'); } } else if (value instanceof Date) { this.emitScalar('Date(' + value + ')'); } else if (j$.isSet(value)) { this.emitSet(value); } else if (j$.isMap(value)) { this.emitMap(value); } else if (j$.isTypedArray_(value)) { this.emitTypedArray(value); } else if ( value.toString && typeof value === 'object' && !j$.isArray_(value) && hasCustomToString(value) ) { try { this.emitScalar(value.toString()); } catch (e) { this.emitScalar('has-invalid-toString-method'); } } else if (this.seen.includes(value)) { this.emitScalar( '' ); } else if (j$.isArray_(value) || j$.isA_('Object', value)) { this.seen.push(value); if (j$.isArray_(value)) { this.emitArray(value); } else { this.emitObject(value); } this.seen.pop(); } else { this.emitScalar(value.toString()); } } catch (e) { if (this.ppNestLevel_ > 1 || !(e instanceof MaxCharsReachedError)) { throw e; } } finally { this.ppNestLevel_--; } } applyCustomFormatters_(value) { return customFormat(value, this.customObjectFormatters_); } iterateObject(obj, fn) { const objKeys = j$.MatchersUtil.keys(obj, j$.isArray_(obj)); const length = Math.min(objKeys.length, j$.MAX_PRETTY_PRINT_ARRAY_LENGTH); for (let i = 0; i < length; i++) { fn(objKeys[i]); } return objKeys.length > length; } emitScalar(value) { this.append(value); } emitString(value) { this.append("'" + value + "'"); } emitArray(array) { if (this.ppNestLevel_ > j$.MAX_PRETTY_PRINT_DEPTH) { this.append('Array'); return; } const length = Math.min(array.length, j$.MAX_PRETTY_PRINT_ARRAY_LENGTH); this.append('[ '); for (let i = 0; i < length; i++) { if (i > 0) { this.append(', '); } this.format(array[i]); } if (array.length > length) { this.append(', ...'); } let first = array.length === 0; const wasTruncated = this.iterateObject(array, property => { if (first) { first = false; } else { this.append(', '); } this.formatProperty(array, property); }); if (wasTruncated) { this.append(', ...'); } this.append(' ]'); } emitSet(set) { if (this.ppNestLevel_ > j$.MAX_PRETTY_PRINT_DEPTH) { this.append('Set'); return; } this.append('Set( '); const size = Math.min(set.size, j$.MAX_PRETTY_PRINT_ARRAY_LENGTH); let i = 0; set.forEach(function(value, key) { if (i >= size) { return; } if (i > 0) { this.append(', '); } this.format(value); i++; }, this); if (set.size > size) { this.append(', ...'); } this.append(' )'); } emitMap(map) { if (this.ppNestLevel_ > j$.MAX_PRETTY_PRINT_DEPTH) { this.append('Map'); return; } this.append('Map( '); const size = Math.min(map.size, j$.MAX_PRETTY_PRINT_ARRAY_LENGTH); let i = 0; map.forEach(function(value, key) { if (i >= size) { return; } if (i > 0) { this.append(', '); } this.format([key, value]); i++; }, this); if (map.size > size) { this.append(', ...'); } this.append(' )'); } emitObject(obj) { const ctor = obj.constructor; const constructorName = typeof ctor === 'function' && obj instanceof ctor ? j$.fnNameFor(obj.constructor) : 'null'; this.append(constructorName); if (this.ppNestLevel_ > j$.MAX_PRETTY_PRINT_DEPTH) { return; } this.append('({ '); let first = true; const wasTruncated = this.iterateObject(obj, property => { if (first) { first = false; } else { this.append(', '); } this.formatProperty(obj, property); }); if (wasTruncated) { this.append(', ...'); } this.append(' })'); } emitTypedArray(arr) { const constructorName = j$.fnNameFor(arr.constructor); const limitedArray = Array.prototype.slice.call( arr, 0, j$.MAX_PRETTY_PRINT_ARRAY_LENGTH ); let itemsString = Array.prototype.join.call(limitedArray, ', '); if (limitedArray.length !== arr.length) { itemsString += ', ...'; } this.append(constructorName + ' [ ' + itemsString + ' ]'); } emitDomElement(el) { const tagName = el.tagName.toLowerCase(); let out = '<' + tagName; for (const attr of el.attributes) { out += ' ' + attr.name; if (attr.value !== '') { out += '="' + attr.value + '"'; } } out += '>'; if (el.childElementCount !== 0 || el.textContent !== '') { out += '...'; } this.append(out); } formatProperty(obj, property) { if (typeof property === 'symbol') { this.append(property.toString()); } else { this.append(property); } this.append(': '); this.format(obj[property]); } append(value) { // This check protects us from the rare case where an object has overriden // `toString()` with an invalid implementation (returning a non-string). if (typeof value !== 'string') { value = Object.prototype.toString.call(value); } const result = truncate(value, j$.MAX_PRETTY_PRINT_CHARS - this.length); this.length += result.value.length; this.stringParts.push(result.value); if (result.truncated) { throw new MaxCharsReachedError(); } } } function hasCustomToString(value) { // value.toString !== Object.prototype.toString if value has no custom toString but is from another context (e.g. // iframe, web worker) try { return ( j$.isFunction_(value.toString) && value.toString !== Object.prototype.toString && value.toString() !== Object.prototype.toString.call(value) ); } catch (e) { // The custom toString() threw. return true; } } function truncate(s, maxlen) { if (s.length <= maxlen) { return { value: s, truncated: false }; } s = s.substring(0, maxlen - 4) + ' ...'; return { value: s, truncated: true }; } function MaxCharsReachedError() { this.message = 'Exceeded ' + j$.MAX_PRETTY_PRINT_CHARS + ' characters while pretty-printing a value'; } MaxCharsReachedError.prototype = new Error(); function customFormat(value, customObjectFormatters) { for (const formatter of customObjectFormatters) { const result = formatter(value); if (result !== undefined) { return result; } } } return function(customObjectFormatters) { customObjectFormatters = customObjectFormatters || []; const pp = function(value) { const prettyPrinter = new SinglePrettyPrintRun( customObjectFormatters, pp ); prettyPrinter.format(value); return prettyPrinter.stringParts.join(''); }; pp.customFormat_ = function(value) { return customFormat(value, customObjectFormatters); }; return pp; }; }; jasmine-4.5.0/src/core/QueueRunner.js000066400000000000000000000202561432731766000175170ustar00rootroot00000000000000getJasmineRequireObj().QueueRunner = function(j$) { let nextid = 1; function StopExecutionError() {} StopExecutionError.prototype = new Error(); j$.StopExecutionError = StopExecutionError; function once(fn, onTwice) { let called = false; return function(arg) { if (called) { if (onTwice) { onTwice(); } } else { called = true; // Direct call using single parameter, because cleanup/next does not need more fn(arg); } return null; }; } function fallbackOnMultipleDone() { console.error( new Error( "An asynchronous function called its 'done' " + 'callback more than once, in a QueueRunner without a onMultipleDone ' + 'handler.' ) ); } function emptyFn() {} function QueueRunner(attrs) { this.id_ = nextid++; this.queueableFns = attrs.queueableFns || []; this.onComplete = attrs.onComplete || emptyFn; this.clearStack = attrs.clearStack || function(fn) { fn(); }; this.onException = attrs.onException || emptyFn; this.onMultipleDone = attrs.onMultipleDone || fallbackOnMultipleDone; this.userContext = attrs.userContext || new j$.UserContext(); this.timeout = attrs.timeout || { setTimeout: setTimeout, clearTimeout: clearTimeout }; this.fail = attrs.fail || emptyFn; this.globalErrors = attrs.globalErrors || { pushListener: emptyFn, popListener: emptyFn }; const SkipPolicy = attrs.SkipPolicy || j$.NeverSkipPolicy; this.skipPolicy_ = new SkipPolicy(this.queueableFns); this.errored_ = false; if (typeof this.onComplete !== 'function') { throw new Error('invalid onComplete ' + JSON.stringify(this.onComplete)); } this.deprecated = attrs.deprecated; } QueueRunner.prototype.execute = function() { this.handleFinalError = (message, source, lineno, colno, error) => { // Older browsers would send the error as the first parameter. HTML5 // specifies the the five parameters above. The error instance should // be preffered, otherwise the call stack would get lost. this.onException(error || message); }; this.globalErrors.pushListener(this.handleFinalError); this.run(0); }; QueueRunner.prototype.clearTimeout = function(timeoutId) { Function.prototype.apply.apply(this.timeout.clearTimeout, [ j$.getGlobal(), [timeoutId] ]); }; QueueRunner.prototype.setTimeout = function(fn, timeout) { return Function.prototype.apply.apply(this.timeout.setTimeout, [ j$.getGlobal(), [fn, timeout] ]); }; QueueRunner.prototype.attempt = function attempt(iterativeIndex) { let timeoutId; let timedOut; let completedSynchronously = true; const onException = e => { this.onException(e); this.recordError_(iterativeIndex); }; function handleError(error) { // TODO probably shouldn't next() right away here. // That makes debugging async failures much more confusing. onException(error); } const cleanup = once(() => { if (timeoutId !== void 0) { this.clearTimeout(timeoutId); } this.globalErrors.popListener(handleError); }); const next = once( err => { cleanup(); if (typeof err !== 'undefined') { if (!(err instanceof StopExecutionError) && !err.jasmineMessage) { this.fail(err); } this.recordError_(iterativeIndex); } const runNext = () => { this.run(this.nextFnIx_(iterativeIndex)); }; if (completedSynchronously) { this.setTimeout(runNext); } else { runNext(); } }, () => { try { if (!timedOut) { this.onMultipleDone(); } } catch (error) { // Any error we catch here is probably due to a bug in Jasmine, // and it's not likely to end up anywhere useful if we let it // propagate. Log it so it can at least show up when debugging. console.error(error); } } ); timedOut = false; const queueableFn = this.queueableFns[iterativeIndex]; next.fail = function nextFail() { this.fail.apply(null, arguments); this.recordError_(iterativeIndex); next(); }.bind(this); this.globalErrors.pushListener(handleError); if (queueableFn.timeout !== undefined) { const timeoutInterval = queueableFn.timeout || j$.DEFAULT_TIMEOUT_INTERVAL; timeoutId = this.setTimeout(function() { timedOut = true; const error = new Error( 'Timeout - Async function did not complete within ' + timeoutInterval + 'ms ' + (queueableFn.timeout ? '(custom timeout)' : '(set by jasmine.DEFAULT_TIMEOUT_INTERVAL)') ); // TODO Need to decide what to do about a successful completion after a // timeout. That should probably not be a deprecation, and maybe not // an error in 4.0. (But a diagnostic of some sort might be helpful.) onException(error); next(); }, timeoutInterval); } try { let maybeThenable; if (queueableFn.fn.length === 0) { maybeThenable = queueableFn.fn.call(this.userContext); if (maybeThenable && j$.isFunction_(maybeThenable.then)) { maybeThenable.then( wrapInPromiseResolutionHandler(next), onPromiseRejection ); completedSynchronously = false; return { completedSynchronously: false }; } } else { maybeThenable = queueableFn.fn.call(this.userContext, next); this.diagnoseConflictingAsync_(queueableFn.fn, maybeThenable); completedSynchronously = false; return { completedSynchronously: false }; } } catch (e) { onException(e); this.recordError_(iterativeIndex); } cleanup(); return { completedSynchronously: true }; function onPromiseRejection(e) { onException(e); next(); } }; QueueRunner.prototype.run = function(recursiveIndex) { const length = this.queueableFns.length; for ( let iterativeIndex = recursiveIndex; iterativeIndex < length; iterativeIndex = this.nextFnIx_(iterativeIndex) ) { const result = this.attempt(iterativeIndex); if (!result.completedSynchronously) { return; } } this.clearStack(() => { this.globalErrors.popListener(this.handleFinalError); if (this.errored_) { this.onComplete(new StopExecutionError()); } else { this.onComplete(); } }); }; QueueRunner.prototype.nextFnIx_ = function(currentFnIx) { const result = this.skipPolicy_.skipTo(currentFnIx); if (result === currentFnIx) { throw new Error("Can't skip to the same queueable fn that just finished"); } return result; }; QueueRunner.prototype.recordError_ = function(currentFnIx) { this.errored_ = true; this.skipPolicy_.fnErrored(currentFnIx); }; QueueRunner.prototype.diagnoseConflictingAsync_ = function(fn, retval) { if (retval && j$.isFunction_(retval.then)) { // Issue a warning that matches the user's code. // Omit the stack trace because there's almost certainly no user code // on the stack at this point. if (j$.isAsyncFunction_(fn)) { this.onException( 'An asynchronous before/it/after ' + 'function was defined with the async keyword but also took a ' + 'done callback. Either remove the done callback (recommended) or ' + 'remove the async keyword.' ); } else { this.onException( '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.' ); } } }; function wrapInPromiseResolutionHandler(fn) { return function(maybeArg) { if (j$.isError_(maybeArg)) { fn(maybeArg); } else { fn(); } }; } return QueueRunner; }; jasmine-4.5.0/src/core/ReportDispatcher.js000066400000000000000000000036051432731766000205220ustar00rootroot00000000000000getJasmineRequireObj().ReportDispatcher = function(j$) { function ReportDispatcher(methods, queueRunnerFactory, onLateError) { const dispatchedMethods = methods || []; for (const method of dispatchedMethods) { this[method] = (function(m) { return function() { return dispatch(m, arguments); }; })(method); } let reporters = []; let fallbackReporter = null; this.addReporter = function(reporter) { reporters.push(reporter); }; this.provideFallbackReporter = function(reporter) { fallbackReporter = reporter; }; this.clearReporters = function() { reporters = []; }; return this; function dispatch(method, args) { if (reporters.length === 0 && fallbackReporter !== null) { reporters.push(fallbackReporter); } const fns = []; for (const reporter of reporters) { addFn(fns, reporter, method, args); } return new Promise(function(resolve) { queueRunnerFactory({ queueableFns: fns, onComplete: resolve, isReporter: true, onMultipleDone: function() { onLateError( new Error( "An asynchronous reporter callback called its 'done' callback " + 'more than once.' ) ); } }); }); } function addFn(fns, reporter, method, args) { const fn = reporter[method]; if (!fn) { return; } const thisArgs = j$.util.cloneArgs(args); if (fn.length <= 1) { fns.push({ fn: function() { return fn.apply(reporter, thisArgs); } }); } else { fns.push({ fn: function(done) { return fn.apply(reporter, thisArgs.concat([done])); } }); } } } return ReportDispatcher; }; jasmine-4.5.0/src/core/RunableResources.js000066400000000000000000000100561432731766000205210ustar00rootroot00000000000000getJasmineRequireObj().RunableResources = function(j$) { class RunableResources { constructor(options) { this.byRunableId_ = {}; this.getCurrentRunableId_ = options.getCurrentRunableId; this.globalErrors_ = options.globalErrors; this.spyFactory = new j$.SpyFactory( () => { if (this.getCurrentRunableId_()) { return this.customSpyStrategies(); } else { return {}; } }, () => this.defaultSpyStrategy(), () => this.makeMatchersUtil() ); this.spyRegistry = new j$.SpyRegistry({ currentSpies: () => this.spies(), createSpy: (name, originalFn) => this.spyFactory.createSpy(name, originalFn) }); } initForRunable(runableId, parentId) { const newRes = (this.byRunableId_[runableId] = { customEqualityTesters: [], customMatchers: {}, customAsyncMatchers: {}, customSpyStrategies: {}, customObjectFormatters: [], defaultSpyStrategy: undefined, spies: [] }); const parentRes = this.byRunableId_[parentId]; if (parentRes) { newRes.defaultSpyStrategy = parentRes.defaultSpyStrategy; const toClone = [ 'customEqualityTesters', 'customMatchers', 'customAsyncMatchers', 'customObjectFormatters', 'customSpyStrategies' ]; for (const k of toClone) { newRes[k] = j$.util.clone(parentRes[k]); } } } clearForRunable(runableId) { this.globalErrors_.removeOverrideListener(); this.spyRegistry.clearSpies(); delete this.byRunableId_[runableId]; } spies() { return this.forCurrentRunable_( 'Spies must be created in a before function or a spec' ).spies; } defaultSpyStrategy() { if (!this.getCurrentRunableId_()) { return undefined; } return this.byRunableId_[this.getCurrentRunableId_()].defaultSpyStrategy; } setDefaultSpyStrategy(fn) { this.forCurrentRunable_( 'Default spy strategy must be set in a before function or a spec' ).defaultSpyStrategy = fn; } customSpyStrategies() { return this.forCurrentRunable_( 'Custom spy strategies must be added in a before function or a spec' ).customSpyStrategies; } customEqualityTesters() { return this.forCurrentRunable_( 'Custom Equalities must be added in a before function or a spec' ).customEqualityTesters; } customMatchers() { return this.forCurrentRunable_( 'Matchers must be added in a before function or a spec' ).customMatchers; } addCustomMatchers(matchersToAdd) { const matchers = this.customMatchers(); for (const name in matchersToAdd) { matchers[name] = matchersToAdd[name]; } } customAsyncMatchers() { return this.forCurrentRunable_( 'Async Matchers must be added in a before function or a spec' ).customAsyncMatchers; } addCustomAsyncMatchers(matchersToAdd) { const matchers = this.customAsyncMatchers(); for (const name in matchersToAdd) { matchers[name] = matchersToAdd[name]; } } customObjectFormatters() { return this.forCurrentRunable_( 'Custom object formatters must be added in a before function or a spec' ).customObjectFormatters; } makePrettyPrinter() { return j$.makePrettyPrinter(this.customObjectFormatters()); } makeMatchersUtil() { if (this.getCurrentRunableId_()) { return new j$.MatchersUtil({ customTesters: this.customEqualityTesters(), pp: this.makePrettyPrinter() }); } else { return new j$.MatchersUtil({ pp: j$.basicPrettyPrinter_ }); } } forCurrentRunable_(errorMsg) { const resources = this.byRunableId_[this.getCurrentRunableId_()]; if (!resources && errorMsg) { throw new Error(errorMsg); } return resources; } } return RunableResources; }; jasmine-4.5.0/src/core/Runner.js000066400000000000000000000174671432731766000165240ustar00rootroot00000000000000getJasmineRequireObj().Runner = function(j$) { class Runner { constructor(options) { this.topSuite_ = options.topSuite; this.totalSpecsDefined_ = options.totalSpecsDefined; this.focusedRunables_ = options.focusedRunables; this.runableResources_ = options.runableResources; this.queueRunnerFactory_ = options.queueRunnerFactory; this.reporter_ = options.reporter; this.getConfig_ = options.getConfig; this.reportSpecDone_ = options.reportSpecDone; this.hasFailures = false; this.executedBefore_ = false; this.currentlyExecutingSuites_ = []; this.currentSpec = null; } currentRunable() { return this.currentSpec || this.currentSuite(); } currentSuite() { return this.currentlyExecutingSuites_[ this.currentlyExecutingSuites_.length - 1 ]; } // Although execute returns a promise, it isn't async for backwards // compatibility: The "Invalid order" exception needs to be propagated // synchronously from Env#execute. // TODO: make this and Env#execute async in the next major release execute(runablesToRun) { if (this.executedBefore_) { this.topSuite_.reset(); } this.executedBefore_ = true; this.hasFailures = false; const focusedRunables = this.focusedRunables_(); const config = this.getConfig_(); if (!runablesToRun) { if (focusedRunables.length) { runablesToRun = focusedRunables; } else { runablesToRun = [this.topSuite_.id]; } } const order = new j$.Order({ random: config.random, seed: j$.isNumber_(config.seed) ? config.seed + '' : config.seed }); const processor = new j$.TreeProcessor({ tree: this.topSuite_, runnableIds: runablesToRun, queueRunnerFactory: options => { if (options.isLeaf) { // A spec options.SkipPolicy = j$.CompleteOnFirstErrorSkipPolicy; } else { // A suite if (config.stopOnSpecFailure) { options.SkipPolicy = j$.CompleteOnFirstErrorSkipPolicy; } else { options.SkipPolicy = j$.SkipAfterBeforeAllErrorPolicy; } } return this.queueRunnerFactory_(options); }, failSpecWithNoExpectations: config.failSpecWithNoExpectations, nodeStart: (suite, next) => { this.currentlyExecutingSuites_.push(suite); this.runableResources_.initForRunable(suite.id, suite.parentSuite.id); this.reporter_.suiteStarted(suite.result).then(next); suite.startTimer(); }, nodeComplete: (suite, result, next) => { if (suite !== this.currentSuite()) { throw new Error('Tried to complete the wrong suite'); } this.runableResources_.clearForRunable(suite.id); this.currentlyExecutingSuites_.pop(); if (result.status === 'failed') { this.hasFailures = true; } suite.endTimer(); if (suite.hadBeforeAllFailure) { this.reportChildrenOfBeforeAllFailure_(suite).then(() => { this.reportSuiteDone_(suite, result, next); }); } else { this.reportSuiteDone_(suite, result, next); } }, orderChildren: function(node) { return order.sort(node.children); }, excludeNode: function(spec) { return !config.specFilter(spec); } }); if (!processor.processTree().valid) { throw new Error( 'Invalid order: would cause a beforeAll or afterAll to be run multiple times' ); } return this.execute2_(runablesToRun, order, processor); } async execute2_(runablesToRun, order, processor) { const totalSpecsDefined = this.totalSpecsDefined_(); this.runableResources_.initForRunable(this.topSuite_.id); const jasmineTimer = new j$.Timer(); jasmineTimer.start(); /** * Information passed to the {@link Reporter#jasmineStarted} event. * @typedef JasmineStartedInfo * @property {Int} totalSpecsDefined - The total number of specs defined in this suite. * @property {Order} order - Information about the ordering (random or not) of this execution of the suite. * @since 2.0.0 */ await this.reporter_.jasmineStarted({ totalSpecsDefined, order: order }); this.currentlyExecutingSuites_.push(this.topSuite_); await processor.execute(); if (this.topSuite_.hadBeforeAllFailure) { await this.reportChildrenOfBeforeAllFailure_(this.topSuite_); } this.runableResources_.clearForRunable(this.topSuite_.id); this.currentlyExecutingSuites_.pop(); let overallStatus, incompleteReason; if ( this.hasFailures || this.topSuite_.result.failedExpectations.length > 0 ) { overallStatus = 'failed'; } else if (this.focusedRunables_().length > 0) { overallStatus = 'incomplete'; incompleteReason = 'fit() or fdescribe() was found'; } else if (totalSpecsDefined === 0) { overallStatus = 'incomplete'; incompleteReason = 'No specs found'; } else { overallStatus = 'passed'; } /** * Information passed to the {@link Reporter#jasmineDone} event. * @typedef JasmineDoneInfo * @property {OverallStatus} overallStatus - The overall result of the suite: 'passed', 'failed', or 'incomplete'. * @property {Int} totalTime - The total time (in ms) that it took to execute the suite * @property {IncompleteReason} incompleteReason - Explanation of why the suite was incomplete. * @property {Order} order - Information about the ordering (random or not) of this execution of the suite. * @property {Expectation[]} failedExpectations - List of expectations that failed in an {@link afterAll} at the global level. * @property {Expectation[]} deprecationWarnings - List of deprecation warnings that occurred at the global level. * @since 2.4.0 */ const jasmineDoneInfo = { overallStatus: overallStatus, totalTime: jasmineTimer.elapsed(), incompleteReason: incompleteReason, order: order, failedExpectations: this.topSuite_.result.failedExpectations, deprecationWarnings: this.topSuite_.result.deprecationWarnings }; this.topSuite_.reportedDone = true; await this.reporter_.jasmineDone(jasmineDoneInfo); return jasmineDoneInfo; } reportSuiteDone_(suite, result, next) { suite.reportedDone = true; this.reporter_.suiteDone(result).then(next); } async reportChildrenOfBeforeAllFailure_(suite) { for (const child of suite.children) { if (child instanceof j$.Suite) { await this.reporter_.suiteStarted(child.result); await this.reportChildrenOfBeforeAllFailure_(child); // Marking the suite passed is consistent with how suites that // contain failed specs but no suite-level failures are reported. child.result.status = 'passed'; await this.reporter_.suiteDone(child.result); } else { /* a spec */ await this.reporter_.specStarted(child.result); child.addExpectationResult( false, { passed: false, message: 'Not run because a beforeAll function failed. The ' + 'beforeAll failure will be reported on the suite that ' + 'caused it.' }, true ); child.result.status = 'failed'; await new Promise(resolve => { this.reportSpecDone_(child, child.result, resolve); }); } } } } return Runner; }; jasmine-4.5.0/src/core/SkipAfterBeforeAllErrorPolicy.js000066400000000000000000000024031432731766000230710ustar00rootroot00000000000000getJasmineRequireObj().SkipAfterBeforeAllErrorPolicy = function(j$) { function SkipAfterBeforeAllErrorPolicy(queueableFns) { this.queueableFns_ = queueableFns; this.skipping_ = false; } SkipAfterBeforeAllErrorPolicy.prototype.skipTo = function(lastRanFnIx) { if (this.skipping_) { return this.nextAfterAllAfter_(lastRanFnIx); } else { return lastRanFnIx + 1; } }; SkipAfterBeforeAllErrorPolicy.prototype.nextAfterAllAfter_ = function(i) { for ( i++; i < this.queueableFns_.length && this.queueableFns_[i].type !== 'afterAll'; i++ ) {} return i; }; SkipAfterBeforeAllErrorPolicy.prototype.fnErrored = function(fnIx) { if (this.queueableFns_[fnIx].type === 'beforeAll') { this.skipping_ = true; // Failures need to be reported for each contained spec. But we can't do // that from here because reporting is async. This function isn't async // (and can't be without greatly complicating QueueRunner). Mark the // failure so that the code that reports the suite result (which is // already async) can detect the failure and report the specs. this.queueableFns_[fnIx].suite.hadBeforeAllFailure = true; } }; return SkipAfterBeforeAllErrorPolicy; }; jasmine-4.5.0/src/core/Spec.js000066400000000000000000000241711432731766000161330ustar00rootroot00000000000000getJasmineRequireObj().Spec = function(j$) { function Spec(attrs) { this.expectationFactory = attrs.expectationFactory; this.asyncExpectationFactory = attrs.asyncExpectationFactory; this.resultCallback = attrs.resultCallback || function() {}; this.id = attrs.id; this.description = attrs.description || ''; this.queueableFn = attrs.queueableFn; this.beforeAndAfterFns = attrs.beforeAndAfterFns || function() { return { befores: [], afters: [] }; }; this.userContext = attrs.userContext || function() { return {}; }; this.onStart = attrs.onStart || function() {}; this.autoCleanClosures = attrs.autoCleanClosures === undefined ? true : !!attrs.autoCleanClosures; this.getSpecName = attrs.getSpecName || function() { return ''; }; this.onLateError = attrs.onLateError || function() {}; this.catchingExceptions = attrs.catchingExceptions || function() { return true; }; this.throwOnExpectationFailure = !!attrs.throwOnExpectationFailure; this.timer = attrs.timer || new j$.Timer(); if (!this.queueableFn.fn) { this.exclude(); } /** * @typedef SpecResult * @property {String} id - The unique id of this spec. * @property {String} description - The description passed to the {@link it} that created this spec. * @property {String} fullName - The full description including all ancestors of this spec. * @property {Expectation[]} failedExpectations - The list of expectations that failed during execution of this spec. * @property {Expectation[]} passedExpectations - The list of expectations that passed during execution of this spec. * @property {Expectation[]} deprecationWarnings - The list of deprecation warnings that occurred during execution this spec. * @property {String} pendingReason - If the spec is {@link pending}, this will be the reason. * @property {String} status - Once the spec has completed, this string represents the pass/fail status of this spec. * @property {number} duration - The time in ms used by the spec execution, including any before/afterEach. * @property {Object} properties - User-supplied properties, if any, that were set using {@link Env#setSpecProperty} * @property {DebugLogEntry[]|null} debugLogs - Messages, if any, that were logged using {@link jasmine.debugLog} during a failing spec. * @since 2.0.0 */ this.result = { id: this.id, description: this.description, fullName: this.getFullName(), failedExpectations: [], passedExpectations: [], deprecationWarnings: [], pendingReason: '', duration: null, properties: null, debugLogs: null }; this.reportedDone = false; } Spec.prototype.addExpectationResult = function(passed, data, isError) { const expectationResult = j$.buildExpectationResult(data); if (passed) { this.result.passedExpectations.push(expectationResult); } else { if (this.reportedDone) { this.onLateError(expectationResult); } else { this.result.failedExpectations.push(expectationResult); // TODO: refactor so that we don't need to override cached status if (this.result.status) { this.result.status = 'failed'; } } if (this.throwOnExpectationFailure && !isError) { throw new j$.errors.ExpectationFailed(); } } }; Spec.prototype.setSpecProperty = function(key, value) { this.result.properties = this.result.properties || {}; this.result.properties[key] = value; }; Spec.prototype.expect = function(actual) { return this.expectationFactory(actual, this); }; Spec.prototype.expectAsync = function(actual) { return this.asyncExpectationFactory(actual, this); }; Spec.prototype.execute = function( queueRunnerFactory, onComplete, excluded, failSpecWithNoExp ) { const onStart = { fn: done => { this.timer.start(); this.onStart(this, done); } }; const complete = { fn: done => { if (this.autoCleanClosures) { this.queueableFn.fn = null; } this.result.status = this.status(excluded, failSpecWithNoExp); this.result.duration = this.timer.elapsed(); if (this.result.status !== 'failed') { this.result.debugLogs = null; } this.resultCallback(this.result, done); }, type: 'specCleanup' }; const fns = this.beforeAndAfterFns(); const runnerConfig = { isLeaf: true, queueableFns: [...fns.befores, this.queueableFn, ...fns.afters], onException: e => this.handleException(e), onMultipleDone: () => { // Issue a deprecation. Include the context ourselves and pass // ignoreRunnable: true, since getting here always means that we've already // moved on and the current runnable isn't the one that caused the problem. this.onLateError( new Error( 'An asynchronous spec, beforeEach, or afterEach function called its ' + "'done' callback more than once.\n(in spec: " + this.getFullName() + ')' ) ); }, onComplete: () => { if (this.result.status === 'failed') { onComplete(new j$.StopExecutionError('spec failed')); } else { onComplete(); } }, userContext: this.userContext(), runnableName: this.getFullName.bind(this) }; if (this.markedPending || excluded === true) { runnerConfig.queueableFns = []; } runnerConfig.queueableFns.unshift(onStart); runnerConfig.queueableFns.push(complete); queueRunnerFactory(runnerConfig); }; Spec.prototype.reset = function() { this.result = { id: this.id, description: this.description, fullName: this.getFullName(), failedExpectations: [], passedExpectations: [], deprecationWarnings: [], pendingReason: this.excludeMessage, duration: null, properties: null, debugLogs: null }; this.markedPending = this.markedExcluding; this.reportedDone = false; }; Spec.prototype.handleException = function handleException(e) { if (Spec.isPendingSpecException(e)) { this.pend(extractCustomPendingMessage(e)); return; } if (e instanceof j$.errors.ExpectationFailed) { return; } this.addExpectationResult( false, { matcherName: '', passed: false, expected: '', actual: '', error: e }, true ); }; /* * Marks state as pending * @param {string} [message] An optional reason message */ Spec.prototype.pend = function(message) { this.markedPending = true; if (message) { this.result.pendingReason = message; } }; /* * Like {@link Spec#pend}, but pending state will survive {@link Spec#reset} * Useful for fit, xit, where pending state remains. * @param {string} [message] An optional reason message */ Spec.prototype.exclude = function(message) { this.markedExcluding = true; if (this.message) { this.excludeMessage = message; } this.pend(message); }; Spec.prototype.getResult = function() { this.result.status = this.status(); return this.result; }; Spec.prototype.status = function(excluded, failSpecWithNoExpectations) { if (excluded === true) { return 'excluded'; } if (this.markedPending) { return 'pending'; } if ( this.result.failedExpectations.length > 0 || (failSpecWithNoExpectations && this.result.failedExpectations.length + this.result.passedExpectations.length === 0) ) { return 'failed'; } return 'passed'; }; Spec.prototype.getFullName = function() { return this.getSpecName(this); }; Spec.prototype.addDeprecationWarning = function(deprecation) { if (typeof deprecation === 'string') { deprecation = { message: deprecation }; } this.result.deprecationWarnings.push( j$.buildExpectationResult(deprecation) ); }; Spec.prototype.debugLog = function(msg) { if (!this.result.debugLogs) { this.result.debugLogs = []; } /** * @typedef DebugLogEntry * @property {String} message - The message that was passed to {@link jasmine.debugLog}. * @property {number} timestamp - The time when the entry was added, in * milliseconds from the spec's start time */ this.result.debugLogs.push({ message: msg, timestamp: this.timer.elapsed() }); }; const extractCustomPendingMessage = function(e) { const fullMessage = e.toString(), boilerplateStart = fullMessage.indexOf(Spec.pendingSpecExceptionMessage), boilerplateEnd = boilerplateStart + Spec.pendingSpecExceptionMessage.length; return fullMessage.slice(boilerplateEnd); }; Spec.pendingSpecExceptionMessage = '=> marked Pending'; Spec.isPendingSpecException = function(e) { return !!( e && e.toString && e.toString().indexOf(Spec.pendingSpecExceptionMessage) !== -1 ); }; /** * @interface Spec * @see Configuration#specFilter * @since 2.0.0 */ Object.defineProperty(Spec.prototype, 'metadata', { get: function() { if (!this.metadata_) { this.metadata_ = { /** * The unique ID of this spec. * @name Spec#id * @readonly * @type {string} * @since 2.0.0 */ id: this.id, /** * The description passed to the {@link it} that created this spec. * @name Spec#description * @readonly * @type {string} * @since 2.0.0 */ description: this.description, /** * The full description including all ancestors of this spec. * @name Spec#getFullName * @function * @returns {string} * @since 2.0.0 */ getFullName: this.getFullName.bind(this) }; } return this.metadata_; } }); return Spec; }; jasmine-4.5.0/src/core/Spy.js000066400000000000000000000143011432731766000160060ustar00rootroot00000000000000getJasmineRequireObj().Spy = function(j$) { const nextOrder = (function() { let order = 0; return function() { return order++; }; })(); /** * @classdesc _Note:_ Do not construct this directly. Use {@link spyOn}, * {@link spyOnProperty}, {@link jasmine.createSpy}, or * {@link jasmine.createSpyObj} instead. * @class Spy * @hideconstructor */ function Spy(name, matchersUtil, optionals) { const spy = function(context, args, invokeNew) { /** * @name Spy.callData * @property {object} object - `this` context for the invocation. * @property {number} invocationOrder - Order of the invocation. * @property {Array} args - The arguments passed for this invocation. * @property returnValue - The value that was returned from this invocation. */ const callData = { object: context, invocationOrder: nextOrder(), args: Array.prototype.slice.apply(args) }; callTracker.track(callData); const returnValue = strategyDispatcher.exec(context, args, invokeNew); callData.returnValue = returnValue; return returnValue; }; const { originalFn, customStrategies, defaultStrategyFn } = optionals || {}; const numArgs = typeof originalFn === 'function' ? originalFn.length : 0, wrapper = makeFunc(numArgs, function(context, args, invokeNew) { return spy(context, args, invokeNew); }), strategyDispatcher = new SpyStrategyDispatcher( { name: name, fn: originalFn, getSpy: function() { return wrapper; }, customStrategies: customStrategies }, matchersUtil ), callTracker = new j$.CallTracker(); function makeFunc(length, fn) { switch (length) { case 1: return function wrap1(a) { return fn(this, arguments, this instanceof wrap1); }; case 2: return function wrap2(a, b) { return fn(this, arguments, this instanceof wrap2); }; case 3: return function wrap3(a, b, c) { return fn(this, arguments, this instanceof wrap3); }; case 4: return function wrap4(a, b, c, d) { return fn(this, arguments, this instanceof wrap4); }; case 5: return function wrap5(a, b, c, d, e) { return fn(this, arguments, this instanceof wrap5); }; case 6: return function wrap6(a, b, c, d, e, f) { return fn(this, arguments, this instanceof wrap6); }; case 7: return function wrap7(a, b, c, d, e, f, g) { return fn(this, arguments, this instanceof wrap7); }; case 8: return function wrap8(a, b, c, d, e, f, g, h) { return fn(this, arguments, this instanceof wrap8); }; case 9: return function wrap9(a, b, c, d, e, f, g, h, i) { return fn(this, arguments, this instanceof wrap9); }; default: return function wrap() { return fn(this, arguments, this instanceof wrap); }; } } for (const prop in originalFn) { if (prop === 'and' || prop === 'calls') { throw new Error( "Jasmine spies would overwrite the 'and' and 'calls' properties on the object being spied upon" ); } wrapper[prop] = originalFn[prop]; } /** * @member {SpyStrategy} - Accesses the default strategy for the spy. This strategy will be used * whenever the spy is called with arguments that don't match any strategy * created with {@link Spy#withArgs}. * @name Spy#and * @since 2.0.0 * @example * spyOn(someObj, 'func').and.returnValue(42); */ wrapper.and = strategyDispatcher.and; /** * Specifies a strategy to be used for calls to the spy that have the * specified arguments. * @name Spy#withArgs * @since 3.0.0 * @function * @param {...*} args - The arguments to match * @type {SpyStrategy} * @example * spyOn(someObj, 'func').withArgs(1, 2, 3).and.returnValue(42); * someObj.func(1, 2, 3); // returns 42 */ wrapper.withArgs = function() { return strategyDispatcher.withArgs.apply(strategyDispatcher, arguments); }; wrapper.calls = callTracker; if (defaultStrategyFn) { defaultStrategyFn(wrapper.and); } return wrapper; } function SpyStrategyDispatcher(strategyArgs, matchersUtil) { const baseStrategy = new j$.SpyStrategy(strategyArgs); const argsStrategies = new StrategyDict(function() { return new j$.SpyStrategy(strategyArgs); }, matchersUtil); this.and = baseStrategy; this.exec = function(spy, args, invokeNew) { let strategy = argsStrategies.get(args); if (!strategy) { if (argsStrategies.any() && !baseStrategy.isConfigured()) { throw new Error( "Spy '" + strategyArgs.name + "' received a call with arguments " + j$.basicPrettyPrinter_(Array.prototype.slice.call(args)) + ' but all configured strategies specify other arguments.' ); } else { strategy = baseStrategy; } } return strategy.exec(spy, args, invokeNew); }; this.withArgs = function() { return { and: argsStrategies.getOrCreate(arguments) }; }; } function StrategyDict(strategyFactory, matchersUtil) { this.strategies = []; this.strategyFactory = strategyFactory; this.matchersUtil = matchersUtil; } StrategyDict.prototype.any = function() { return this.strategies.length > 0; }; StrategyDict.prototype.getOrCreate = function(args) { let strategy = this.get(args); if (!strategy) { strategy = this.strategyFactory(); this.strategies.push({ args: args, strategy: strategy }); } return strategy; }; StrategyDict.prototype.get = function(args) { for (let i = 0; i < this.strategies.length; i++) { if (this.matchersUtil.equals(args, this.strategies[i].args)) { return this.strategies[i].strategy; } } }; return Spy; }; jasmine-4.5.0/src/core/SpyFactory.js000066400000000000000000000044621432731766000173450ustar00rootroot00000000000000getJasmineRequireObj().SpyFactory = function(j$) { function SpyFactory( getCustomStrategies, getDefaultStrategyFn, getMatchersUtil ) { this.createSpy = function(name, originalFn) { if (j$.isFunction_(name) && originalFn === undefined) { originalFn = name; name = originalFn.name; } return j$.Spy(name, getMatchersUtil(), { originalFn, customStrategies: getCustomStrategies(), defaultStrategyFn: getDefaultStrategyFn() }); }; this.createSpyObj = function(baseName, methodNames, propertyNames) { const baseNameIsCollection = j$.isObject_(baseName) || j$.isArray_(baseName); if (baseNameIsCollection) { propertyNames = methodNames; methodNames = baseName; baseName = 'unknown'; } const obj = {}; const methods = normalizeKeyValues(methodNames); for (let i = 0; i < methods.length; i++) { const spy = (obj[methods[i][0]] = this.createSpy( baseName + '.' + methods[i][0] )); if (methods[i].length > 1) { spy.and.returnValue(methods[i][1]); } } const properties = normalizeKeyValues(propertyNames); for (let i = 0; i < properties.length; i++) { const descriptor = { enumerable: true, get: this.createSpy(baseName + '.' + properties[i][0] + '.get'), set: this.createSpy(baseName + '.' + properties[i][0] + '.set') }; if (properties[i].length > 1) { descriptor.get.and.returnValue(properties[i][1]); descriptor.set.and.returnValue(properties[i][1]); } Object.defineProperty(obj, properties[i][0], descriptor); } if (methods.length === 0 && properties.length === 0) { throw 'createSpyObj requires a non-empty array or object of method names to create spies for'; } return obj; }; } function normalizeKeyValues(object) { const result = []; if (j$.isArray_(object)) { for (let i = 0; i < object.length; i++) { result.push([object[i]]); } } else if (j$.isObject_(object)) { for (const key in object) { if (object.hasOwnProperty(key)) { result.push([key, object[key]]); } } } return result; } return SpyFactory; }; jasmine-4.5.0/src/core/SpyRegistry.js000066400000000000000000000151071432731766000175440ustar00rootroot00000000000000getJasmineRequireObj().SpyRegistry = function(j$) { const spyOnMsg = j$.formatErrorMsg( '', 'spyOn(, )' ); const spyOnPropertyMsg = j$.formatErrorMsg( '', 'spyOnProperty(, , [accessType])' ); function SpyRegistry(options) { options = options || {}; const global = options.global || j$.getGlobal(); const createSpy = options.createSpy; const currentSpies = options.currentSpies || function() { return []; }; this.allowRespy = function(allow) { this.respy = allow; }; this.spyOn = function(obj, methodName) { const getErrorMsg = spyOnMsg; if (j$.util.isUndefined(obj) || obj === null) { throw new Error( getErrorMsg( 'could not find an object to spy upon for ' + methodName + '()' ) ); } if (j$.util.isUndefined(methodName) || methodName === null) { throw new Error(getErrorMsg('No method name supplied')); } if (j$.util.isUndefined(obj[methodName])) { throw new Error(getErrorMsg(methodName + '() method does not exist')); } if (obj[methodName] && j$.isSpy(obj[methodName])) { if (this.respy) { return obj[methodName]; } else { throw new Error( getErrorMsg(methodName + ' has already been spied upon') ); } } const descriptor = Object.getOwnPropertyDescriptor(obj, methodName); if (descriptor && !(descriptor.writable || descriptor.set)) { throw new Error( getErrorMsg(methodName + ' is not declared writable or has no setter') ); } const originalMethod = obj[methodName]; const spiedMethod = createSpy(methodName, originalMethod); let restoreStrategy; if ( Object.prototype.hasOwnProperty.call(obj, methodName) || (obj === global && methodName === 'onerror') ) { restoreStrategy = function() { obj[methodName] = originalMethod; }; } else { restoreStrategy = function() { if (!delete obj[methodName]) { obj[methodName] = originalMethod; } }; } currentSpies().push({ restoreObjectToOriginalState: restoreStrategy }); obj[methodName] = spiedMethod; return spiedMethod; }; this.spyOnProperty = function(obj, propertyName, accessType) { const getErrorMsg = spyOnPropertyMsg; accessType = accessType || 'get'; if (j$.util.isUndefined(obj)) { throw new Error( getErrorMsg( 'spyOn could not find an object to spy upon for ' + propertyName + '' ) ); } if (j$.util.isUndefined(propertyName)) { throw new Error(getErrorMsg('No property name supplied')); } const descriptor = j$.util.getPropertyDescriptor(obj, propertyName); if (!descriptor) { throw new Error(getErrorMsg(propertyName + ' property does not exist')); } if (!descriptor.configurable) { throw new Error( getErrorMsg(propertyName + ' is not declared configurable') ); } if (!descriptor[accessType]) { throw new Error( getErrorMsg( 'Property ' + propertyName + ' does not have access type ' + accessType ) ); } if (j$.isSpy(descriptor[accessType])) { if (this.respy) { return descriptor[accessType]; } else { throw new Error( getErrorMsg( propertyName + '#' + accessType + ' has already been spied upon' ) ); } } const originalDescriptor = j$.util.clone(descriptor); const spy = createSpy(propertyName, descriptor[accessType]); let restoreStrategy; if (Object.prototype.hasOwnProperty.call(obj, propertyName)) { restoreStrategy = function() { Object.defineProperty(obj, propertyName, originalDescriptor); }; } else { restoreStrategy = function() { delete obj[propertyName]; }; } currentSpies().push({ restoreObjectToOriginalState: restoreStrategy }); descriptor[accessType] = spy; Object.defineProperty(obj, propertyName, descriptor); return spy; }; this.spyOnAllFunctions = function(obj, includeNonEnumerable) { if (j$.util.isUndefined(obj)) { throw new Error( 'spyOnAllFunctions could not find an object to spy upon' ); } let pointer = obj, propsToSpyOn = [], properties, propertiesToSkip = []; while ( pointer && (!includeNonEnumerable || pointer !== Object.prototype) ) { properties = getProps(pointer, includeNonEnumerable); properties = properties.filter(function(prop) { return propertiesToSkip.indexOf(prop) === -1; }); propertiesToSkip = propertiesToSkip.concat(properties); propsToSpyOn = propsToSpyOn.concat( getSpyableFunctionProps(pointer, properties) ); pointer = Object.getPrototypeOf(pointer); } for (const prop of propsToSpyOn) { this.spyOn(obj, prop); } return obj; }; this.clearSpies = function() { const spies = currentSpies(); for (let i = spies.length - 1; i >= 0; i--) { const spyEntry = spies[i]; spyEntry.restoreObjectToOriginalState(); } }; } function getProps(obj, includeNonEnumerable) { const enumerableProperties = Object.keys(obj); if (!includeNonEnumerable) { return enumerableProperties; } return Object.getOwnPropertyNames(obj).filter(function(prop) { return ( prop !== 'constructor' || enumerableProperties.indexOf('constructor') > -1 ); }); } function getSpyableFunctionProps(obj, propertiesToCheck) { const props = []; for (const prop of propertiesToCheck) { if ( Object.prototype.hasOwnProperty.call(obj, prop) && isSpyableProp(obj, prop) ) { props.push(prop); } } return props; } function isSpyableProp(obj, prop) { let value; try { value = obj[prop]; } catch (e) { return false; } if (value instanceof Function) { const descriptor = Object.getOwnPropertyDescriptor(obj, prop); return (descriptor.writable || descriptor.set) && descriptor.configurable; } return false; } return SpyRegistry; }; jasmine-4.5.0/src/core/SpyStrategy.js000066400000000000000000000106771432731766000175450ustar00rootroot00000000000000getJasmineRequireObj().SpyStrategy = function(j$) { /** * @interface SpyStrategy */ function SpyStrategy(options) { options = options || {}; /** * Get the identifying information for the spy. * @name SpyStrategy#identity * @since 3.0.0 * @member * @type {String} */ this.identity = options.name || 'unknown'; this.originalFn = options.fn || function() {}; this.getSpy = options.getSpy || function() {}; this.plan = this._defaultPlan = function() {}; const cs = options.customStrategies || {}; for (const k in cs) { if (j$.util.has(cs, k) && !this[k]) { this[k] = createCustomPlan(cs[k]); } } /** * Tell the spy to return a promise resolving to the specified value when invoked. * @name SpyStrategy#resolveTo * @since 3.5.0 * @function * @param {*} value The value to return. */ this.resolveTo = function(value) { this.plan = function() { return Promise.resolve(value); }; return this.getSpy(); }; /** * Tell the spy to return a promise rejecting with the specified value when invoked. * @name SpyStrategy#rejectWith * @since 3.5.0 * @function * @param {*} value The value to return. */ this.rejectWith = function(value) { this.plan = function() { return Promise.reject(value); }; return this.getSpy(); }; } function createCustomPlan(factory) { return function() { const plan = factory.apply(null, arguments); if (!j$.isFunction_(plan)) { throw new Error('Spy strategy must return a function'); } this.plan = plan; return this.getSpy(); }; } /** * Execute the current spy strategy. * @name SpyStrategy#exec * @since 2.0.0 * @function */ SpyStrategy.prototype.exec = function(context, args, invokeNew) { const contextArgs = [context].concat( args ? Array.prototype.slice.call(args) : [] ); const target = this.plan.bind.apply(this.plan, contextArgs); return invokeNew ? new target() : target(); }; /** * Tell the spy to call through to the real implementation when invoked. * @name SpyStrategy#callThrough * @since 2.0.0 * @function */ SpyStrategy.prototype.callThrough = function() { this.plan = this.originalFn; return this.getSpy(); }; /** * Tell the spy to return the value when invoked. * @name SpyStrategy#returnValue * @since 2.0.0 * @function * @param {*} value The value to return. */ SpyStrategy.prototype.returnValue = function(value) { this.plan = function() { return value; }; return this.getSpy(); }; /** * Tell the spy to return one of the specified values (sequentially) each time the spy is invoked. * @name SpyStrategy#returnValues * @since 2.1.0 * @function * @param {...*} values - Values to be returned on subsequent calls to the spy. */ SpyStrategy.prototype.returnValues = function() { const values = Array.prototype.slice.call(arguments); this.plan = function() { return values.shift(); }; return this.getSpy(); }; /** * Tell the spy to throw an error when invoked. * @name SpyStrategy#throwError * @since 2.0.0 * @function * @param {Error|Object|String} something Thing to throw */ SpyStrategy.prototype.throwError = function(something) { const error = j$.isString_(something) ? new Error(something) : something; this.plan = function() { throw error; }; return this.getSpy(); }; /** * Tell the spy to call a fake implementation when invoked. * @name SpyStrategy#callFake * @since 2.0.0 * @function * @param {Function} fn The function to invoke with the passed parameters. */ SpyStrategy.prototype.callFake = function(fn) { if ( !( j$.isFunction_(fn) || j$.isAsyncFunction_(fn) || j$.isGeneratorFunction_(fn) ) ) { throw new Error( 'Argument passed to callFake should be a function, got ' + fn ); } this.plan = fn; return this.getSpy(); }; /** * Tell the spy to do nothing when invoked. This is the default. * @name SpyStrategy#stub * @since 2.0.0 * @function */ SpyStrategy.prototype.stub = function(fn) { this.plan = function() {}; return this.getSpy(); }; SpyStrategy.prototype.isConfigured = function() { return this.plan !== this._defaultPlan; }; return SpyStrategy; }; jasmine-4.5.0/src/core/StackTrace.js000066400000000000000000000057641432731766000172740ustar00rootroot00000000000000getJasmineRequireObj().StackTrace = function(j$) { function StackTrace(error) { let lines = error.stack.split('\n').filter(function(line) { return line !== ''; }); const extractResult = extractMessage(error.message, lines); if (extractResult) { this.message = extractResult.message; lines = extractResult.remainder; } const parseResult = tryParseFrames(lines); this.frames = parseResult.frames; this.style = parseResult.style; } const framePatterns = [ // Node, Chrome, Edge // e.g. " at QueueRunner.run (http://localhost:8888/__jasmine__/jasmine.js:4320:20)" // Note that the "function name" can include a surprisingly large set of // characters, including angle brackets and square brackets. { re: /^\s*at ([^\)]+) \(([^\)]+)\)$/, fnIx: 1, fileLineColIx: 2, style: 'v8' }, // NodeJS alternate form, often mixed in with the Chrome style // e.g. " at /some/path:4320:20 { re: /\s*at (.+)$/, fileLineColIx: 1, style: 'v8' }, // PhantomJS on OS X, Safari, Firefox // e.g. "run@http://localhost:8888/__jasmine__/jasmine.js:4320:27" // or "http://localhost:8888/__jasmine__/jasmine.js:4320:27" { re: /^(?:(([^@\s]+)@)|@)?([^\s]+)$/, fnIx: 2, fileLineColIx: 3, style: 'webkit' } ]; // regexes should capture the function name (if any) as group 1 // and the file, line, and column as group 2. function tryParseFrames(lines) { let style = null; const frames = lines.map(function(line) { const convertedLine = first(framePatterns, function(pattern) { const overallMatch = line.match(pattern.re); if (!overallMatch) { return null; } const fileLineColMatch = overallMatch[pattern.fileLineColIx].match( /^(.*):(\d+):\d+$/ ); if (!fileLineColMatch) { return null; } style = style || pattern.style; return { raw: line, file: fileLineColMatch[1], line: parseInt(fileLineColMatch[2], 10), func: overallMatch[pattern.fnIx] }; }); return convertedLine || { raw: line }; }); return { style: style, frames: frames }; } function first(items, fn) { for (const item of items) { const result = fn(item); if (result) { return result; } } } function extractMessage(message, stackLines) { const len = messagePrefixLength(message, stackLines); if (len > 0) { return { message: stackLines.slice(0, len).join('\n'), remainder: stackLines.slice(len) }; } } function messagePrefixLength(message, stackLines) { if (!stackLines[0].match(/^\w*Error/)) { return 0; } const messageLines = message.split('\n'); for (let i = 1; i < messageLines.length; i++) { if (messageLines[i] !== stackLines[i]) { return 0; } } return messageLines.length; } return StackTrace; }; jasmine-4.5.0/src/core/Suite.js000066400000000000000000000212421432731766000163260ustar00rootroot00000000000000getJasmineRequireObj().Suite = function(j$) { function Suite(attrs) { this.env = attrs.env; this.id = attrs.id; this.parentSuite = attrs.parentSuite; this.description = attrs.description; this.expectationFactory = attrs.expectationFactory; this.asyncExpectationFactory = attrs.asyncExpectationFactory; this.throwOnExpectationFailure = !!attrs.throwOnExpectationFailure; this.autoCleanClosures = attrs.autoCleanClosures === undefined ? true : !!attrs.autoCleanClosures; this.onLateError = attrs.onLateError || function() {}; this.beforeFns = []; this.afterFns = []; this.beforeAllFns = []; this.afterAllFns = []; this.timer = attrs.timer || new j$.Timer(); this.children = []; this.reset(); } Suite.prototype.setSuiteProperty = function(key, value) { this.result.properties = this.result.properties || {}; this.result.properties[key] = value; }; Suite.prototype.expect = function(actual) { return this.expectationFactory(actual, this); }; Suite.prototype.expectAsync = function(actual) { return this.asyncExpectationFactory(actual, this); }; Suite.prototype.getFullName = function() { const fullName = []; for ( let parentSuite = this; parentSuite; parentSuite = parentSuite.parentSuite ) { if (parentSuite.parentSuite) { fullName.unshift(parentSuite.description); } } return fullName.join(' '); }; /* * Mark the suite with "pending" status */ Suite.prototype.pend = function() { this.markedPending = true; }; /* * Like {@link Suite#pend}, but pending state will survive {@link Spec#reset} * Useful for fdescribe, xdescribe, where pending state should remain. */ Suite.prototype.exclude = function() { this.pend(); this.markedExcluding = true; }; Suite.prototype.beforeEach = function(fn) { this.beforeFns.unshift({ ...fn, suite: this }); }; Suite.prototype.beforeAll = function(fn) { this.beforeAllFns.push({ ...fn, type: 'beforeAll', suite: this }); }; Suite.prototype.afterEach = function(fn) { this.afterFns.unshift({ ...fn, suite: this, type: 'afterEach' }); }; Suite.prototype.afterAll = function(fn) { this.afterAllFns.unshift({ ...fn, type: 'afterAll' }); }; Suite.prototype.startTimer = function() { this.timer.start(); }; Suite.prototype.endTimer = function() { this.result.duration = this.timer.elapsed(); }; function removeFns(queueableFns) { for (const qf of queueableFns) { qf.fn = null; } } Suite.prototype.cleanupBeforeAfter = function() { if (this.autoCleanClosures) { removeFns(this.beforeAllFns); removeFns(this.afterAllFns); removeFns(this.beforeFns); removeFns(this.afterFns); } }; Suite.prototype.reset = function() { /** * @typedef SuiteResult * @property {String} id - The unique id of this suite. * @property {String} description - The description text passed to the {@link describe} that made this suite. * @property {String} fullName - The full description including all ancestors of this suite. * @property {Expectation[]} failedExpectations - The list of expectations that failed in an {@link afterAll} for this suite. * @property {Expectation[]} deprecationWarnings - The list of deprecation warnings that occurred on this suite. * @property {String} status - Once the suite has completed, this string represents the pass/fail status of this suite. * @property {number} duration - The time in ms for Suite execution, including any before/afterAll, before/afterEach. * @property {Object} properties - User-supplied properties, if any, that were set using {@link Env#setSuiteProperty} * @since 2.0.0 */ this.result = { id: this.id, description: this.description, fullName: this.getFullName(), failedExpectations: [], deprecationWarnings: [], duration: null, properties: null }; this.markedPending = this.markedExcluding; this.children.forEach(function(child) { child.reset(); }); this.reportedDone = false; }; Suite.prototype.addChild = function(child) { this.children.push(child); }; Suite.prototype.status = function() { if (this.markedPending) { return 'pending'; } if (this.result.failedExpectations.length > 0) { return 'failed'; } else { return 'passed'; } }; Suite.prototype.canBeReentered = function() { return this.beforeAllFns.length === 0 && this.afterAllFns.length === 0; }; Suite.prototype.getResult = function() { this.result.status = this.status(); return this.result; }; Suite.prototype.sharedUserContext = function() { if (!this.sharedContext) { this.sharedContext = this.parentSuite ? this.parentSuite.clonedSharedUserContext() : new j$.UserContext(); } return this.sharedContext; }; Suite.prototype.clonedSharedUserContext = function() { return j$.UserContext.fromExisting(this.sharedUserContext()); }; Suite.prototype.handleException = function() { if (arguments[0] instanceof j$.errors.ExpectationFailed) { return; } const data = { matcherName: '', passed: false, expected: '', actual: '', error: arguments[0] }; const failedExpectation = j$.buildExpectationResult(data); if (!this.parentSuite) { failedExpectation.globalErrorType = 'afterAll'; } if (this.reportedDone) { this.onLateError(failedExpectation); } else { this.result.failedExpectations.push(failedExpectation); } }; Suite.prototype.onMultipleDone = function() { let msg; // Issue a deprecation. Include the context ourselves and pass // ignoreRunnable: true, since getting here always means that we've already // moved on and the current runnable isn't the one that caused the problem. if (this.parentSuite) { msg = "An asynchronous beforeAll or afterAll function called its 'done' " + 'callback more than once.\n' + '(in suite: ' + this.getFullName() + ')'; } else { msg = 'A top-level beforeAll or afterAll function called its ' + "'done' callback more than once."; } this.onLateError(new Error(msg)); }; Suite.prototype.addExpectationResult = function() { if (isFailure(arguments)) { const data = arguments[1]; const expectationResult = j$.buildExpectationResult(data); if (this.reportedDone) { this.onLateError(expectationResult); } else { this.result.failedExpectations.push(expectationResult); // TODO: refactor so that we don't need to override cached status if (this.result.status) { this.result.status = 'failed'; } } if (this.throwOnExpectationFailure) { throw new j$.errors.ExpectationFailed(); } } }; Suite.prototype.addDeprecationWarning = function(deprecation) { if (typeof deprecation === 'string') { deprecation = { message: deprecation }; } this.result.deprecationWarnings.push( j$.buildExpectationResult(deprecation) ); }; Object.defineProperty(Suite.prototype, 'metadata', { get: function() { if (!this.metadata_) { this.metadata_ = new SuiteMetadata(this); } return this.metadata_; } }); /** * @interface Suite * @see Env#topSuite * @since 2.0.0 */ function SuiteMetadata(suite) { this.suite_ = suite; /** * The unique ID of this suite. * @name Suite#id * @readonly * @type {string} * @since 2.0.0 */ this.id = suite.id; /** * The parent of this suite, or null if this is the top suite. * @name Suite#parentSuite * @readonly * @type {Suite} */ this.parentSuite = suite.parentSuite ? suite.parentSuite.metadata : null; /** * The description passed to the {@link describe} that created this suite. * @name Suite#description * @readonly * @type {string} * @since 2.0.0 */ this.description = suite.description; } /** * The full description including all ancestors of this suite. * @name Suite#getFullName * @function * @returns {string} * @since 2.0.0 */ SuiteMetadata.prototype.getFullName = function() { return this.suite_.getFullName(); }; /** * The suite's children. * @name Suite#children * @type {Array.<(Spec|Suite)>} * @since 2.0.0 */ Object.defineProperty(SuiteMetadata.prototype, 'children', { get: function() { return this.suite_.children.map(child => child.metadata); } }); function isFailure(args) { return !args[0]; } return Suite; }; jasmine-4.5.0/src/core/SuiteBuilder.js000066400000000000000000000206671432731766000176470ustar00rootroot00000000000000getJasmineRequireObj().SuiteBuilder = function(j$) { class SuiteBuilder { constructor(options) { this.env_ = options.env; this.expectationFactory_ = options.expectationFactory; this.suiteAsyncExpectationFactory_ = function(actual, suite) { return options.asyncExpectationFactory(actual, suite, 'Suite'); }; this.specAsyncExpectationFactory_ = function(actual, suite) { return options.asyncExpectationFactory(actual, suite, 'Spec'); }; this.onLateError_ = options.onLateError; this.specResultCallback_ = options.specResultCallback; this.specStarted_ = options.specStarted; this.nextSuiteId_ = 0; this.nextSpecId_ = 0; this.topSuite = this.suiteFactory_('Jasmine__TopLevel__Suite'); this.currentDeclarationSuite_ = this.topSuite; this.totalSpecsDefined = 0; this.focusedRunables = []; } describe(description, definitionFn) { ensureIsFunction(definitionFn, 'describe'); const suite = this.suiteFactory_(description); if (definitionFn.length > 0) { throw new Error('describe does not expect any arguments'); } if (this.currentDeclarationSuite_.markedExcluding) { suite.exclude(); } this.addSpecsToSuite_(suite, definitionFn); return suite; } fdescribe(description, definitionFn) { ensureIsFunction(definitionFn, 'fdescribe'); const suite = this.suiteFactory_(description); suite.isFocused = true; this.focusedRunables.push(suite.id); this.unfocusAncestor_(); this.addSpecsToSuite_(suite, definitionFn); return suite; } xdescribe(description, definitionFn) { ensureIsFunction(definitionFn, 'xdescribe'); const suite = this.suiteFactory_(description); suite.exclude(); this.addSpecsToSuite_(suite, definitionFn); return suite; } it(description, fn, timeout) { // it() sometimes doesn't have a fn argument, so only check the type if // it's given. if (arguments.length > 1 && typeof fn !== 'undefined') { ensureIsFunctionOrAsync(fn, 'it'); } return this.it_(description, fn, timeout); } xit(description, fn, timeout) { // xit(), like it(), doesn't always have a fn argument, so only check the // type when needed. if (arguments.length > 1 && typeof fn !== 'undefined') { ensureIsFunctionOrAsync(fn, 'xit'); } const spec = this.it_(description, fn, timeout); spec.exclude('Temporarily disabled with xit'); return spec; } fit(description, fn, timeout) { // Unlike it and xit, the function is required because it doesn't make // sense to focus on nothing. ensureIsFunctionOrAsync(fn, 'fit'); if (timeout) { j$.util.validateTimeout(timeout); } const spec = this.specFactory_(description, fn, timeout); this.currentDeclarationSuite_.addChild(spec); this.focusedRunables.push(spec.id); this.unfocusAncestor_(); return spec; } beforeEach(beforeEachFunction, timeout) { ensureIsFunctionOrAsync(beforeEachFunction, 'beforeEach'); if (timeout) { j$.util.validateTimeout(timeout); } this.currentDeclarationSuite_.beforeEach({ fn: beforeEachFunction, timeout: timeout || 0 }); } beforeAll(beforeAllFunction, timeout) { ensureIsFunctionOrAsync(beforeAllFunction, 'beforeAll'); if (timeout) { j$.util.validateTimeout(timeout); } this.currentDeclarationSuite_.beforeAll({ fn: beforeAllFunction, timeout: timeout || 0 }); } afterEach(afterEachFunction, timeout) { ensureIsFunctionOrAsync(afterEachFunction, 'afterEach'); if (timeout) { j$.util.validateTimeout(timeout); } afterEachFunction.isCleanup = true; this.currentDeclarationSuite_.afterEach({ fn: afterEachFunction, timeout: timeout || 0 }); } afterAll(afterAllFunction, timeout) { ensureIsFunctionOrAsync(afterAllFunction, 'afterAll'); if (timeout) { j$.util.validateTimeout(timeout); } this.currentDeclarationSuite_.afterAll({ fn: afterAllFunction, timeout: timeout || 0 }); } it_(description, fn, timeout) { if (timeout) { j$.util.validateTimeout(timeout); } const spec = this.specFactory_(description, fn, timeout); if (this.currentDeclarationSuite_.markedExcluding) { spec.exclude(); } this.currentDeclarationSuite_.addChild(spec); return spec; } suiteFactory_(description) { const config = this.env_.configuration(); return new j$.Suite({ id: 'suite' + this.nextSuiteId_++, description, parentSuite: this.currentDeclarationSuite_, timer: new j$.Timer(), expectationFactory: this.expectationFactory_, asyncExpectationFactory: this.suiteAsyncExpectationFactory_, throwOnExpectationFailure: config.stopSpecOnExpectationFailure, autoCleanClosures: config.autoCleanClosures, onLateError: this.onLateError_ }); } addSpecsToSuite_(suite, definitionFn) { const parentSuite = this.currentDeclarationSuite_; parentSuite.addChild(suite); this.currentDeclarationSuite_ = suite; let threw = false; try { definitionFn(); } catch (e) { suite.handleException(e); threw = true; } if (suite.parentSuite && !suite.children.length && !threw) { throw new Error( `describe with no children (describe() or it()): ${suite.getFullName()}` ); } this.currentDeclarationSuite_ = parentSuite; } specFactory_(description, fn, timeout) { this.totalSpecsDefined++; const config = this.env_.configuration(); const suite = this.currentDeclarationSuite_; const spec = new j$.Spec({ id: 'spec' + this.nextSpecId_++, beforeAndAfterFns: beforeAndAfterFns(suite), expectationFactory: this.expectationFactory_, asyncExpectationFactory: this.specAsyncExpectationFactory_, onLateError: this.onLateError_, resultCallback: (result, next) => { this.specResultCallback_(spec, result, next); }, getSpecName: function(spec) { return getSpecName(spec, suite); }, onStart: (spec, next) => this.specStarted_(spec, suite, next), description: description, userContext: function() { return suite.clonedSharedUserContext(); }, queueableFn: { fn: fn, timeout: timeout || 0 }, throwOnExpectationFailure: config.stopSpecOnExpectationFailure, autoCleanClosures: config.autoCleanClosures, timer: new j$.Timer() }); return spec; } unfocusAncestor_() { const focusedAncestor = findFocusedAncestor( this.currentDeclarationSuite_ ); if (focusedAncestor) { for (let i = 0; i < this.focusedRunables.length; i++) { if (this.focusedRunables[i] === focusedAncestor) { this.focusedRunables.splice(i, 1); break; } } } } } function findFocusedAncestor(suite) { while (suite) { if (suite.isFocused) { return suite.id; } suite = suite.parentSuite; } return null; } function ensureIsFunction(fn, caller) { if (!j$.isFunction_(fn)) { throw new Error( caller + ' expects a function argument; received ' + j$.getType_(fn) ); } } function ensureIsFunctionOrAsync(fn, caller) { if (!j$.isFunction_(fn) && !j$.isAsyncFunction_(fn)) { throw new Error( caller + ' expects a function argument; received ' + j$.getType_(fn) ); } } function beforeAndAfterFns(targetSuite) { return function() { let befores = [], afters = [], suite = targetSuite; while (suite) { befores = befores.concat(suite.beforeFns); afters = afters.concat(suite.afterFns); suite = suite.parentSuite; } return { befores: befores.reverse(), afters: afters }; }; } function getSpecName(spec, suite) { const fullName = [spec.description], suiteFullName = suite.getFullName(); if (suiteFullName !== '') { fullName.unshift(suiteFullName); } return fullName.join(' '); } return SuiteBuilder; }; jasmine-4.5.0/src/core/Timer.js000066400000000000000000000006711432731766000163200ustar00rootroot00000000000000getJasmineRequireObj().Timer = function() { const defaultNow = (function(Date) { return function() { return new Date().getTime(); }; })(Date); function Timer(options) { options = options || {}; const now = options.now || defaultNow; let startTime; this.start = function() { startTime = now(); }; this.elapsed = function() { return now() - startTime; }; } return Timer; }; jasmine-4.5.0/src/core/TreeProcessor.js000066400000000000000000000160071432731766000200370ustar00rootroot00000000000000getJasmineRequireObj().TreeProcessor = function() { function TreeProcessor(attrs) { const tree = attrs.tree; const runnableIds = attrs.runnableIds; const queueRunnerFactory = attrs.queueRunnerFactory; const nodeStart = attrs.nodeStart || function() {}; const nodeComplete = attrs.nodeComplete || function() {}; const failSpecWithNoExpectations = !!attrs.failSpecWithNoExpectations; const orderChildren = attrs.orderChildren || function(node) { return node.children; }; const excludeNode = attrs.excludeNode || function(node) { return false; }; let stats = { valid: true }; let processed = false; const defaultMin = Infinity; const defaultMax = 1 - Infinity; this.processTree = function() { processNode(tree, true); processed = true; return stats; }; this.execute = async function() { if (!processed) { this.processTree(); } if (!stats.valid) { throw 'invalid order'; } const childFns = wrapChildren(tree, 0); await new Promise(function(resolve) { queueRunnerFactory({ queueableFns: childFns, userContext: tree.sharedUserContext(), onException: function() { tree.handleException.apply(tree, arguments); }, onComplete: resolve, onMultipleDone: tree.onMultipleDone ? tree.onMultipleDone.bind(tree) : null }); }); }; function runnableIndex(id) { for (let i = 0; i < runnableIds.length; i++) { if (runnableIds[i] === id) { return i; } } } function processNode(node, parentExcluded) { const executableIndex = runnableIndex(node.id); if (executableIndex !== undefined) { parentExcluded = false; } if (!node.children) { const excluded = parentExcluded || excludeNode(node); stats[node.id] = { excluded: excluded, willExecute: !excluded && !node.markedPending, segments: [ { index: 0, owner: node, nodes: [node], min: startingMin(executableIndex), max: startingMax(executableIndex) } ] }; } else { let hasExecutableChild = false; const orderedChildren = orderChildren(node); for (let i = 0; i < orderedChildren.length; i++) { const child = orderedChildren[i]; processNode(child, parentExcluded); if (!stats.valid) { return; } const childStats = stats[child.id]; hasExecutableChild = hasExecutableChild || childStats.willExecute; } stats[node.id] = { excluded: parentExcluded, willExecute: hasExecutableChild }; segmentChildren(node, orderedChildren, stats[node.id], executableIndex); if (!node.canBeReentered() && stats[node.id].segments.length > 1) { stats = { valid: false }; } } } function startingMin(executableIndex) { return executableIndex === undefined ? defaultMin : executableIndex; } function startingMax(executableIndex) { return executableIndex === undefined ? defaultMax : executableIndex; } function segmentChildren( node, orderedChildren, nodeStats, executableIndex ) { let currentSegment = { index: 0, owner: node, nodes: [], min: startingMin(executableIndex), max: startingMax(executableIndex) }, result = [currentSegment], lastMax = defaultMax, orderedChildSegments = orderChildSegments(orderedChildren); function isSegmentBoundary(minIndex) { return ( lastMax !== defaultMax && minIndex !== defaultMin && lastMax < minIndex - 1 ); } for (let i = 0; i < orderedChildSegments.length; i++) { const childSegment = orderedChildSegments[i], maxIndex = childSegment.max, minIndex = childSegment.min; if (isSegmentBoundary(minIndex)) { currentSegment = { index: result.length, owner: node, nodes: [], min: defaultMin, max: defaultMax }; result.push(currentSegment); } currentSegment.nodes.push(childSegment); currentSegment.min = Math.min(currentSegment.min, minIndex); currentSegment.max = Math.max(currentSegment.max, maxIndex); lastMax = maxIndex; } nodeStats.segments = result; } function orderChildSegments(children) { const specifiedOrder = [], unspecifiedOrder = []; for (let i = 0; i < children.length; i++) { const child = children[i], segments = stats[child.id].segments; for (let j = 0; j < segments.length; j++) { const seg = segments[j]; if (seg.min === defaultMin) { unspecifiedOrder.push(seg); } else { specifiedOrder.push(seg); } } } specifiedOrder.sort(function(a, b) { return a.min - b.min; }); return specifiedOrder.concat(unspecifiedOrder); } function executeNode(node, segmentNumber) { if (node.children) { return { fn: function(done) { const onStart = { fn: function(next) { nodeStart(node, next); } }; queueRunnerFactory({ onComplete: function() { const args = Array.prototype.slice.call(arguments, [0]); node.cleanupBeforeAfter(); nodeComplete(node, node.getResult(), function() { done.apply(undefined, args); }); }, queueableFns: [onStart].concat(wrapChildren(node, segmentNumber)), userContext: node.sharedUserContext(), onException: function() { node.handleException.apply(node, arguments); }, onMultipleDone: node.onMultipleDone ? node.onMultipleDone.bind(node) : null }); } }; } else { return { fn: function(done) { node.execute( queueRunnerFactory, done, stats[node.id].excluded, failSpecWithNoExpectations ); } }; } } function wrapChildren(node, segmentNumber) { const result = [], segmentChildren = stats[node.id].segments[segmentNumber].nodes; for (let i = 0; i < segmentChildren.length; i++) { result.push( executeNode(segmentChildren[i].owner, segmentChildren[i].index) ); } if (!stats[node.id].willExecute) { return result; } return node.beforeAllFns.concat(result).concat(node.afterAllFns); } } return TreeProcessor; }; jasmine-4.5.0/src/core/UserContext.js000066400000000000000000000005531432731766000175220ustar00rootroot00000000000000getJasmineRequireObj().UserContext = function(j$) { function UserContext() {} UserContext.fromExisting = function(oldContext) { const context = new UserContext(); for (const prop in oldContext) { if (oldContext.hasOwnProperty(prop)) { context[prop] = oldContext[prop]; } } return context; }; return UserContext; }; jasmine-4.5.0/src/core/asymmetric_equality/000077500000000000000000000000001432731766000207705ustar00rootroot00000000000000jasmine-4.5.0/src/core/asymmetric_equality/Any.js000066400000000000000000000023671432731766000220650ustar00rootroot00000000000000getJasmineRequireObj().Any = function(j$) { function Any(expectedObject) { if (typeof expectedObject === 'undefined') { throw new TypeError( 'jasmine.any() expects to be passed a constructor function. ' + 'Please pass one or use jasmine.anything() to match any object.' ); } this.expectedObject = expectedObject; } Any.prototype.asymmetricMatch = function(other) { if (this.expectedObject == String) { return typeof other == 'string' || other instanceof String; } if (this.expectedObject == Number) { return typeof other == 'number' || other instanceof Number; } if (this.expectedObject == Function) { return typeof other == 'function' || other instanceof Function; } if (this.expectedObject == Object) { return other !== null && typeof other == 'object'; } if (this.expectedObject == Boolean) { return typeof other == 'boolean'; } if (typeof Symbol != 'undefined' && this.expectedObject == Symbol) { return typeof other == 'symbol'; } return other instanceof this.expectedObject; }; Any.prototype.jasmineToString = function() { return ''; }; return Any; }; jasmine-4.5.0/src/core/asymmetric_equality/Anything.js000066400000000000000000000004651432731766000231140ustar00rootroot00000000000000getJasmineRequireObj().Anything = function(j$) { function Anything() {} Anything.prototype.asymmetricMatch = function(other) { return !j$.util.isUndefined(other) && other !== null; }; Anything.prototype.jasmineToString = function() { return ''; }; return Anything; }; jasmine-4.5.0/src/core/asymmetric_equality/ArrayContaining.js000066400000000000000000000020411432731766000244130ustar00rootroot00000000000000getJasmineRequireObj().ArrayContaining = function(j$) { function ArrayContaining(sample) { this.sample = sample; } ArrayContaining.prototype.asymmetricMatch = function(other, matchersUtil) { if (!j$.isArray_(this.sample)) { throw new Error( 'You must provide an array to arrayContaining, not ' + j$.basicPrettyPrinter_(this.sample) + '.' ); } // If the actual parameter is not an array, we can fail immediately, since it couldn't // possibly be an "array containing" anything. However, we also want an empty sample // array to match anything, so we need to double-check we aren't in that case if (!j$.isArray_(other) && this.sample.length > 0) { return false; } for (const item of this.sample) { if (!matchersUtil.contains(other, item)) { return false; } } return true; }; ArrayContaining.prototype.jasmineToString = function(pp) { return ''; }; return ArrayContaining; }; jasmine-4.5.0/src/core/asymmetric_equality/ArrayWithExactContents.js000066400000000000000000000015161432731766000257460ustar00rootroot00000000000000getJasmineRequireObj().ArrayWithExactContents = function(j$) { function ArrayWithExactContents(sample) { this.sample = sample; } ArrayWithExactContents.prototype.asymmetricMatch = function( other, matchersUtil ) { if (!j$.isArray_(this.sample)) { throw new Error( 'You must provide an array to arrayWithExactContents, not ' + j$.basicPrettyPrinter_(this.sample) + '.' ); } if (this.sample.length !== other.length) { return false; } for (const item of this.sample) { if (!matchersUtil.contains(other, item)) { return false; } } return true; }; ArrayWithExactContents.prototype.jasmineToString = function(pp) { return ''; }; return ArrayWithExactContents; }; jasmine-4.5.0/src/core/asymmetric_equality/Empty.js000066400000000000000000000010321432731766000224200ustar00rootroot00000000000000getJasmineRequireObj().Empty = function(j$) { function Empty() {} Empty.prototype.asymmetricMatch = function(other) { if (j$.isString_(other) || j$.isArray_(other) || j$.isTypedArray_(other)) { return other.length === 0; } if (j$.isMap(other) || j$.isSet(other)) { return other.size === 0; } if (j$.isObject_(other)) { return Object.keys(other).length === 0; } return false; }; Empty.prototype.jasmineToString = function() { return ''; }; return Empty; }; jasmine-4.5.0/src/core/asymmetric_equality/Falsy.js000066400000000000000000000003741432731766000224100ustar00rootroot00000000000000getJasmineRequireObj().Falsy = function(j$) { function Falsy() {} Falsy.prototype.asymmetricMatch = function(other) { return !other; }; Falsy.prototype.jasmineToString = function() { return ''; }; return Falsy; }; jasmine-4.5.0/src/core/asymmetric_equality/Is.js000066400000000000000000000004621432731766000217030ustar00rootroot00000000000000getJasmineRequireObj().Is = function(j$) { class Is { constructor(expected) { this.expected_ = expected; } asymmetricMatch(actual) { return actual === this.expected_; } jasmineToString(pp) { return ``; } } return Is; }; jasmine-4.5.0/src/core/asymmetric_equality/MapContaining.js000066400000000000000000000020551432731766000240570ustar00rootroot00000000000000getJasmineRequireObj().MapContaining = function(j$) { function MapContaining(sample) { if (!j$.isMap(sample)) { throw new Error( 'You must provide a map to `mapContaining`, not ' + j$.basicPrettyPrinter_(sample) ); } this.sample = sample; } MapContaining.prototype.asymmetricMatch = function(other, matchersUtil) { if (!j$.isMap(other)) return false; for (const [key, value] of this.sample) { // for each key/value pair in `sample` // there should be at least one pair in `other` whose key and value both match let hasMatch = false; for (const [oKey, oValue] of other) { if ( matchersUtil.equals(oKey, key) && matchersUtil.equals(oValue, value) ) { hasMatch = true; break; } } if (!hasMatch) { return false; } } return true; }; MapContaining.prototype.jasmineToString = function(pp) { return ''; }; return MapContaining; }; jasmine-4.5.0/src/core/asymmetric_equality/NotEmpty.js000066400000000000000000000010551432731766000231060ustar00rootroot00000000000000getJasmineRequireObj().NotEmpty = function(j$) { function NotEmpty() {} NotEmpty.prototype.asymmetricMatch = function(other) { if (j$.isString_(other) || j$.isArray_(other) || j$.isTypedArray_(other)) { return other.length !== 0; } if (j$.isMap(other) || j$.isSet(other)) { return other.size !== 0; } if (j$.isObject_(other)) { return Object.keys(other).length !== 0; } return false; }; NotEmpty.prototype.jasmineToString = function() { return ''; }; return NotEmpty; }; jasmine-4.5.0/src/core/asymmetric_equality/ObjectContaining.js000066400000000000000000000031501432731766000245450ustar00rootroot00000000000000getJasmineRequireObj().ObjectContaining = function(j$) { function ObjectContaining(sample) { this.sample = sample; } function hasProperty(obj, property) { if (!obj || typeof obj !== 'object') { return false; } if (Object.prototype.hasOwnProperty.call(obj, property)) { return true; } return hasProperty(Object.getPrototypeOf(obj), property); } ObjectContaining.prototype.asymmetricMatch = function(other, matchersUtil) { if (typeof this.sample !== 'object') { throw new Error( "You must provide an object to objectContaining, not '" + this.sample + "'." ); } if (typeof other !== 'object') { return false; } for (const property in this.sample) { if ( !hasProperty(other, property) || !matchersUtil.equals(this.sample[property], other[property]) ) { return false; } } return true; }; ObjectContaining.prototype.valuesForDiff_ = function(other, pp) { if (!j$.isObject_(other)) { return { self: this.jasmineToString(pp), other: other }; } const filteredOther = {}; Object.keys(this.sample).forEach(function(k) { // eq short-circuits comparison of objects that have different key sets, // so include all keys even if undefined. filteredOther[k] = other[k]; }); return { self: this.sample, other: filteredOther }; }; ObjectContaining.prototype.jasmineToString = function(pp) { return ''; }; return ObjectContaining; }; jasmine-4.5.0/src/core/asymmetric_equality/SetContaining.js000066400000000000000000000020601432731766000240710ustar00rootroot00000000000000getJasmineRequireObj().SetContaining = function(j$) { function SetContaining(sample) { if (!j$.isSet(sample)) { throw new Error( 'You must provide a set to `setContaining`, not ' + j$.basicPrettyPrinter_(sample) ); } this.sample = sample; } SetContaining.prototype.asymmetricMatch = function(other, matchersUtil) { if (!j$.isSet(other)) return false; for (const item of this.sample) { // for each item in `sample` there should be at least one matching item in `other` // (not using `matchersUtil.contains` because it compares set members by reference, // not by deep value equality) let hasMatch = false; for (const oItem of other) { if (matchersUtil.equals(oItem, item)) { hasMatch = true; break; } } if (!hasMatch) { return false; } } return true; }; SetContaining.prototype.jasmineToString = function(pp) { return ''; }; return SetContaining; }; jasmine-4.5.0/src/core/asymmetric_equality/StringContaining.js000066400000000000000000000011751432731766000246120ustar00rootroot00000000000000getJasmineRequireObj().StringContaining = function(j$) { function StringContaining(expected) { if (!j$.isString_(expected)) { throw new Error('Expected is not a String'); } this.expected = expected; } StringContaining.prototype.asymmetricMatch = function(other) { if (!j$.isString_(other)) { // Arrays, etc. don't match no matter what their indexOf returns. return false; } return other.indexOf(this.expected) !== -1; }; StringContaining.prototype.jasmineToString = function() { return ''; }; return StringContaining; }; jasmine-4.5.0/src/core/asymmetric_equality/StringMatching.js000066400000000000000000000010241432731766000242440ustar00rootroot00000000000000getJasmineRequireObj().StringMatching = function(j$) { function StringMatching(expected) { if (!j$.isString_(expected) && !j$.isA_('RegExp', expected)) { throw new Error('Expected is not a String or a RegExp'); } this.regexp = new RegExp(expected); } StringMatching.prototype.asymmetricMatch = function(other) { return this.regexp.test(other); }; StringMatching.prototype.jasmineToString = function() { return ''; }; return StringMatching; }; jasmine-4.5.0/src/core/asymmetric_equality/Truthy.js000066400000000000000000000004031432731766000226220ustar00rootroot00000000000000getJasmineRequireObj().Truthy = function(j$) { function Truthy() {} Truthy.prototype.asymmetricMatch = function(other) { return !!other; }; Truthy.prototype.jasmineToString = function() { return ''; }; return Truthy; }; jasmine-4.5.0/src/core/base.js000066400000000000000000000401751432731766000161550ustar00rootroot00000000000000getJasmineRequireObj().base = function(j$, jasmineGlobal) { /** * Maximum object depth the pretty printer will print to. * Set this to a lower value to speed up pretty printing if you have large objects. * @name jasmine.MAX_PRETTY_PRINT_DEPTH * @default 8 * @since 1.3.0 */ j$.MAX_PRETTY_PRINT_DEPTH = 8; /** * Maximum number of array elements to display when pretty printing objects. * This will also limit the number of keys and values displayed for an object. * Elements past this number will be ellipised. * @name jasmine.MAX_PRETTY_PRINT_ARRAY_LENGTH * @default 50 * @since 2.7.0 */ j$.MAX_PRETTY_PRINT_ARRAY_LENGTH = 50; /** * Maximum number of characters to display when pretty printing objects. * Characters past this number will be ellipised. * @name jasmine.MAX_PRETTY_PRINT_CHARS * @default 100 * @since 2.9.0 */ j$.MAX_PRETTY_PRINT_CHARS = 1000; /** * Default number of milliseconds Jasmine will wait for an asynchronous spec, * before, or after function to complete. This can be overridden on a case by * case basis by passing a time limit as the third argument to {@link it}, * {@link beforeEach}, {@link afterEach}, {@link beforeAll}, or * {@link afterAll}. The value must be no greater than the largest number of * milliseconds supported by setTimeout, which is usually 2147483647. * * While debugging tests, you may want to set this to a large number (or pass * a large number to one of the functions mentioned above) so that Jasmine * does not move on to after functions or the next spec while you're debugging. * @name jasmine.DEFAULT_TIMEOUT_INTERVAL * @default 5000 * @since 1.3.0 */ let DEFAULT_TIMEOUT_INTERVAL = 5000; Object.defineProperty(j$, 'DEFAULT_TIMEOUT_INTERVAL', { get: function() { return DEFAULT_TIMEOUT_INTERVAL; }, set: function(newValue) { j$.util.validateTimeout(newValue, 'jasmine.DEFAULT_TIMEOUT_INTERVAL'); DEFAULT_TIMEOUT_INTERVAL = newValue; } }); j$.getGlobal = function() { return jasmineGlobal; }; /** * Get the currently booted Jasmine Environment. * * @name jasmine.getEnv * @since 1.3.0 * @function * @return {Env} */ j$.getEnv = function(options) { const env = (j$.currentEnv_ = j$.currentEnv_ || new j$.Env(options)); //jasmine. singletons in here (setTimeout blah blah). return env; }; j$.isArray_ = function(value) { return j$.isA_('Array', value); }; j$.isObject_ = function(value) { return ( !j$.util.isUndefined(value) && value !== null && j$.isA_('Object', value) ); }; j$.isString_ = function(value) { return j$.isA_('String', value); }; j$.isNumber_ = function(value) { return j$.isA_('Number', value); }; j$.isFunction_ = function(value) { return j$.isA_('Function', value); }; j$.isAsyncFunction_ = function(value) { return j$.isA_('AsyncFunction', value); }; j$.isGeneratorFunction_ = function(value) { return j$.isA_('GeneratorFunction', value); }; j$.isTypedArray_ = function(value) { return ( j$.isA_('Float32Array', value) || j$.isA_('Float64Array', value) || j$.isA_('Int16Array', value) || j$.isA_('Int32Array', value) || j$.isA_('Int8Array', value) || j$.isA_('Uint16Array', value) || j$.isA_('Uint32Array', value) || j$.isA_('Uint8Array', value) || j$.isA_('Uint8ClampedArray', value) ); }; j$.isA_ = function(typeName, value) { return j$.getType_(value) === '[object ' + typeName + ']'; }; j$.isError_ = function(value) { if (!value) { return false; } if (value instanceof Error) { return true; } return typeof value.stack === 'string' && typeof value.message === 'string'; }; j$.isAsymmetricEqualityTester_ = function(obj) { return obj ? j$.isA_('Function', obj.asymmetricMatch) : false; }; j$.getType_ = function(value) { return Object.prototype.toString.apply(value); }; j$.isDomNode = function(obj) { // Node is a function, because constructors return typeof jasmineGlobal.Node !== 'undefined' ? obj instanceof jasmineGlobal.Node : obj !== null && typeof obj === 'object' && typeof obj.nodeType === 'number' && typeof obj.nodeName === 'string'; // return obj.nodeType > 0; }; j$.isMap = function(obj) { return ( obj !== null && typeof obj !== 'undefined' && obj.constructor === jasmineGlobal.Map ); }; j$.isSet = function(obj) { return ( obj !== null && typeof obj !== 'undefined' && obj.constructor === jasmineGlobal.Set ); }; j$.isWeakMap = function(obj) { return ( obj !== null && typeof obj !== 'undefined' && obj.constructor === jasmineGlobal.WeakMap ); }; j$.isURL = function(obj) { return ( obj !== null && typeof obj !== 'undefined' && obj.constructor === jasmineGlobal.URL ); }; j$.isIterable_ = function(value) { return value && !!value[Symbol.iterator]; }; j$.isDataView = function(obj) { return ( obj !== null && typeof obj !== 'undefined' && obj.constructor === jasmineGlobal.DataView ); }; j$.isPromise = function(obj) { return !!obj && obj.constructor === jasmineGlobal.Promise; }; j$.isPromiseLike = function(obj) { return !!obj && j$.isFunction_(obj.then); }; j$.fnNameFor = function(func) { if (func.name) { return func.name; } const matches = func.toString().match(/^\s*function\s*(\w+)\s*\(/) || func.toString().match(/^\s*\[object\s*(\w+)Constructor\]/); return matches ? matches[1] : ''; }; j$.isPending_ = function(promise) { const sentinel = {}; return Promise.race([promise, Promise.resolve(sentinel)]).then( function(result) { return result === sentinel; }, function() { return false; } ); }; /** * Get an {@link AsymmetricEqualityTester}, usable in any {@link matchers|matcher} that uses Jasmine's equality (e.g. {@link matchers#toEqual|toEqual}, {@link matchers#toContain|toContain}, or {@link matchers#toHaveBeenCalledWith|toHaveBeenCalledWith}), * that will succeed if the actual value being compared is an instance of the specified class/constructor. * @name jasmine.any * @since 1.3.0 * @function * @param {Constructor} clazz - The constructor to check against. */ j$.any = function(clazz) { return new j$.Any(clazz); }; /** * Get an {@link AsymmetricEqualityTester}, usable in any {@link matchers|matcher} that uses Jasmine's equality (e.g. {@link matchers#toEqual|toEqual}, {@link matchers#toContain|toContain}, or {@link matchers#toHaveBeenCalledWith|toHaveBeenCalledWith}), * that will succeed if the actual value being compared is not `null` and not `undefined`. * @name jasmine.anything * @since 2.2.0 * @function */ j$.anything = function() { return new j$.Anything(); }; /** * Get an {@link AsymmetricEqualityTester}, usable in any {@link matchers|matcher} that uses Jasmine's equality (e.g. {@link matchers#toEqual|toEqual}, {@link matchers#toContain|toContain}, or {@link matchers#toHaveBeenCalledWith|toHaveBeenCalledWith}), * that will succeed if the actual value being compared is `true` or anything truthy. * @name jasmine.truthy * @since 3.1.0 * @function */ j$.truthy = function() { return new j$.Truthy(); }; /** * Get an {@link AsymmetricEqualityTester}, usable in any {@link matchers|matcher} that uses Jasmine's equality (e.g. {@link matchers#toEqual|toEqual}, {@link matchers#toContain|toContain}, or {@link matchers#toHaveBeenCalledWith|toHaveBeenCalledWith}), * that will succeed if the actual value being compared is `null`, `undefined`, `0`, `false` or anything falsey. * @name jasmine.falsy * @since 3.1.0 * @function */ j$.falsy = function() { return new j$.Falsy(); }; /** * Get an {@link AsymmetricEqualityTester}, usable in any {@link matchers|matcher} that uses Jasmine's equality (e.g. {@link matchers#toEqual|toEqual}, {@link matchers#toContain|toContain}, or {@link matchers#toHaveBeenCalledWith|toHaveBeenCalledWith}), * that will succeed if the actual value being compared is empty. * @name jasmine.empty * @since 3.1.0 * @function */ j$.empty = function() { return new j$.Empty(); }; /** * Get an {@link AsymmetricEqualityTester}, usable in any {@link matchers|matcher} * that passes if the actual value is the same as the sample as determined * by the `===` operator. * @name jasmine.is * @function * @param {Object} sample - The value to compare the actual to. */ j$.is = function(sample) { return new j$.Is(sample); }; /** * Get an {@link AsymmetricEqualityTester}, usable in any {@link matchers|matcher} that uses Jasmine's equality (e.g. {@link matchers#toEqual|toEqual}, {@link matchers#toContain|toContain}, or {@link matchers#toHaveBeenCalledWith|toHaveBeenCalledWith}), * that will succeed if the actual value being compared is not empty. * @name jasmine.notEmpty * @since 3.1.0 * @function */ j$.notEmpty = function() { return new j$.NotEmpty(); }; /** * Get an {@link AsymmetricEqualityTester}, usable in any {@link matchers|matcher} that uses Jasmine's equality (e.g. {@link matchers#toEqual|toEqual}, {@link matchers#toContain|toContain}, or {@link matchers#toHaveBeenCalledWith|toHaveBeenCalledWith}), * that will succeed if the actual value being compared contains at least the keys and values. * @name jasmine.objectContaining * @since 1.3.0 * @function * @param {Object} sample - The subset of properties that _must_ be in the actual. */ j$.objectContaining = function(sample) { return new j$.ObjectContaining(sample); }; /** * Get an {@link AsymmetricEqualityTester}, usable in any {@link matchers|matcher} that uses Jasmine's equality (e.g. {@link matchers#toEqual|toEqual}, {@link matchers#toContain|toContain}, or {@link matchers#toHaveBeenCalledWith|toHaveBeenCalledWith}), * that will succeed if the actual value is a `String` that matches the `RegExp` or `String`. * @name jasmine.stringMatching * @since 2.2.0 * @function * @param {RegExp|String} expected */ j$.stringMatching = function(expected) { return new j$.StringMatching(expected); }; /** * Get an {@link AsymmetricEqualityTester}, usable in any {@link matchers|matcher} that uses Jasmine's equality (e.g. {@link matchers#toEqual|toEqual}, {@link matchers#toContain|toContain}, or {@link matchers#toHaveBeenCalledWith|toHaveBeenCalledWith}), * that will succeed if the actual value is a `String` that contains the specified `String`. * @name jasmine.stringContaining * @since 3.10.0 * @function * @param {String} expected */ j$.stringContaining = function(expected) { return new j$.StringContaining(expected); }; /** * Get an {@link AsymmetricEqualityTester}, usable in any {@link matchers|matcher} that uses Jasmine's equality (e.g. {@link matchers#toEqual|toEqual}, {@link matchers#toContain|toContain}, or {@link matchers#toHaveBeenCalledWith|toHaveBeenCalledWith}), * that will succeed if the actual value is an `Array` that contains at least the elements in the sample. * @name jasmine.arrayContaining * @since 2.2.0 * @function * @param {Array} sample */ j$.arrayContaining = function(sample) { return new j$.ArrayContaining(sample); }; /** * Get an {@link AsymmetricEqualityTester}, usable in any {@link matchers|matcher} that uses Jasmine's equality (e.g. {@link matchers#toEqual|toEqual}, {@link matchers#toContain|toContain}, or {@link matchers#toHaveBeenCalledWith|toHaveBeenCalledWith}), * that will succeed if the actual value is an `Array` that contains all of the elements in the sample in any order. * @name jasmine.arrayWithExactContents * @since 2.8.0 * @function * @param {Array} sample */ j$.arrayWithExactContents = function(sample) { return new j$.ArrayWithExactContents(sample); }; /** * Get an {@link AsymmetricEqualityTester}, usable in any {@link matchers|matcher} that uses Jasmine's equality (e.g. {@link matchers#toEqual|toEqual}, {@link matchers#toContain|toContain}, or {@link matchers#toHaveBeenCalledWith|toHaveBeenCalledWith}), * that will succeed if every key/value pair in the sample passes the deep equality comparison * with at least one key/value pair in the actual value being compared * @name jasmine.mapContaining * @since 3.5.0 * @function * @param {Map} sample - The subset of items that _must_ be in the actual. */ j$.mapContaining = function(sample) { return new j$.MapContaining(sample); }; /** * Get an {@link AsymmetricEqualityTester}, usable in any {@link matchers|matcher} that uses Jasmine's equality (e.g. {@link matchers#toEqual|toEqual}, {@link matchers#toContain|toContain}, or {@link matchers#toHaveBeenCalledWith|toHaveBeenCalledWith}), * that will succeed if every item in the sample passes the deep equality comparison * with at least one item in the actual value being compared * @name jasmine.setContaining * @since 3.5.0 * @function * @param {Set} sample - The subset of items that _must_ be in the actual. */ j$.setContaining = function(sample) { return new j$.SetContaining(sample); }; /** * Determines whether the provided function is a Jasmine spy. * @name jasmine.isSpy * @since 2.0.0 * @function * @param {Function} putativeSpy - The function to check. * @return {Boolean} */ j$.isSpy = function(putativeSpy) { if (!putativeSpy) { return false; } return ( putativeSpy.and instanceof j$.SpyStrategy && putativeSpy.calls instanceof j$.CallTracker ); }; /** * Logs a message for use in debugging. If the spec fails, trace messages * will be included in the {@link SpecResult|result} passed to the * reporter's specDone method. * * This method should be called only when a spec (including any associated * beforeEach or afterEach functions) is running. * @function * @name jasmine.debugLog * @since 4.0.0 * @param {String} msg - The message to log */ j$.debugLog = function(msg) { j$.getEnv().debugLog(msg); }; /** * Replaces Jasmine's global error handling with a spy. This prevents Jasmine * from treating uncaught exceptions and unhandled promise rejections * as spec failures and allows them to be inspected using the spy's * {@link Spy#calls|calls property} and related matchers such as * {@link matchers#toHaveBeenCalledWith|toHaveBeenCalledWith}. * * After installing the spy, spyOnGlobalErrorsAsync immediately calls its * argument, which must be an async or promise-returning function. The spy * will be passed as the first argument to that callback. Normal error * handling will be restored when the promise returned from the callback is * settled. * * Note: The JavaScript runtime may deliver uncaught error events and unhandled * rejection events asynchronously, especially in browsers. If the event * occurs after the promise returned from the callback is settled, it won't * be routed to the spy even if the underlying error occurred previously. * It's up to you to ensure that the returned promise isn't resolved until * all of the error/rejection events that you want to handle have occurred. * * You must await the return value of spyOnGlobalErrorsAsync. * @name jasmine.spyOnGlobalErrorsAsync * @function * @async * @param {AsyncFunction} fn - A function to run, during which the global error spy will be effective * @example * it('demonstrates global error spies', async function() { * await jasmine.spyOnGlobalErrorsAsync(async function(globalErrorSpy) { * setTimeout(function() { * throw new Error('the expected error'); * }); * await new Promise(function(resolve) { * setTimeout(resolve); * }); * const expected = new Error('the expected error'); * expect(globalErrorSpy).toHaveBeenCalledWith(expected); * }); * }); */ j$.spyOnGlobalErrorsAsync = async function(fn) { await jasmine.getEnv().spyOnGlobalErrorsAsync(fn); }; }; jasmine-4.5.0/src/core/buildExpectationResult.js000066400000000000000000000052531432731766000217430ustar00rootroot00000000000000//TODO: expectation result may make more sense as a presentation of an expectation. getJasmineRequireObj().buildExpectationResult = function(j$) { function buildExpectationResult(options) { const exceptionFormatter = new j$.ExceptionFormatter(); /** * @typedef Expectation * @property {String} matcherName - The name of the matcher that was executed for this expectation. * @property {String} message - The failure message for the expectation. * @property {String} stack - The stack trace for the failure if available. * @property {Boolean} passed - Whether the expectation passed or failed. * @property {Object} expected - If the expectation failed, what was the expected value. * @property {Object} actual - If the expectation failed, what actual value was produced. * @property {String|undefined} globalErrorType - The type of an error that * is reported on the top suite. Valid values are undefined, "afterAll", * "load", "lateExpectation", and "lateError". */ const result = { matcherName: options.matcherName, message: message(), stack: options.omitStackTrace ? '' : stack(), passed: options.passed }; if (!result.passed) { result.expected = options.expected; result.actual = options.actual; if (options.error && !j$.isString_(options.error)) { if ('code' in options.error) { result.code = options.error.code; } if ( options.error.code === 'ERR_ASSERTION' && options.expected === '' && options.actual === '' ) { result.expected = options.error.expected; result.actual = options.error.actual; result.matcherName = 'assert ' + options.error.operator; } } } return result; function message() { if (options.passed) { return 'Passed.'; } else if (options.message) { return options.message; } else if (options.error) { return exceptionFormatter.message(options.error); } return ''; } function stack() { if (options.passed) { return ''; } let error = options.error; if (!error) { if (options.errorForStack) { error = options.errorForStack; } else if (options.stack) { error = options; } else { try { throw new Error(message()); } catch (e) { error = e; } } } // Omit the message from the stack trace because it will be // included elsewhere. return exceptionFormatter.stack(error, { omitMessage: true }); } } return buildExpectationResult; }; jasmine-4.5.0/src/core/errors.js000066400000000000000000000003711432731766000165510ustar00rootroot00000000000000getJasmineRequireObj().errors = function() { function ExpectationFailed() {} ExpectationFailed.prototype = new Error(); ExpectationFailed.prototype.constructor = ExpectationFailed; return { ExpectationFailed: ExpectationFailed }; }; jasmine-4.5.0/src/core/formatErrorMsg.js000066400000000000000000000004441432731766000202070ustar00rootroot00000000000000getJasmineRequireObj().formatErrorMsg = function() { function generateErrorMsg(domain, usage) { const usageDefinition = usage ? '\nUsage: ' + usage : ''; return function errorMsg(msg) { return domain + ' : ' + msg + usageDefinition; }; } return generateErrorMsg; }; jasmine-4.5.0/src/core/matchers/000077500000000000000000000000001432731766000165045ustar00rootroot00000000000000jasmine-4.5.0/src/core/matchers/DiffBuilder.js000066400000000000000000000054711432731766000212300ustar00rootroot00000000000000getJasmineRequireObj().DiffBuilder = function(j$) { class DiffBuilder { constructor(config) { this.prettyPrinter_ = (config || {}).prettyPrinter || j$.makePrettyPrinter(); this.mismatches_ = new j$.MismatchTree(); this.path_ = new j$.ObjectPath(); this.actualRoot_ = undefined; this.expectedRoot_ = undefined; } setRoots(actual, expected) { this.actualRoot_ = actual; this.expectedRoot_ = expected; } recordMismatch(formatter) { this.mismatches_.add(this.path_, formatter); } getMessage() { const messages = []; this.mismatches_.traverse((path, isLeaf, formatter) => { const { actual, expected } = this.dereferencePath_(path); if (formatter) { messages.push(formatter(actual, expected, path, this.prettyPrinter_)); return true; } const actualCustom = this.prettyPrinter_.customFormat_(actual); const expectedCustom = this.prettyPrinter_.customFormat_(expected); const useCustom = !( j$.util.isUndefined(actualCustom) && j$.util.isUndefined(expectedCustom) ); if (useCustom) { messages.push(wrapPrettyPrinted(actualCustom, expectedCustom, path)); return false; // don't recurse further } if (isLeaf) { messages.push(this.defaultFormatter_(actual, expected, path)); } return true; }); return messages.join('\n'); } withPath(pathComponent, block) { const oldPath = this.path_; this.path_ = this.path_.add(pathComponent); block(); this.path_ = oldPath; } dereferencePath_(objectPath) { let actual = this.actualRoot_; let expected = this.expectedRoot_; const handleAsymmetricExpected = () => { if ( j$.isAsymmetricEqualityTester_(expected) && j$.isFunction_(expected.valuesForDiff_) ) { const asymmetricResult = expected.valuesForDiff_( actual, this.prettyPrinter_ ); expected = asymmetricResult.self; actual = asymmetricResult.other; } }; handleAsymmetricExpected(); for (const pc of objectPath.components) { actual = actual[pc]; expected = expected[pc]; handleAsymmetricExpected(); } return { actual: actual, expected: expected }; } defaultFormatter_(actual, expected, path) { return wrapPrettyPrinted( this.prettyPrinter_(actual), this.prettyPrinter_(expected), path ); } } function wrapPrettyPrinted(actual, expected, path) { return ( 'Expected ' + path + (path.depth() ? ' = ' : '') + actual + ' to equal ' + expected + '.' ); } return DiffBuilder; }; jasmine-4.5.0/src/core/matchers/MismatchTree.js000066400000000000000000000030071432731766000214270ustar00rootroot00000000000000getJasmineRequireObj().MismatchTree = function(j$) { /* To be able to apply custom object formatters at all possible levels of an object graph, DiffBuilder needs to be able to know not just where the mismatch occurred but also all ancestors of the mismatched value in both the expected and actual object graphs. MismatchTree maintains that context and provides it via the traverse method. */ class MismatchTree { constructor(path) { this.path = path || new j$.ObjectPath([]); this.formatter = undefined; this.children = []; this.isMismatch = false; } add(path, formatter) { if (path.depth() === 0) { this.formatter = formatter; this.isMismatch = true; } else { const key = path.components[0]; path = path.shift(); let child = this.child(key); if (!child) { child = new MismatchTree(this.path.add(key)); this.children.push(child); } child.add(path, formatter); } } traverse(visit) { const hasChildren = this.children.length > 0; if (this.isMismatch || hasChildren) { if (visit(this.path, !hasChildren, this.formatter)) { for (const child of this.children) { child.traverse(visit); } } } } child(key) { return this.children.find(child => { const pathEls = child.path.components; return pathEls[pathEls.length - 1] === key; }); } } return MismatchTree; }; jasmine-4.5.0/src/core/matchers/NullDiffBuilder.js000066400000000000000000000003541432731766000220560ustar00rootroot00000000000000getJasmineRequireObj().NullDiffBuilder = function(j$) { return function() { return { withPath: function(_, block) { block(); }, setRoots: function() {}, recordMismatch: function() {} }; }; }; jasmine-4.5.0/src/core/matchers/ObjectPath.js000066400000000000000000000016451432731766000210730ustar00rootroot00000000000000getJasmineRequireObj().ObjectPath = function(j$) { class ObjectPath { constructor(components) { this.components = components || []; } toString() { if (this.components.length) { return '$' + this.components.map(formatPropertyAccess).join(''); } else { return ''; } } add(component) { return new ObjectPath(this.components.concat([component])); } shift() { return new ObjectPath(this.components.slice(1)); } depth() { return this.components.length; } } function formatPropertyAccess(prop) { if (typeof prop === 'number' || typeof prop === 'symbol') { return '[' + prop.toString() + ']'; } if (isValidIdentifier(prop)) { return '.' + prop; } return `['${prop}']`; } function isValidIdentifier(string) { return /^[A-Za-z\$_][A-Za-z0-9\$_]*$/.test(string); } return ObjectPath; }; jasmine-4.5.0/src/core/matchers/async/000077500000000000000000000000001432731766000176215ustar00rootroot00000000000000jasmine-4.5.0/src/core/matchers/async/toBePending.js000066400000000000000000000014201432731766000223520ustar00rootroot00000000000000getJasmineRequireObj().toBePending = function(j$) { /** * Expect a promise to be pending, i.e. the promise is neither resolved nor rejected. * @function * @async * @name async-matchers#toBePending * @since 3.6 * @example * await expectAsync(aPromise).toBePending(); */ return function toBePending() { return { compare: function(actual) { if (!j$.isPromiseLike(actual)) { throw new Error('Expected toBePending to be called on a promise.'); } const want = {}; return Promise.race([actual, Promise.resolve(want)]).then( function(got) { return { pass: want === got }; }, function() { return { pass: false }; } ); } }; }; }; jasmine-4.5.0/src/core/matchers/async/toBeRejected.js000066400000000000000000000013321432731766000225150ustar00rootroot00000000000000getJasmineRequireObj().toBeRejected = function(j$) { /** * Expect a promise to be rejected. * @function * @async * @name async-matchers#toBeRejected * @since 3.1.0 * @example * await expectAsync(aPromise).toBeRejected(); * @example * return expectAsync(aPromise).toBeRejected(); */ return function toBeRejected() { return { compare: function(actual) { if (!j$.isPromiseLike(actual)) { throw new Error('Expected toBeRejected to be called on a promise.'); } return actual.then( function() { return { pass: false }; }, function() { return { pass: true }; } ); } }; }; }; jasmine-4.5.0/src/core/matchers/async/toBeRejectedWith.js000066400000000000000000000033511432731766000233540ustar00rootroot00000000000000getJasmineRequireObj().toBeRejectedWith = function(j$) { /** * Expect a promise to be rejected with a value equal to the expected, using deep equality comparison. * @function * @async * @name async-matchers#toBeRejectedWith * @since 3.3.0 * @param {Object} expected - Value that the promise is expected to be rejected with * @example * await expectAsync(aPromise).toBeRejectedWith({prop: 'value'}); * @example * return expectAsync(aPromise).toBeRejectedWith({prop: 'value'}); */ return function toBeRejectedWith(matchersUtil) { return { compare: function(actualPromise, expectedValue) { if (!j$.isPromiseLike(actualPromise)) { throw new Error( 'Expected toBeRejectedWith to be called on a promise.' ); } function prefix(passed) { return ( 'Expected a promise ' + (passed ? 'not ' : '') + 'to be rejected with ' + matchersUtil.pp(expectedValue) ); } return actualPromise.then( function() { return { pass: false, message: prefix(false) + ' but it was resolved.' }; }, function(actualValue) { if (matchersUtil.equals(actualValue, expectedValue)) { return { pass: true, message: prefix(true) + '.' }; } else { return { pass: false, message: prefix(false) + ' but it was rejected with ' + matchersUtil.pp(actualValue) + '.' }; } } ); } }; }; }; jasmine-4.5.0/src/core/matchers/async/toBeRejectedWithError.js000066400000000000000000000064401432731766000243700ustar00rootroot00000000000000getJasmineRequireObj().toBeRejectedWithError = function(j$) { /** * Expect a promise to be rejected with a value matched to the expected * @function * @async * @name async-matchers#toBeRejectedWithError * @since 3.5.0 * @param {Error} [expected] - `Error` constructor the object that was thrown needs to be an instance of. If not provided, `Error` will be used. * @param {RegExp|String} [message] - The message that should be set on the thrown `Error` * @example * await expectAsync(aPromise).toBeRejectedWithError(MyCustomError, 'Error message'); * await expectAsync(aPromise).toBeRejectedWithError(MyCustomError, /Error message/); * await expectAsync(aPromise).toBeRejectedWithError(MyCustomError); * await expectAsync(aPromise).toBeRejectedWithError('Error message'); * return expectAsync(aPromise).toBeRejectedWithError(/Error message/); */ return function toBeRejectedWithError(matchersUtil) { return { compare: function(actualPromise, arg1, arg2) { if (!j$.isPromiseLike(actualPromise)) { throw new Error( 'Expected toBeRejectedWithError to be called on a promise.' ); } const expected = getExpectedFromArgs(arg1, arg2, matchersUtil); return actualPromise.then( function() { return { pass: false, message: 'Expected a promise to be rejected but it was resolved.' }; }, function(actualValue) { return matchError(actualValue, expected, matchersUtil); } ); } }; }; function matchError(actual, expected, matchersUtil) { if (!j$.isError_(actual)) { return fail(expected, 'rejected with ' + matchersUtil.pp(actual)); } if (!(actual instanceof expected.error)) { return fail( expected, 'rejected with type ' + j$.fnNameFor(actual.constructor) ); } const actualMessage = actual.message; if ( actualMessage === expected.message || typeof expected.message === 'undefined' ) { return pass(expected); } if ( expected.message instanceof RegExp && expected.message.test(actualMessage) ) { return pass(expected); } return fail(expected, 'rejected with ' + matchersUtil.pp(actual)); } function pass(expected) { return { pass: true, message: 'Expected a promise not to be rejected with ' + expected.printValue + ', but it was.' }; } function fail(expected, message) { return { pass: false, message: 'Expected a promise to be rejected with ' + expected.printValue + ' but it was ' + message + '.' }; } function getExpectedFromArgs(arg1, arg2, matchersUtil) { let error, message; if (isErrorConstructor(arg1)) { error = arg1; message = arg2; } else { error = Error; message = arg1; } return { error: error, message: message, printValue: j$.fnNameFor(error) + (typeof message === 'undefined' ? '' : ': ' + matchersUtil.pp(message)) }; } function isErrorConstructor(value) { return ( typeof value === 'function' && (value === Error || j$.isError_(value.prototype)) ); } }; jasmine-4.5.0/src/core/matchers/async/toBeResolved.js000066400000000000000000000016701432731766000225600ustar00rootroot00000000000000getJasmineRequireObj().toBeResolved = function(j$) { /** * Expect a promise to be resolved. * @function * @async * @name async-matchers#toBeResolved * @since 3.1.0 * @example * await expectAsync(aPromise).toBeResolved(); * @example * return expectAsync(aPromise).toBeResolved(); */ return function toBeResolved(matchersUtil) { return { compare: function(actual) { if (!j$.isPromiseLike(actual)) { throw new Error('Expected toBeResolved to be called on a promise.'); } return actual.then( function() { return { pass: true }; }, function(e) { return { pass: false, message: 'Expected a promise to be resolved but it was ' + 'rejected with ' + matchersUtil.pp(e) + '.' }; } ); } }; }; }; jasmine-4.5.0/src/core/matchers/async/toBeResolvedTo.js000066400000000000000000000034321432731766000230610ustar00rootroot00000000000000getJasmineRequireObj().toBeResolvedTo = function(j$) { /** * Expect a promise to be resolved to a value equal to the expected, using deep equality comparison. * @function * @async * @name async-matchers#toBeResolvedTo * @since 3.1.0 * @param {Object} expected - Value that the promise is expected to resolve to * @example * await expectAsync(aPromise).toBeResolvedTo({prop: 'value'}); * @example * return expectAsync(aPromise).toBeResolvedTo({prop: 'value'}); */ return function toBeResolvedTo(matchersUtil) { return { compare: function(actualPromise, expectedValue) { if (!j$.isPromiseLike(actualPromise)) { throw new Error('Expected toBeResolvedTo to be called on a promise.'); } function prefix(passed) { return ( 'Expected a promise ' + (passed ? 'not ' : '') + 'to be resolved to ' + matchersUtil.pp(expectedValue) ); } return actualPromise.then( function(actualValue) { if (matchersUtil.equals(actualValue, expectedValue)) { return { pass: true, message: prefix(true) + '.' }; } else { return { pass: false, message: prefix(false) + ' but it was resolved to ' + matchersUtil.pp(actualValue) + '.' }; } }, function(e) { return { pass: false, message: prefix(false) + ' but it was rejected with ' + matchersUtil.pp(e) + '.' }; } ); } }; }; }; jasmine-4.5.0/src/core/matchers/matchersUtil.js000066400000000000000000000457301432731766000215170ustar00rootroot00000000000000getJasmineRequireObj().MatchersUtil = function(j$) { /** * @class MatchersUtil * @classdesc Utilities for use in implementing matchers.
* _Note:_ Do not construct this directly. Jasmine will construct one and * pass it to matchers and asymmetric equality testers. * @hideconstructor */ function MatchersUtil(options) { options = options || {}; this.customTesters_ = options.customTesters || []; /** * Formats a value for use in matcher failure messages and similar contexts, * taking into account the current set of custom value formatters. * @function * @name MatchersUtil#pp * @since 3.6.0 * @param {*} value The value to pretty-print * @return {string} The pretty-printed value */ this.pp = options.pp || function() {}; } /** * Determines whether `haystack` contains `needle`, using the same comparison * logic as {@link MatchersUtil#equals}. * @function * @name MatchersUtil#contains * @since 2.0.0 * @param {*} haystack The collection to search * @param {*} needle The value to search for * @returns {boolean} True if `needle` was found in `haystack` */ MatchersUtil.prototype.contains = function(haystack, needle) { if (!haystack) { return false; } if (j$.isSet(haystack)) { // Try .has() first. It should be faster in cases where // needle === something in haystack. Fall back to .equals() comparison // if that fails. if (haystack.has(needle)) { return true; } } if (j$.isIterable_(haystack) && !j$.isString_(haystack)) { // Arrays, Sets, etc. for (const candidate of haystack) { if (this.equals(candidate, needle)) { return true; } } return false; } if (haystack.indexOf) { // Mainly strings return haystack.indexOf(needle) >= 0; } if (j$.isNumber_(haystack.length)) { // Objects that are shaped like arrays but aren't iterable for (let i = 0; i < haystack.length; i++) { if (this.equals(haystack[i], needle)) { return true; } } } return false; }; MatchersUtil.prototype.buildFailureMessage = function() { const args = Array.prototype.slice.call(arguments, 0), matcherName = args[0], isNot = args[1], actual = args[2], expected = args.slice(3), englishyPredicate = matcherName.replace(/[A-Z]/g, function(s) { return ' ' + s.toLowerCase(); }); let message = 'Expected ' + this.pp(actual) + (isNot ? ' not ' : ' ') + englishyPredicate; if (expected.length > 0) { for (let i = 0; i < expected.length; i++) { if (i > 0) { message += ','; } message += ' ' + this.pp(expected[i]); } } return message + '.'; }; MatchersUtil.prototype.asymmetricDiff_ = function( a, b, aStack, bStack, diffBuilder ) { if (j$.isFunction_(b.valuesForDiff_)) { const values = b.valuesForDiff_(a, this.pp); this.eq_(values.other, values.self, aStack, bStack, diffBuilder); } else { diffBuilder.recordMismatch(); } }; MatchersUtil.prototype.asymmetricMatch_ = function( a, b, aStack, bStack, diffBuilder ) { const asymmetricA = j$.isAsymmetricEqualityTester_(a); const asymmetricB = j$.isAsymmetricEqualityTester_(b); if (asymmetricA === asymmetricB) { return undefined; } let result; if (asymmetricA) { result = a.asymmetricMatch(b, this); if (!result) { diffBuilder.recordMismatch(); } return result; } if (asymmetricB) { result = b.asymmetricMatch(a, this); if (!result) { this.asymmetricDiff_(a, b, aStack, bStack, diffBuilder); } return result; } }; /** * Determines whether two values are deeply equal to each other. * @function * @name MatchersUtil#equals * @since 2.0.0 * @param {*} a The first value to compare * @param {*} b The second value to compare * @returns {boolean} True if the values are equal */ MatchersUtil.prototype.equals = function(a, b, diffBuilder) { diffBuilder = diffBuilder || j$.NullDiffBuilder(); diffBuilder.setRoots(a, b); return this.eq_(a, b, [], [], diffBuilder); }; // Equality function lovingly adapted from isEqual in // [Underscore](http://underscorejs.org) MatchersUtil.prototype.eq_ = function(a, b, aStack, bStack, diffBuilder) { let result = true; const asymmetricResult = this.asymmetricMatch_( a, b, aStack, bStack, diffBuilder ); if (!j$.util.isUndefined(asymmetricResult)) { return asymmetricResult; } for (const tester of this.customTesters_) { const customTesterResult = tester(a, b); if (!j$.util.isUndefined(customTesterResult)) { if (!customTesterResult) { diffBuilder.recordMismatch(); } return customTesterResult; } } if (a instanceof Error && b instanceof Error) { result = a.message == b.message; if (!result) { diffBuilder.recordMismatch(); } return result; } // Identical objects are equal. `0 === -0`, but they aren't identical. // See the [Harmony `egal` proposal](http://wiki.ecmascript.org/doku.php?id=harmony:egal). if (a === b) { result = a !== 0 || 1 / a == 1 / b; if (!result) { diffBuilder.recordMismatch(); } return result; } // A strict comparison is necessary because `null == undefined`. if (a === null || b === null) { result = a === b; if (!result) { diffBuilder.recordMismatch(); } return result; } const className = Object.prototype.toString.call(a); if (className != Object.prototype.toString.call(b)) { diffBuilder.recordMismatch(); return false; } switch (className) { // Strings, numbers, dates, and booleans are compared by value. case '[object String]': // Primitives and their corresponding object wrappers are equivalent; thus, `"5"` is // equivalent to `new String("5")`. result = a == String(b); if (!result) { diffBuilder.recordMismatch(); } return result; case '[object Number]': // `NaN`s are equivalent, but non-reflexive. An `egal` comparison is performed for // other numeric values. result = a != +a ? b != +b : a === 0 && b === 0 ? 1 / a == 1 / b : a == +b; if (!result) { diffBuilder.recordMismatch(); } return result; case '[object Date]': case '[object Boolean]': // Coerce dates and booleans to numeric primitive values. Dates are compared by their // millisecond representations. Note that invalid dates with millisecond representations // of `NaN` are not equivalent. result = +a == +b; if (!result) { diffBuilder.recordMismatch(); } return result; case '[object ArrayBuffer]': // If we have an instance of ArrayBuffer the Uint8Array ctor // will be defined as well return this.eq_( new Uint8Array(a), new Uint8Array(b), aStack, bStack, diffBuilder ); // RegExps are compared by their source patterns and flags. case '[object RegExp]': return ( a.source == b.source && a.global == b.global && a.multiline == b.multiline && a.ignoreCase == b.ignoreCase ); } if (typeof a != 'object' || typeof b != 'object') { diffBuilder.recordMismatch(); return false; } const aIsDomNode = j$.isDomNode(a); const bIsDomNode = j$.isDomNode(b); if (aIsDomNode && bIsDomNode) { // At first try to use DOM3 method isEqualNode result = a.isEqualNode(b); if (!result) { diffBuilder.recordMismatch(); } return result; } if (aIsDomNode || bIsDomNode) { diffBuilder.recordMismatch(); return false; } const aIsPromise = j$.isPromise(a); const bIsPromise = j$.isPromise(b); if (aIsPromise && bIsPromise) { return a === b; } // Assume equality for cyclic structures. The algorithm for detecting cyclic // structures is adapted from ES 5.1 section 15.12.3, abstract operation `JO`. let length = aStack.length; while (length--) { // Linear search. Performance is inversely proportional to the number of // unique nested structures. if (aStack[length] == a) { return bStack[length] == b; } } // Add the first object to the stack of traversed objects. aStack.push(a); bStack.push(b); let size = 0; // Recursively compare objects and arrays. // Compare array lengths to determine if a deep comparison is necessary. if (className == '[object Array]') { const aLength = a.length; const bLength = b.length; diffBuilder.withPath('length', function() { if (aLength !== bLength) { diffBuilder.recordMismatch(); result = false; } }); for (let i = 0; i < aLength || i < bLength; i++) { diffBuilder.withPath(i, () => { if (i >= bLength) { diffBuilder.recordMismatch( actualArrayIsLongerFormatter.bind(null, this.pp) ); result = false; } else { result = this.eq_( i < aLength ? a[i] : void 0, i < bLength ? b[i] : void 0, aStack, bStack, diffBuilder ) && result; } }); } if (!result) { return false; } } else if (j$.isMap(a) && j$.isMap(b)) { if (a.size != b.size) { diffBuilder.recordMismatch(); return false; } const keysA = []; const keysB = []; a.forEach(function(valueA, keyA) { keysA.push(keyA); }); b.forEach(function(valueB, keyB) { keysB.push(keyB); }); // For both sets of keys, check they map to equal values in both maps. // Keep track of corresponding keys (in insertion order) in order to handle asymmetric obj keys. const mapKeys = [keysA, keysB]; const cmpKeys = [keysB, keysA]; for (let i = 0; result && i < mapKeys.length; i++) { const mapIter = mapKeys[i]; const cmpIter = cmpKeys[i]; for (let j = 0; result && j < mapIter.length; j++) { const mapKey = mapIter[j]; const cmpKey = cmpIter[j]; const mapValueA = a.get(mapKey); let mapValueB; // Only use the cmpKey when one of the keys is asymmetric and the corresponding key matches, // otherwise explicitly look up the mapKey in the other Map since we want keys with unique // obj identity (that are otherwise equal) to not match. if ( j$.isAsymmetricEqualityTester_(mapKey) || (j$.isAsymmetricEqualityTester_(cmpKey) && this.eq_(mapKey, cmpKey, aStack, bStack, j$.NullDiffBuilder())) ) { mapValueB = b.get(cmpKey); } else { mapValueB = b.get(mapKey); } result = this.eq_( mapValueA, mapValueB, aStack, bStack, j$.NullDiffBuilder() ); } } if (!result) { diffBuilder.recordMismatch(); return false; } } else if (j$.isSet(a) && j$.isSet(b)) { if (a.size != b.size) { diffBuilder.recordMismatch(); return false; } const valuesA = []; a.forEach(function(valueA) { valuesA.push(valueA); }); const valuesB = []; b.forEach(function(valueB) { valuesB.push(valueB); }); // For both sets, check they are all contained in the other set const setPairs = [[valuesA, valuesB], [valuesB, valuesA]]; const stackPairs = [[aStack, bStack], [bStack, aStack]]; for (let i = 0; result && i < setPairs.length; i++) { const baseValues = setPairs[i][0]; const otherValues = setPairs[i][1]; const baseStack = stackPairs[i][0]; const otherStack = stackPairs[i][1]; // For each value in the base set... for (const baseValue of baseValues) { let found = false; // ... test that it is present in the other set for (let j = 0; !found && j < otherValues.length; j++) { const otherValue = otherValues[j]; const prevStackSize = baseStack.length; // compare by value equality found = this.eq_( baseValue, otherValue, baseStack, otherStack, j$.NullDiffBuilder() ); if (!found && prevStackSize !== baseStack.length) { baseStack.splice(prevStackSize); otherStack.splice(prevStackSize); } } result = result && found; } } if (!result) { diffBuilder.recordMismatch(); return false; } } else if (j$.isURL(a) && j$.isURL(b)) { // URLs have no enumrable properties, so the default object comparison // would consider any two URLs to be equal. return a.toString() === b.toString(); } else { // Objects with different constructors are not equivalent, but `Object`s // or `Array`s from different frames are. const aCtor = a.constructor, bCtor = b.constructor; if ( aCtor !== bCtor && isFunction(aCtor) && isFunction(bCtor) && a instanceof aCtor && b instanceof bCtor && !(aCtor instanceof aCtor && bCtor instanceof bCtor) ) { diffBuilder.recordMismatch( constructorsAreDifferentFormatter.bind(null, this.pp) ); return false; } } // Deep compare objects. const aKeys = MatchersUtil.keys(a, className == '[object Array]'); size = aKeys.length; // Ensure that both objects contain the same number of properties before comparing deep equality. if (MatchersUtil.keys(b, className == '[object Array]').length !== size) { diffBuilder.recordMismatch( objectKeysAreDifferentFormatter.bind(null, this.pp) ); return false; } for (const key of aKeys) { // Deep compare each member if (!j$.util.has(b, key)) { diffBuilder.recordMismatch( objectKeysAreDifferentFormatter.bind(null, this.pp) ); result = false; continue; } diffBuilder.withPath(key, () => { if (!this.eq_(a[key], b[key], aStack, bStack, diffBuilder)) { result = false; } }); } if (!result) { return false; } // Remove the first object from the stack of traversed objects. aStack.pop(); bStack.pop(); return result; }; MatchersUtil.keys = function(obj, isArray) { const allKeys = (function(o) { const keys = []; for (const key in o) { if (j$.util.has(o, key)) { keys.push(key); } } const symbols = Object.getOwnPropertySymbols(o); for (const sym of symbols) { if (o.propertyIsEnumerable(sym)) { keys.push(sym); } } return keys; })(obj); if (!isArray) { return allKeys; } if (allKeys.length === 0) { return allKeys; } const extraKeys = []; for (const k of allKeys) { if (typeof k === 'symbol' || !/^[0-9]+$/.test(k)) { extraKeys.push(k); } } return extraKeys; }; function isFunction(obj) { return typeof obj === 'function'; } // Returns an array of [k, v] pairs for eacch property that's in objA // and not in objB. function extraKeysAndValues(objA, objB) { return MatchersUtil.keys(objA) .filter(key => !j$.util.has(objB, key)) .map(key => [key, objA[key]]); } function objectKeysAreDifferentFormatter(pp, actual, expected, path) { const missingProperties = extraKeysAndValues(expected, actual), extraProperties = extraKeysAndValues(actual, expected), missingPropertiesMessage = formatKeyValuePairs(pp, missingProperties), extraPropertiesMessage = formatKeyValuePairs(pp, extraProperties), messages = []; if (!path.depth()) { path = 'object'; } if (missingPropertiesMessage.length) { messages.push( 'Expected ' + path + ' to have properties' + missingPropertiesMessage ); } if (extraPropertiesMessage.length) { messages.push( 'Expected ' + path + ' not to have properties' + extraPropertiesMessage ); } return messages.join('\n'); } function constructorsAreDifferentFormatter(pp, actual, expected, path) { if (!path.depth()) { path = 'object'; } return ( 'Expected ' + path + ' to be a kind of ' + j$.fnNameFor(expected.constructor) + ', but was ' + pp(actual) + '.' ); } function actualArrayIsLongerFormatter(pp, actual, expected, path) { return ( 'Unexpected ' + path + (path.depth() ? ' = ' : '') + pp(actual) + ' in array.' ); } function formatKeyValuePairs(pp, keyValuePairs) { let formatted = ''; for (const [key, value] of keyValuePairs) { formatted += '\n ' + key.toString() + ': ' + pp(value); } return formatted; } return MatchersUtil; }; /** * @interface AsymmetricEqualityTester * @classdesc An asymmetric equality tester is an object that can match multiple * objects. Examples include jasmine.any() and jasmine.stringMatching(). Jasmine * includes a number of built-in asymmetric equality testers, such as * {@link jasmine.objectContaining}. User-defined asymmetric equality testers are * also supported. * * Asymmetric equality testers work with any matcher, including user-defined * custom matchers, that uses {@link MatchersUtil#equals} or * {@link MatchersUtil#contains}. * * @example * function numberDivisibleBy(divisor) { * return { * asymmetricMatch: function(n) { * return typeof n === 'number' && n % divisor === 0; * }, * jasmineToString: function() { * return ``; * } * }; * } * * const actual = { * n: 2, * otherFields: "don't care" * }; * * expect(actual).toEqual(jasmine.objectContaining({n: numberDivisibleBy(2)})); * @see custom_asymmetric_equality_testers * @since 2.0.0 */ /** * Determines whether a value matches this tester * @function * @name AsymmetricEqualityTester#asymmetricMatch * @param value {any} The value to test * @param matchersUtil {MatchersUtil} utilities for testing equality, etc * @return {Boolean} */ /** * Returns a string representation of this tester to use in matcher failure messages * @function * @name AsymmetricEqualityTester#jasmineToString * @param pp {function} Function that takes a value and returns a pretty-printed representation * @return {String} */ jasmine-4.5.0/src/core/matchers/nothing.js000066400000000000000000000005401432731766000205070ustar00rootroot00000000000000getJasmineRequireObj().nothing = function() { /** * {@link expect} nothing explicitly. * @function * @name matchers#nothing * @since 2.8.0 * @example * expect().nothing(); */ function nothing() { return { compare: function() { return { pass: true }; } }; } return nothing; }; jasmine-4.5.0/src/core/matchers/requireAsyncMatchers.js000066400000000000000000000005771432731766000232140ustar00rootroot00000000000000getJasmineRequireObj().requireAsyncMatchers = function(jRequire, j$) { const availableMatchers = [ 'toBePending', 'toBeResolved', 'toBeRejected', 'toBeResolvedTo', 'toBeRejectedWith', 'toBeRejectedWithError' ], matchers = {}; for (const name of availableMatchers) { matchers[name] = jRequire[name](j$); } return matchers; }; jasmine-4.5.0/src/core/matchers/requireMatchers.js000077500000000000000000000017151432731766000222140ustar00rootroot00000000000000getJasmineRequireObj().requireMatchers = function(jRequire, j$) { const availableMatchers = [ 'nothing', 'toBe', 'toBeCloseTo', 'toBeDefined', 'toBeInstanceOf', 'toBeFalse', 'toBeFalsy', 'toBeGreaterThan', 'toBeGreaterThanOrEqual', 'toBeLessThan', 'toBeLessThanOrEqual', 'toBeNaN', 'toBeNegativeInfinity', 'toBeNull', 'toBePositiveInfinity', 'toBeTrue', 'toBeTruthy', 'toBeUndefined', 'toContain', 'toEqual', 'toHaveSize', 'toHaveBeenCalled', 'toHaveBeenCalledBefore', 'toHaveBeenCalledOnceWith', 'toHaveBeenCalledTimes', 'toHaveBeenCalledWith', 'toHaveClass', 'toHaveSpyInteractions', 'toMatch', 'toThrow', 'toThrowError', 'toThrowMatching' ], matchers = {}; for (const name of availableMatchers) { matchers[name] = jRequire[name](j$); } return matchers; }; jasmine-4.5.0/src/core/matchers/toBe.js000066400000000000000000000015451432731766000177400ustar00rootroot00000000000000getJasmineRequireObj().toBe = function(j$) { /** * {@link expect} the actual value to be `===` to the expected value. * @function * @name matchers#toBe * @since 1.3.0 * @param {Object} expected - The expected value to compare against. * @example * expect(thing).toBe(realThing); */ function toBe(matchersUtil) { const tip = ' Tip: To check for deep equality, use .toEqual() instead of .toBe().'; return { compare: function(actual, expected) { const result = { pass: actual === expected }; if (typeof expected === 'object') { result.message = matchersUtil.buildFailureMessage( 'toBe', result.pass, actual, expected ) + tip; } return result; } }; } return toBe; }; jasmine-4.5.0/src/core/matchers/toBeCloseTo.js000066400000000000000000000026551432731766000212340ustar00rootroot00000000000000getJasmineRequireObj().toBeCloseTo = function() { /** * {@link expect} the actual value to be within a specified precision of the expected value. * @function * @name matchers#toBeCloseTo * @since 1.3.0 * @param {Object} expected - The expected value to compare against. * @param {Number} [precision=2] - The number of decimal points to check. * @example * expect(number).toBeCloseTo(42.2, 3); */ function toBeCloseTo() { return { compare: function(actual, expected, precision) { if (precision !== 0) { precision = precision || 2; } if (expected === null || actual === null) { throw new Error( 'Cannot use toBeCloseTo with null. Arguments evaluated to: ' + 'expect(' + actual + ').toBeCloseTo(' + expected + ').' ); } // Infinity is close to Infinity and -Infinity is close to -Infinity, // regardless of the precision. if (expected === Infinity || expected === -Infinity) { return { pass: actual === expected }; } const pow = Math.pow(10, precision + 1); const delta = Math.abs(expected - actual); const maxDelta = Math.pow(10, -precision) / 2; return { pass: Math.round(delta * pow) <= maxDelta * pow }; } }; } return toBeCloseTo; }; jasmine-4.5.0/src/core/matchers/toBeDefined.js000066400000000000000000000006531432731766000212160ustar00rootroot00000000000000getJasmineRequireObj().toBeDefined = function() { /** * {@link expect} the actual value to be defined. (Not `undefined`) * @function * @name matchers#toBeDefined * @since 1.3.0 * @example * expect(result).toBeDefined(); */ function toBeDefined() { return { compare: function(actual) { return { pass: void 0 !== actual }; } }; } return toBeDefined; }; jasmine-4.5.0/src/core/matchers/toBeFalse.js000066400000000000000000000006161432731766000207110ustar00rootroot00000000000000getJasmineRequireObj().toBeFalse = function() { /** * {@link expect} the actual value to be `false`. * @function * @name matchers#toBeFalse * @since 3.5.0 * @example * expect(result).toBeFalse(); */ function toBeFalse() { return { compare: function(actual) { return { pass: actual === false }; } }; } return toBeFalse; }; jasmine-4.5.0/src/core/matchers/toBeFalsy.js000066400000000000000000000006021432731766000207300ustar00rootroot00000000000000getJasmineRequireObj().toBeFalsy = function() { /** * {@link expect} the actual value to be falsy * @function * @name matchers#toBeFalsy * @since 2.0.0 * @example * expect(result).toBeFalsy(); */ function toBeFalsy() { return { compare: function(actual) { return { pass: !actual }; } }; } return toBeFalsy; }; jasmine-4.5.0/src/core/matchers/toBeGreaterThan.js000066400000000000000000000010161432731766000220560ustar00rootroot00000000000000getJasmineRequireObj().toBeGreaterThan = function() { /** * {@link expect} the actual value to be greater than the expected value. * @function * @name matchers#toBeGreaterThan * @since 2.0.0 * @param {Number} expected - The value to compare against. * @example * expect(result).toBeGreaterThan(3); */ function toBeGreaterThan() { return { compare: function(actual, expected) { return { pass: actual > expected }; } }; } return toBeGreaterThan; }; jasmine-4.5.0/src/core/matchers/toBeGreaterThanOrEqual.js000066400000000000000000000011101432731766000233420ustar00rootroot00000000000000getJasmineRequireObj().toBeGreaterThanOrEqual = function() { /** * {@link expect} the actual value to be greater than or equal to the expected value. * @function * @name matchers#toBeGreaterThanOrEqual * @since 2.0.0 * @param {Number} expected - The expected value to compare against. * @example * expect(result).toBeGreaterThanOrEqual(25); */ function toBeGreaterThanOrEqual() { return { compare: function(actual, expected) { return { pass: actual >= expected }; } }; } return toBeGreaterThanOrEqual; }; jasmine-4.5.0/src/core/matchers/toBeInstanceOf.js000066400000000000000000000033421432731766000217070ustar00rootroot00000000000000getJasmineRequireObj().toBeInstanceOf = function(j$) { const usageError = j$.formatErrorMsg( '', 'expect(value).toBeInstanceOf()' ); /** * {@link expect} the actual to be an instance of the expected class * @function * @name matchers#toBeInstanceOf * @since 3.5.0 * @param {Object} expected - The class or constructor function to check for * @example * expect('foo').toBeInstanceOf(String); * expect(3).toBeInstanceOf(Number); * expect(new Error()).toBeInstanceOf(Error); */ function toBeInstanceOf(matchersUtil) { return { compare: function(actual, expected) { const actualType = actual && actual.constructor ? j$.fnNameFor(actual.constructor) : matchersUtil.pp(actual); const expectedType = expected ? j$.fnNameFor(expected) : matchersUtil.pp(expected); let expectedMatcher; let pass; try { expectedMatcher = new j$.Any(expected); pass = expectedMatcher.asymmetricMatch(actual); } catch (error) { throw new Error( usageError('Expected value is not a constructor function') ); } if (pass) { return { pass: true, message: 'Expected instance of ' + actualType + ' not to be an instance of ' + expectedType }; } else { return { pass: false, message: 'Expected instance of ' + actualType + ' to be an instance of ' + expectedType }; } } }; } return toBeInstanceOf; }; jasmine-4.5.0/src/core/matchers/toBeLessThan.js000066400000000000000000000010051432731766000213710ustar00rootroot00000000000000getJasmineRequireObj().toBeLessThan = function() { /** * {@link expect} the actual value to be less than the expected value. * @function * @name matchers#toBeLessThan * @since 2.0.0 * @param {Number} expected - The expected value to compare against. * @example * expect(result).toBeLessThan(0); */ function toBeLessThan() { return { compare: function(actual, expected) { return { pass: actual < expected }; } }; } return toBeLessThan; }; jasmine-4.5.0/src/core/matchers/toBeLessThanOrEqual.js000066400000000000000000000010671432731766000226720ustar00rootroot00000000000000getJasmineRequireObj().toBeLessThanOrEqual = function() { /** * {@link expect} the actual value to be less than or equal to the expected value. * @function * @name matchers#toBeLessThanOrEqual * @since 2.0.0 * @param {Number} expected - The expected value to compare against. * @example * expect(result).toBeLessThanOrEqual(123); */ function toBeLessThanOrEqual() { return { compare: function(actual, expected) { return { pass: actual <= expected }; } }; } return toBeLessThanOrEqual; }; jasmine-4.5.0/src/core/matchers/toBeNaN.js000066400000000000000000000012621432731766000203310ustar00rootroot00000000000000getJasmineRequireObj().toBeNaN = function(j$) { /** * {@link expect} the actual value to be `NaN` (Not a Number). * @function * @name matchers#toBeNaN * @since 1.3.0 * @example * expect(thing).toBeNaN(); */ function toBeNaN(matchersUtil) { return { compare: function(actual) { const result = { pass: actual !== actual }; if (result.pass) { result.message = 'Expected actual not to be NaN.'; } else { result.message = function() { return 'Expected ' + matchersUtil.pp(actual) + ' to be NaN.'; }; } return result; } }; } return toBeNaN; }; jasmine-4.5.0/src/core/matchers/toBeNegativeInfinity.js000066400000000000000000000014241432731766000231310ustar00rootroot00000000000000getJasmineRequireObj().toBeNegativeInfinity = function(j$) { /** * {@link expect} the actual value to be `-Infinity` (-infinity). * @function * @name matchers#toBeNegativeInfinity * @since 2.6.0 * @example * expect(thing).toBeNegativeInfinity(); */ function toBeNegativeInfinity(matchersUtil) { return { compare: function(actual) { const result = { pass: actual === Number.NEGATIVE_INFINITY }; if (result.pass) { result.message = 'Expected actual not to be -Infinity.'; } else { result.message = function() { return 'Expected ' + matchersUtil.pp(actual) + ' to be -Infinity.'; }; } return result; } }; } return toBeNegativeInfinity; }; jasmine-4.5.0/src/core/matchers/toBeNull.js000066400000000000000000000006071432731766000205710ustar00rootroot00000000000000getJasmineRequireObj().toBeNull = function() { /** * {@link expect} the actual value to be `null`. * @function * @name matchers#toBeNull * @since 1.3.0 * @example * expect(result).toBeNull(); */ function toBeNull() { return { compare: function(actual) { return { pass: actual === null }; } }; } return toBeNull; }; jasmine-4.5.0/src/core/matchers/toBePositiveInfinity.js000066400000000000000000000014201432731766000231650ustar00rootroot00000000000000getJasmineRequireObj().toBePositiveInfinity = function(j$) { /** * {@link expect} the actual value to be `Infinity` (infinity). * @function * @name matchers#toBePositiveInfinity * @since 2.6.0 * @example * expect(thing).toBePositiveInfinity(); */ function toBePositiveInfinity(matchersUtil) { return { compare: function(actual) { const result = { pass: actual === Number.POSITIVE_INFINITY }; if (result.pass) { result.message = 'Expected actual not to be Infinity.'; } else { result.message = function() { return 'Expected ' + matchersUtil.pp(actual) + ' to be Infinity.'; }; } return result; } }; } return toBePositiveInfinity; }; jasmine-4.5.0/src/core/matchers/toBeTrue.js000066400000000000000000000006071432731766000205760ustar00rootroot00000000000000getJasmineRequireObj().toBeTrue = function() { /** * {@link expect} the actual value to be `true`. * @function * @name matchers#toBeTrue * @since 3.5.0 * @example * expect(result).toBeTrue(); */ function toBeTrue() { return { compare: function(actual) { return { pass: actual === true }; } }; } return toBeTrue; }; jasmine-4.5.0/src/core/matchers/toBeTruthy.js000066400000000000000000000006111432731766000211510ustar00rootroot00000000000000getJasmineRequireObj().toBeTruthy = function() { /** * {@link expect} the actual value to be truthy. * @function * @name matchers#toBeTruthy * @since 2.0.0 * @example * expect(thing).toBeTruthy(); */ function toBeTruthy() { return { compare: function(actual) { return { pass: !!actual }; } }; } return toBeTruthy; }; jasmine-4.5.0/src/core/matchers/toBeUndefined.js000066400000000000000000000006471432731766000215640ustar00rootroot00000000000000getJasmineRequireObj().toBeUndefined = function() { /** * {@link expect} the actual value to be `undefined`. * @function * @name matchers#toBeUndefined * @since 1.3.0 * @example * expect(result).toBeUndefined(): */ function toBeUndefined() { return { compare: function(actual) { return { pass: void 0 === actual }; } }; } return toBeUndefined; }; jasmine-4.5.0/src/core/matchers/toContain.js000066400000000000000000000010621432731766000207770ustar00rootroot00000000000000getJasmineRequireObj().toContain = function() { /** * {@link expect} the actual value to contain a specific value. * @function * @name matchers#toContain * @since 2.0.0 * @param {Object} expected - The value to look for. * @example * expect(array).toContain(anElement); * expect(string).toContain(substring); */ function toContain(matchersUtil) { return { compare: function(actual, expected) { return { pass: matchersUtil.contains(actual, expected) }; } }; } return toContain; }; jasmine-4.5.0/src/core/matchers/toEqual.js000066400000000000000000000014611432731766000204560ustar00rootroot00000000000000getJasmineRequireObj().toEqual = function(j$) { /** * {@link expect} the actual value to be equal to the expected, using deep equality comparison. * @function * @name matchers#toEqual * @since 1.3.0 * @param {Object} expected - Expected value * @example * expect(bigObject).toEqual({"foo": ['bar', 'baz']}); */ function toEqual(matchersUtil) { return { compare: function(actual, expected) { const result = { pass: false }, diffBuilder = new j$.DiffBuilder({ prettyPrinter: matchersUtil.pp }); result.pass = matchersUtil.equals(actual, expected, diffBuilder); // TODO: only set error message if test fails result.message = diffBuilder.getMessage(); return result; } }; } return toEqual; }; jasmine-4.5.0/src/core/matchers/toHaveBeenCalled.js000066400000000000000000000022701432731766000221700ustar00rootroot00000000000000getJasmineRequireObj().toHaveBeenCalled = function(j$) { const getErrorMsg = j$.formatErrorMsg( '', 'expect().toHaveBeenCalled()' ); /** * {@link expect} the actual (a {@link Spy}) to have been called. * @function * @name matchers#toHaveBeenCalled * @since 1.3.0 * @example * expect(mySpy).toHaveBeenCalled(); * expect(mySpy).not.toHaveBeenCalled(); */ function toHaveBeenCalled(matchersUtil) { return { compare: function(actual) { const result = {}; if (!j$.isSpy(actual)) { throw new Error( getErrorMsg( 'Expected a spy, but got ' + matchersUtil.pp(actual) + '.' ) ); } if (arguments.length > 1) { throw new Error( getErrorMsg('Does not take arguments, use toHaveBeenCalledWith') ); } result.pass = actual.calls.any(); result.message = result.pass ? 'Expected spy ' + actual.and.identity + ' not to have been called.' : 'Expected spy ' + actual.and.identity + ' to have been called.'; return result; } }; } return toHaveBeenCalled; }; jasmine-4.5.0/src/core/matchers/toHaveBeenCalledBefore.js000066400000000000000000000057261432731766000233240ustar00rootroot00000000000000getJasmineRequireObj().toHaveBeenCalledBefore = function(j$) { const getErrorMsg = j$.formatErrorMsg( '', 'expect().toHaveBeenCalledBefore()' ); /** * {@link expect} the actual value (a {@link Spy}) to have been called before another {@link Spy}. * @function * @name matchers#toHaveBeenCalledBefore * @since 2.6.0 * @param {Spy} expected - {@link Spy} that should have been called after the `actual` {@link Spy}. * @example * expect(mySpy).toHaveBeenCalledBefore(otherSpy); */ function toHaveBeenCalledBefore(matchersUtil) { return { compare: function(firstSpy, latterSpy) { if (!j$.isSpy(firstSpy)) { throw new Error( getErrorMsg( 'Expected a spy, but got ' + matchersUtil.pp(firstSpy) + '.' ) ); } if (!j$.isSpy(latterSpy)) { throw new Error( getErrorMsg( 'Expected a spy, but got ' + matchersUtil.pp(latterSpy) + '.' ) ); } const result = { pass: false }; if (!firstSpy.calls.count()) { result.message = 'Expected spy ' + firstSpy.and.identity + ' to have been called.'; return result; } if (!latterSpy.calls.count()) { result.message = 'Expected spy ' + latterSpy.and.identity + ' to have been called.'; return result; } const latest1stSpyCall = firstSpy.calls.mostRecent().invocationOrder; const first2ndSpyCall = latterSpy.calls.first().invocationOrder; result.pass = latest1stSpyCall < first2ndSpyCall; if (result.pass) { result.message = 'Expected spy ' + firstSpy.and.identity + ' to not have been called before spy ' + latterSpy.and.identity + ', but it was'; } else { const first1stSpyCall = firstSpy.calls.first().invocationOrder; const latest2ndSpyCall = latterSpy.calls.mostRecent().invocationOrder; if (first1stSpyCall < first2ndSpyCall) { result.message = 'Expected latest call to spy ' + firstSpy.and.identity + ' to have been called before first call to spy ' + latterSpy.and.identity + ' (no interleaved calls)'; } else if (latest2ndSpyCall > latest1stSpyCall) { result.message = 'Expected first call to spy ' + latterSpy.and.identity + ' to have been called after latest call to spy ' + firstSpy.and.identity + ' (no interleaved calls)'; } else { result.message = 'Expected spy ' + firstSpy.and.identity + ' to have been called before spy ' + latterSpy.and.identity; } } return result; } }; } return toHaveBeenCalledBefore; }; jasmine-4.5.0/src/core/matchers/toHaveBeenCalledOnceWith.js000066400000000000000000000055701432731766000236370ustar00rootroot00000000000000getJasmineRequireObj().toHaveBeenCalledOnceWith = function(j$) { const getErrorMsg = j$.formatErrorMsg( '', 'expect().toHaveBeenCalledOnceWith(...arguments)' ); /** * {@link expect} the actual (a {@link Spy}) to have been called exactly once, and exactly with the particular arguments. * @function * @name matchers#toHaveBeenCalledOnceWith * @since 3.6.0 * @param {...Object} - The arguments to look for * @example * expect(mySpy).toHaveBeenCalledOnceWith('foo', 'bar', 2); */ function toHaveBeenCalledOnceWith(util) { return { compare: function() { const args = Array.prototype.slice.call(arguments, 0), actual = args[0], expectedArgs = args.slice(1); if (!j$.isSpy(actual)) { throw new Error( getErrorMsg('Expected a spy, but got ' + util.pp(actual) + '.') ); } const prettyPrintedCalls = actual.calls .allArgs() .map(function(argsForCall) { return ' ' + util.pp(argsForCall); }); if ( actual.calls.count() === 1 && util.contains(actual.calls.allArgs(), expectedArgs) ) { return { pass: true, message: 'Expected spy ' + actual.and.identity + ' to have been called 0 times, multiple times, or once, but with arguments different from:\n' + ' ' + util.pp(expectedArgs) + '\n' + 'But the actual call was:\n' + prettyPrintedCalls.join(',\n') + '.\n\n' }; } function getDiffs() { return actual.calls.allArgs().map(function(argsForCall, callIx) { const diffBuilder = new j$.DiffBuilder(); util.equals(argsForCall, expectedArgs, diffBuilder); return diffBuilder.getMessage(); }); } function butString() { switch (actual.calls.count()) { case 0: return 'But it was never called.\n\n'; case 1: return ( 'But the actual call was:\n' + prettyPrintedCalls.join(',\n') + '.\n' + getDiffs().join('\n') + '\n\n' ); default: return ( 'But the actual calls were:\n' + prettyPrintedCalls.join(',\n') + '.\n\n' ); } } return { pass: false, message: 'Expected spy ' + actual.and.identity + ' to have been called only once, and with given args:\n' + ' ' + util.pp(expectedArgs) + '\n' + butString() }; } }; } return toHaveBeenCalledOnceWith; }; jasmine-4.5.0/src/core/matchers/toHaveBeenCalledTimes.js000066400000000000000000000034641432731766000232000ustar00rootroot00000000000000getJasmineRequireObj().toHaveBeenCalledTimes = function(j$) { const getErrorMsg = j$.formatErrorMsg( '', 'expect().toHaveBeenCalledTimes()' ); /** * {@link expect} the actual (a {@link Spy}) to have been called the specified number of times. * @function * @name matchers#toHaveBeenCalledTimes * @since 2.4.0 * @param {Number} expected - The number of invocations to look for. * @example * expect(mySpy).toHaveBeenCalledTimes(3); */ function toHaveBeenCalledTimes(matchersUtil) { return { compare: function(actual, expected) { if (!j$.isSpy(actual)) { throw new Error( getErrorMsg( 'Expected a spy, but got ' + matchersUtil.pp(actual) + '.' ) ); } const args = Array.prototype.slice.call(arguments, 0), result = { pass: false }; if (!j$.isNumber_(expected)) { throw new Error( getErrorMsg( 'The expected times failed is a required argument and must be a number.' ) ); } actual = args[0]; const calls = actual.calls.count(); const timesMessage = expected === 1 ? 'once' : expected + ' times'; result.pass = calls === expected; result.message = result.pass ? 'Expected spy ' + actual.and.identity + ' not to have been called ' + timesMessage + '. It was called ' + calls + ' times.' : 'Expected spy ' + actual.and.identity + ' to have been called ' + timesMessage + '. It was called ' + calls + ' times.'; return result; } }; } return toHaveBeenCalledTimes; }; jasmine-4.5.0/src/core/matchers/toHaveBeenCalledWith.js000066400000000000000000000056441432731766000230340ustar00rootroot00000000000000getJasmineRequireObj().toHaveBeenCalledWith = function(j$) { const getErrorMsg = j$.formatErrorMsg( '', 'expect().toHaveBeenCalledWith(...arguments)' ); /** * {@link expect} the actual (a {@link Spy}) to have been called with particular arguments at least once. * @function * @name matchers#toHaveBeenCalledWith * @since 1.3.0 * @param {...Object} - The arguments to look for * @example * expect(mySpy).toHaveBeenCalledWith('foo', 'bar', 2); */ function toHaveBeenCalledWith(matchersUtil) { return { compare: function() { const args = Array.prototype.slice.call(arguments, 0), actual = args[0], expectedArgs = args.slice(1), result = { pass: false }; if (!j$.isSpy(actual)) { throw new Error( getErrorMsg( 'Expected a spy, but got ' + matchersUtil.pp(actual) + '.' ) ); } if (!actual.calls.any()) { result.message = function() { return ( 'Expected spy ' + actual.and.identity + ' to have been called with:\n' + ' ' + matchersUtil.pp(expectedArgs) + '\nbut it was never called.' ); }; return result; } if (matchersUtil.contains(actual.calls.allArgs(), expectedArgs)) { result.pass = true; result.message = function() { return ( 'Expected spy ' + actual.and.identity + ' not to have been called with:\n' + ' ' + matchersUtil.pp(expectedArgs) + '\nbut it was.' ); }; } else { result.message = function() { const prettyPrintedCalls = actual.calls .allArgs() .map(function(argsForCall) { return ' ' + matchersUtil.pp(argsForCall); }); const diffs = actual.calls .allArgs() .map(function(argsForCall, callIx) { const diffBuilder = new j$.DiffBuilder(); matchersUtil.equals(argsForCall, expectedArgs, diffBuilder); return ( 'Call ' + callIx + ':\n' + diffBuilder.getMessage().replace(/^/gm, ' ') ); }); return ( 'Expected spy ' + actual.and.identity + ' to have been called with:\n' + ' ' + matchersUtil.pp(expectedArgs) + '\n' + '' + 'but actual calls were:\n' + prettyPrintedCalls.join(',\n') + '.\n\n' + diffs.join('\n') ); }; } return result; } }; } return toHaveBeenCalledWith; }; jasmine-4.5.0/src/core/matchers/toHaveClass.js000066400000000000000000000015671432731766000212670ustar00rootroot00000000000000getJasmineRequireObj().toHaveClass = function(j$) { /** * {@link expect} the actual value to be a DOM element that has the expected class * @function * @name matchers#toHaveClass * @since 3.0.0 * @param {Object} expected - The class name to test for * @example * const el = document.createElement('div'); * el.className = 'foo bar baz'; * expect(el).toHaveClass('bar'); */ function toHaveClass(matchersUtil) { return { compare: function(actual, expected) { if (!isElement(actual)) { throw new Error(matchersUtil.pp(actual) + ' is not a DOM element'); } return { pass: actual.classList.contains(expected) }; } }; } function isElement(maybeEl) { return ( maybeEl && maybeEl.classList && j$.isFunction_(maybeEl.classList.contains) ); } return toHaveClass; }; jasmine-4.5.0/src/core/matchers/toHaveSize.js000066400000000000000000000024211432731766000211220ustar00rootroot00000000000000getJasmineRequireObj().toHaveSize = function(j$) { /** * {@link expect} the actual size to be equal to the expected, using array-like length or object keys size. * @function * @name matchers#toHaveSize * @since 3.6.0 * @param {Object} expected - Expected size * @example * array = [1,2]; * expect(array).toHaveSize(2); */ function toHaveSize() { return { compare: function(actual, expected) { const result = { pass: false }; if ( j$.isA_('WeakSet', actual) || j$.isWeakMap(actual) || j$.isDataView(actual) ) { throw new Error('Cannot get size of ' + actual + '.'); } if (j$.isSet(actual) || j$.isMap(actual)) { result.pass = actual.size === expected; } else if (isLength(actual.length)) { result.pass = actual.length === expected; } else { result.pass = Object.keys(actual).length === expected; } return result; } }; } const MAX_SAFE_INTEGER = Number.MAX_SAFE_INTEGER || 9007199254740991; function isLength(value) { return ( typeof value == 'number' && value > -1 && value % 1 === 0 && value <= MAX_SAFE_INTEGER ); } return toHaveSize; }; jasmine-4.5.0/src/core/matchers/toHaveSpyInteractions.js000077500000000000000000000040441432731766000233540ustar00rootroot00000000000000getJasmineRequireObj().toHaveSpyInteractions = function(j$) { const getErrorMsg = j$.formatErrorMsg( '', 'expect().toHaveSpyInteractions()' ); /** * {@link expect} the actual (a {@link SpyObj}) spies to have been called. * @function * @name matchers#toHaveSpyInteractions * @since 4.1.0 * @example * expect(mySpyObj).toHaveSpyInteractions(); * expect(mySpyObj).not.toHaveSpyInteractions(); */ function toHaveSpyInteractions(matchersUtil) { return { compare: function(actual) { const result = {}; if (!j$.isObject_(actual)) { throw new Error( getErrorMsg('Expected a spy object, but got ' + typeof actual + '.') ); } if (arguments.length > 1) { throw new Error(getErrorMsg('Does not take arguments')); } result.pass = false; let hasSpy = false; const calledSpies = []; for (const spy of Object.values(actual)) { if (!j$.isSpy(spy)) continue; hasSpy = true; if (spy.calls.any()) { result.pass = true; calledSpies.push([spy.and.identity, spy.calls.count()]); } } if (!hasSpy) { throw new Error( getErrorMsg( 'Expected a spy object with spies, but object has no spies.' ) ); } let resultMessage; if (result.pass) { resultMessage = 'Expected spy object spies not to have been called, ' + 'but the following spies were called: '; resultMessage += calledSpies .map(([spyName, spyCount]) => { return `${spyName} called ${spyCount} time(s)`; }) .join(', '); } else { resultMessage = 'Expected spy object spies to have been called, ' + 'but no spies were called.'; } result.message = resultMessage; return result; } }; } return toHaveSpyInteractions; }; jasmine-4.5.0/src/core/matchers/toMatch.js000066400000000000000000000015561432731766000204500ustar00rootroot00000000000000getJasmineRequireObj().toMatch = function(j$) { const getErrorMsg = j$.formatErrorMsg( '', 'expect().toMatch( || )' ); /** * {@link expect} the actual value to match a regular expression * @function * @name matchers#toMatch * @since 1.3.0 * @param {RegExp|String} expected - Value to look for in the string. * @example * expect("my string").toMatch(/string$/); * expect("other string").toMatch("her"); */ function toMatch() { return { compare: function(actual, expected) { if (!j$.isString_(expected) && !j$.isA_('RegExp', expected)) { throw new Error(getErrorMsg('Expected is not a String or a RegExp')); } const regexp = new RegExp(expected); return { pass: regexp.test(actual) }; } }; } return toMatch; }; jasmine-4.5.0/src/core/matchers/toThrow.js000066400000000000000000000040141432731766000205070ustar00rootroot00000000000000getJasmineRequireObj().toThrow = function(j$) { const getErrorMsg = j$.formatErrorMsg( '', 'expect(function() {}).toThrow()' ); /** * {@link expect} a function to `throw` something. * @function * @name matchers#toThrow * @since 2.0.0 * @param {Object} [expected] - Value that should be thrown. If not provided, simply the fact that something was thrown will be checked. * @example * expect(function() { return 'things'; }).toThrow('foo'); * expect(function() { return 'stuff'; }).toThrow(); */ function toThrow(matchersUtil) { return { compare: function(actual, expected) { const result = { pass: false }; let threw = false; let thrown; if (typeof actual != 'function') { throw new Error(getErrorMsg('Actual is not a Function')); } try { actual(); } catch (e) { threw = true; thrown = e; } if (!threw) { result.message = 'Expected function to throw an exception.'; return result; } if (arguments.length == 1) { result.pass = true; result.message = function() { return ( 'Expected function not to throw, but it threw ' + matchersUtil.pp(thrown) + '.' ); }; return result; } if (matchersUtil.equals(thrown, expected)) { result.pass = true; result.message = function() { return ( 'Expected function not to throw ' + matchersUtil.pp(expected) + '.' ); }; } else { result.message = function() { return ( 'Expected function to throw ' + matchersUtil.pp(expected) + ', but it threw ' + matchersUtil.pp(thrown) + '.' ); }; } return result; } }; } return toThrow; }; jasmine-4.5.0/src/core/matchers/toThrowError.js000066400000000000000000000125151432731766000215260ustar00rootroot00000000000000getJasmineRequireObj().toThrowError = function(j$) { const getErrorMsg = j$.formatErrorMsg( '', 'expect(function() {}).toThrowError(, )' ); /** * {@link expect} a function to `throw` an `Error`. * @function * @name matchers#toThrowError * @since 2.0.0 * @param {Error} [expected] - `Error` constructor the object that was thrown needs to be an instance of. If not provided, `Error` will be used. * @param {RegExp|String} [message] - The message that should be set on the thrown `Error` * @example * expect(function() { return 'things'; }).toThrowError(MyCustomError, 'message'); * expect(function() { return 'things'; }).toThrowError(MyCustomError, /bar/); * expect(function() { return 'stuff'; }).toThrowError(MyCustomError); * expect(function() { return 'other'; }).toThrowError(/foo/); * expect(function() { return 'other'; }).toThrowError(); */ function toThrowError(matchersUtil) { return { compare: function(actual) { const errorMatcher = getMatcher.apply(null, arguments); if (typeof actual != 'function') { throw new Error(getErrorMsg('Actual is not a Function')); } let thrown; try { actual(); return fail('Expected function to throw an Error.'); } catch (e) { thrown = e; } if (!j$.isError_(thrown)) { return fail(function() { return ( 'Expected function to throw an Error, but it threw ' + matchersUtil.pp(thrown) + '.' ); }); } return errorMatcher.match(thrown); } }; function getMatcher() { let expected, errorType; if (arguments[2]) { errorType = arguments[1]; expected = arguments[2]; if (!isAnErrorType(errorType)) { throw new Error(getErrorMsg('Expected error type is not an Error.')); } return exactMatcher(expected, errorType); } else if (arguments[1]) { expected = arguments[1]; if (isAnErrorType(arguments[1])) { return exactMatcher(null, arguments[1]); } else { return exactMatcher(arguments[1], null); } } else { return anyMatcher(); } } function anyMatcher() { return { match: function(error) { return pass( 'Expected function not to throw an Error, but it threw ' + j$.fnNameFor(error) + '.' ); } }; } function exactMatcher(expected, errorType) { if (expected && !isStringOrRegExp(expected)) { if (errorType) { throw new Error( getErrorMsg('Expected error message is not a string or RegExp.') ); } else { throw new Error( getErrorMsg('Expected is not an Error, string, or RegExp.') ); } } function messageMatch(message) { if (typeof expected == 'string') { return expected == message; } else { return expected.test(message); } } const errorTypeDescription = errorType ? j$.fnNameFor(errorType) : 'an exception'; function thrownDescription(thrown) { const thrownName = errorType ? j$.fnNameFor(thrown.constructor) : 'an exception'; let thrownMessage = ''; if (expected) { thrownMessage = ' with message ' + matchersUtil.pp(thrown.message); } return thrownName + thrownMessage; } function messageDescription() { if (expected === null) { return ''; } else if (expected instanceof RegExp) { return ' with a message matching ' + matchersUtil.pp(expected); } else { return ' with message ' + matchersUtil.pp(expected); } } function matches(error) { return ( (errorType === null || error instanceof errorType) && (expected === null || messageMatch(error.message)) ); } return { match: function(thrown) { if (matches(thrown)) { return pass(function() { return ( 'Expected function not to throw ' + errorTypeDescription + messageDescription() + '.' ); }); } else { return fail(function() { return ( 'Expected function to throw ' + errorTypeDescription + messageDescription() + ', but it threw ' + thrownDescription(thrown) + '.' ); }); } } }; } function isStringOrRegExp(potential) { return potential instanceof RegExp || typeof potential == 'string'; } function isAnErrorType(type) { if (typeof type !== 'function') { return false; } const Surrogate = function() {}; Surrogate.prototype = type.prototype; return j$.isError_(new Surrogate()); } } function pass(message) { return { pass: true, message: message }; } function fail(message) { return { pass: false, message: message }; } return toThrowError; }; jasmine-4.5.0/src/core/matchers/toThrowMatching.js000066400000000000000000000041021432731766000221600ustar00rootroot00000000000000getJasmineRequireObj().toThrowMatching = function(j$) { const usageError = j$.formatErrorMsg( '', 'expect(function() {}).toThrowMatching()' ); /** * {@link expect} a function to `throw` something matching a predicate. * @function * @name matchers#toThrowMatching * @since 3.0.0 * @param {Function} predicate - A function that takes the thrown exception as its parameter and returns true if it matches. * @example * expect(function() { throw new Error('nope'); }).toThrowMatching(function(thrown) { return thrown.message === 'nope'; }); */ function toThrowMatching(matchersUtil) { return { compare: function(actual, predicate) { if (typeof actual !== 'function') { throw new Error(usageError('Actual is not a Function')); } if (typeof predicate !== 'function') { throw new Error(usageError('Predicate is not a Function')); } let thrown; try { actual(); return fail('Expected function to throw an exception.'); } catch (e) { thrown = e; } if (predicate(thrown)) { return pass( 'Expected function not to throw an exception matching a predicate.' ); } else { return fail(function() { return ( 'Expected function to throw an exception matching a predicate, ' + 'but it threw ' + thrownDescription(thrown) + '.' ); }); } } }; function thrownDescription(thrown) { if (thrown && thrown.constructor) { return ( j$.fnNameFor(thrown.constructor) + ' with message ' + matchersUtil.pp(thrown.message) ); } else { return matchersUtil.pp(thrown); } } } function pass(message) { return { pass: true, message: message }; } function fail(message) { return { pass: false, message: message }; } return toThrowMatching; }; jasmine-4.5.0/src/core/requireCore.js000066400000000000000000000073261432731766000175310ustar00rootroot00000000000000// eslint-disable-next-line no-unused-vars,no-var var getJasmineRequireObj = (function(jasmineGlobal) { let jasmineRequire; if ( typeof module !== 'undefined' && module.exports && typeof exports !== 'undefined' ) { if (typeof global !== 'undefined') { jasmineGlobal = global; } else { jasmineGlobal = {}; } jasmineRequire = exports; } else { if ( typeof window !== 'undefined' && typeof window.toString === 'function' && window.toString() === '[object GjsGlobal]' ) { jasmineGlobal = window; } jasmineRequire = jasmineGlobal.jasmineRequire = {}; } function getJasmineRequire() { return jasmineRequire; } getJasmineRequire().core = function(jRequire) { const j$ = {}; jRequire.base(j$, jasmineGlobal); j$.util = jRequire.util(j$); j$.errors = jRequire.errors(); j$.formatErrorMsg = jRequire.formatErrorMsg(); j$.Any = jRequire.Any(j$); j$.Anything = jRequire.Anything(j$); j$.CallTracker = jRequire.CallTracker(j$); j$.MockDate = jRequire.MockDate(j$); j$.getClearStack = jRequire.clearStack(j$); j$.Clock = jRequire.Clock(); j$.DelayedFunctionScheduler = jRequire.DelayedFunctionScheduler(j$); j$.Deprecator = jRequire.Deprecator(j$); j$.Env = jRequire.Env(j$); j$.StackTrace = jRequire.StackTrace(j$); j$.ExceptionFormatter = jRequire.ExceptionFormatter(j$); j$.ExpectationFilterChain = jRequire.ExpectationFilterChain(); j$.Expector = jRequire.Expector(j$); j$.Expectation = jRequire.Expectation(j$); j$.buildExpectationResult = jRequire.buildExpectationResult(j$); j$.JsApiReporter = jRequire.JsApiReporter(j$); j$.makePrettyPrinter = jRequire.makePrettyPrinter(j$); j$.basicPrettyPrinter_ = j$.makePrettyPrinter(); j$.MatchersUtil = jRequire.MatchersUtil(j$); j$.ObjectContaining = jRequire.ObjectContaining(j$); j$.ArrayContaining = jRequire.ArrayContaining(j$); j$.ArrayWithExactContents = jRequire.ArrayWithExactContents(j$); j$.MapContaining = jRequire.MapContaining(j$); j$.SetContaining = jRequire.SetContaining(j$); j$.QueueRunner = jRequire.QueueRunner(j$); j$.NeverSkipPolicy = jRequire.NeverSkipPolicy(j$); j$.SkipAfterBeforeAllErrorPolicy = jRequire.SkipAfterBeforeAllErrorPolicy( j$ ); j$.CompleteOnFirstErrorSkipPolicy = jRequire.CompleteOnFirstErrorSkipPolicy( j$ ); j$.ReportDispatcher = jRequire.ReportDispatcher(j$); j$.RunableResources = jRequire.RunableResources(j$); j$.Runner = jRequire.Runner(j$); j$.Spec = jRequire.Spec(j$); j$.Spy = jRequire.Spy(j$); j$.SpyFactory = jRequire.SpyFactory(j$); j$.SpyRegistry = jRequire.SpyRegistry(j$); j$.SpyStrategy = jRequire.SpyStrategy(j$); j$.StringMatching = jRequire.StringMatching(j$); j$.StringContaining = jRequire.StringContaining(j$); j$.UserContext = jRequire.UserContext(j$); j$.Suite = jRequire.Suite(j$); j$.SuiteBuilder = jRequire.SuiteBuilder(j$); j$.Timer = jRequire.Timer(); j$.TreeProcessor = jRequire.TreeProcessor(); j$.version = jRequire.version(); j$.Order = jRequire.Order(); j$.DiffBuilder = jRequire.DiffBuilder(j$); j$.NullDiffBuilder = jRequire.NullDiffBuilder(j$); j$.ObjectPath = jRequire.ObjectPath(j$); j$.MismatchTree = jRequire.MismatchTree(j$); j$.GlobalErrors = jRequire.GlobalErrors(j$); j$.Truthy = jRequire.Truthy(j$); j$.Falsy = jRequire.Falsy(j$); j$.Empty = jRequire.Empty(j$); j$.NotEmpty = jRequire.NotEmpty(j$); j$.Is = jRequire.Is(j$); j$.matchers = jRequire.requireMatchers(jRequire, j$); j$.asyncMatchers = jRequire.requireAsyncMatchers(jRequire, j$); return j$; }; return getJasmineRequire; })(this); jasmine-4.5.0/src/core/requireInterface.js000066400000000000000000000363721432731766000205440ustar00rootroot00000000000000getJasmineRequireObj().interface = function(jasmine, env) { const jasmineInterface = { /** * Callback passed to parts of the Jasmine base interface. * * By default Jasmine assumes this function completes synchronously. * If you have code that you need to test asynchronously, you can declare that you receive a `done` callback, return a Promise, or use the `async` keyword if it is supported in your environment. * @callback implementationCallback * @param {Function} [done] Used to specify to Jasmine that this callback is asynchronous and Jasmine should wait until it has been called before moving on. * @returns {} Optionally return a Promise instead of using `done` to cause Jasmine to wait for completion. */ /** * Create a group of specs (often called a suite). * * Calls to `describe` can be nested within other calls to compose your suite as a tree. * @name describe * @since 1.3.0 * @function * @global * @param {String} description Textual description of the group * @param {Function} specDefinitions Function for Jasmine to invoke that will define inner suites and specs */ describe: function(description, specDefinitions) { return env.describe(description, specDefinitions); }, /** * A temporarily disabled [`describe`]{@link describe} * * Specs within an `xdescribe` will be marked pending and not executed * @name xdescribe * @since 1.3.0 * @function * @global * @param {String} description Textual description of the group * @param {Function} specDefinitions Function for Jasmine to invoke that will define inner suites and specs */ xdescribe: function(description, specDefinitions) { return env.xdescribe(description, specDefinitions); }, /** * A focused [`describe`]{@link describe} * * If suites or specs are focused, only those that are focused will be executed * @see fit * @name fdescribe * @since 2.1.0 * @function * @global * @param {String} description Textual description of the group * @param {Function} specDefinitions Function for Jasmine to invoke that will define inner suites and specs */ fdescribe: function(description, specDefinitions) { return env.fdescribe(description, specDefinitions); }, /** * Define a single spec. A spec should contain one or more {@link expect|expectations} that test the state of the code. * * A spec whose expectations all succeed will be passing and a spec with any failures will fail. * The name `it` is a pronoun for the test target, not an abbreviation of anything. It makes the * spec more readable by connecting the function name `it` and the argument `description` as a * complete sentence. * @name it * @since 1.3.0 * @function * @global * @param {String} description Textual description of what this spec is checking * @param {implementationCallback} [testFunction] Function that contains the code of your test. If not provided the test will be `pending`. * @param {Int} [timeout={@link jasmine.DEFAULT_TIMEOUT_INTERVAL}] Custom timeout for an async spec. * @see async */ it: function() { return env.it.apply(env, arguments); }, /** * A temporarily disabled [`it`]{@link it} * * The spec will report as `pending` and will not be executed. * @name xit * @since 1.3.0 * @function * @global * @param {String} description Textual description of what this spec is checking. * @param {implementationCallback} [testFunction] Function that contains the code of your test. Will not be executed. */ xit: function() { return env.xit.apply(env, arguments); }, /** * A focused [`it`]{@link it} * * If suites or specs are focused, only those that are focused will be executed. * @name fit * @since 2.1.0 * @function * @global * @param {String} description Textual description of what this spec is checking. * @param {implementationCallback} testFunction Function that contains the code of your test. * @param {Int} [timeout={@link jasmine.DEFAULT_TIMEOUT_INTERVAL}] Custom timeout for an async spec. * @see async */ fit: function() { return env.fit.apply(env, arguments); }, /** * Run some shared setup before each of the specs in the {@link describe} in which it is called. * @name beforeEach * @since 1.3.0 * @function * @global * @param {implementationCallback} [function] Function that contains the code to setup your specs. * @param {Int} [timeout={@link jasmine.DEFAULT_TIMEOUT_INTERVAL}] Custom timeout for an async beforeEach. * @see async */ beforeEach: function() { return env.beforeEach.apply(env, arguments); }, /** * Run some shared teardown after each of the specs in the {@link describe} in which it is called. * @name afterEach * @since 1.3.0 * @function * @global * @param {implementationCallback} [function] Function that contains the code to teardown your specs. * @param {Int} [timeout={@link jasmine.DEFAULT_TIMEOUT_INTERVAL}] Custom timeout for an async afterEach. * @see async */ afterEach: function() { return env.afterEach.apply(env, arguments); }, /** * Run some shared setup once before all of the specs in the {@link describe} are run. * * _Note:_ Be careful, sharing the setup from a beforeAll makes it easy to accidentally leak state between your specs so that they erroneously pass or fail. * @name beforeAll * @since 2.1.0 * @function * @global * @param {implementationCallback} [function] Function that contains the code to setup your specs. * @param {Int} [timeout={@link jasmine.DEFAULT_TIMEOUT_INTERVAL}] Custom timeout for an async beforeAll. * @see async */ beforeAll: function() { return env.beforeAll.apply(env, arguments); }, /** * Run some shared teardown once after all of the specs in the {@link describe} are run. * * _Note:_ Be careful, sharing the teardown from a afterAll makes it easy to accidentally leak state between your specs so that they erroneously pass or fail. * @name afterAll * @since 2.1.0 * @function * @global * @param {implementationCallback} [function] Function that contains the code to teardown your specs. * @param {Int} [timeout={@link jasmine.DEFAULT_TIMEOUT_INTERVAL}] Custom timeout for an async afterAll. * @see async */ afterAll: function() { return env.afterAll.apply(env, arguments); }, /** * Sets a user-defined property that will be provided to reporters as part of the properties field of {@link SpecResult} * @name setSpecProperty * @since 3.6.0 * @function * @param {String} key The name of the property * @param {*} value The value of the property */ setSpecProperty: function(key, value) { return env.setSpecProperty(key, value); }, /** * Sets a user-defined property that will be provided to reporters as part of the properties field of {@link SuiteResult} * @name setSuiteProperty * @since 3.6.0 * @function * @param {String} key The name of the property * @param {*} value The value of the property */ setSuiteProperty: function(key, value) { return env.setSuiteProperty(key, value); }, /** * Create an expectation for a spec. * @name expect * @since 1.3.0 * @function * @global * @param {Object} actual - Actual computed value to test expectations against. * @return {matchers} */ expect: function(actual) { return env.expect(actual); }, /** * Create an asynchronous expectation for a spec. Note that the matchers * that are provided by an asynchronous expectation all return promises * which must be either returned from the spec or waited for using `await` * in order for Jasmine to associate them with the correct spec. * @name expectAsync * @since 3.3.0 * @function * @global * @param {Object} actual - Actual computed value to test expectations against. * @return {async-matchers} * @example * await expectAsync(somePromise).toBeResolved(); * @example * return expectAsync(somePromise).toBeResolved(); */ expectAsync: function(actual) { return env.expectAsync(actual); }, /** * Mark a spec as pending, expectation results will be ignored. * @name pending * @since 2.0.0 * @function * @global * @param {String} [message] - Reason the spec is pending. */ pending: function() { return env.pending.apply(env, arguments); }, /** * Explicitly mark a spec as failed. * @name fail * @since 2.1.0 * @function * @global * @param {String|Error} [error] - Reason for the failure. */ fail: function() { return env.fail.apply(env, arguments); }, /** * Install a spy onto an existing object. * @name spyOn * @since 1.3.0 * @function * @global * @param {Object} obj - The object upon which to install the {@link Spy}. * @param {String} methodName - The name of the method to replace with a {@link Spy}. * @returns {Spy} */ spyOn: function(obj, methodName) { return env.spyOn(obj, methodName); }, /** * Install a spy on a property installed with `Object.defineProperty` onto an existing object. * @name spyOnProperty * @since 2.6.0 * @function * @global * @param {Object} obj - The object upon which to install the {@link Spy} * @param {String} propertyName - The name of the property to replace with a {@link Spy}. * @param {String} [accessType=get] - The access type (get|set) of the property to {@link Spy} on. * @returns {Spy} */ spyOnProperty: function(obj, methodName, accessType) { return env.spyOnProperty(obj, methodName, accessType); }, /** * Installs spies on all writable and configurable properties of an object. * @name spyOnAllFunctions * @since 3.2.1 * @function * @global * @param {Object} obj - The object upon which to install the {@link Spy}s * @param {boolean} includeNonEnumerable - Whether or not to add spies to non-enumerable properties * @returns {Object} the spied object */ spyOnAllFunctions: function(obj, includeNonEnumerable) { return env.spyOnAllFunctions(obj, includeNonEnumerable); }, jsApiReporter: new jasmine.JsApiReporter({ timer: new jasmine.Timer() }), /** * @namespace jasmine */ jasmine: jasmine }; /** * Add a custom equality tester for the current scope of specs. * * _Note:_ This is only callable from within a {@link beforeEach}, {@link it}, or {@link beforeAll}. * @name jasmine.addCustomEqualityTester * @since 2.0.0 * @function * @param {Function} tester - A function which takes two arguments to compare and returns a `true` or `false` comparison result if it knows how to compare them, and `undefined` otherwise. * @see custom_equality */ jasmine.addCustomEqualityTester = function(tester) { env.addCustomEqualityTester(tester); }; /** * Add custom matchers for the current scope of specs. * * _Note:_ This is only callable from within a {@link beforeEach}, {@link it}, or {@link beforeAll}. * @name jasmine.addMatchers * @since 2.0.0 * @function * @param {Object} matchers - Keys from this object will be the new matcher names. * @see custom_matcher */ jasmine.addMatchers = function(matchers) { return env.addMatchers(matchers); }; /** * Add custom async matchers for the current scope of specs. * * _Note:_ This is only callable from within a {@link beforeEach}, {@link it}, or {@link beforeAll}. * @name jasmine.addAsyncMatchers * @since 3.5.0 * @function * @param {Object} matchers - Keys from this object will be the new async matcher names. * @see custom_matcher */ jasmine.addAsyncMatchers = function(matchers) { return env.addAsyncMatchers(matchers); }; /** * Add a custom object formatter for the current scope of specs. * * _Note:_ This is only callable from within a {@link beforeEach}, {@link it}, or {@link beforeAll}. * @name jasmine.addCustomObjectFormatter * @since 3.6.0 * @function * @param {Function} formatter - A function which takes a value to format and returns a string if it knows how to format it, and `undefined` otherwise. * @see custom_object_formatters */ jasmine.addCustomObjectFormatter = function(formatter) { return env.addCustomObjectFormatter(formatter); }; /** * Get the currently booted mock {Clock} for this Jasmine environment. * @name jasmine.clock * @since 2.0.0 * @function * @returns {Clock} */ jasmine.clock = function() { return env.clock; }; /** * Create a bare {@link Spy} object. This won't be installed anywhere and will not have any implementation behind it. * @name jasmine.createSpy * @since 1.3.0 * @function * @param {String} [name] - Name to give the spy. This will be displayed in failure messages. * @param {Function} [originalFn] - Function to act as the real implementation. * @return {Spy} */ jasmine.createSpy = function(name, originalFn) { return env.createSpy(name, originalFn); }; /** * Create an object with multiple {@link Spy}s as its members. * @name jasmine.createSpyObj * @since 1.3.0 * @function * @param {String} [baseName] - Base name for the spies in the object. * @param {String[]|Object} methodNames - Array of method names to create spies for, or Object whose keys will be method names and values the {@link Spy#and#returnValue|returnValue}. * @param {String[]|Object} [propertyNames] - Array of property names to create spies for, or Object whose keys will be propertynames and values the {@link Spy#and#returnValue|returnValue}. * @return {Object} */ jasmine.createSpyObj = function(baseName, methodNames, propertyNames) { return env.createSpyObj(baseName, methodNames, propertyNames); }; /** * Add a custom spy strategy for the current scope of specs. * * _Note:_ This is only callable from within a {@link beforeEach}, {@link it}, or {@link beforeAll}. * @name jasmine.addSpyStrategy * @since 3.5.0 * @function * @param {String} name - The name of the strategy (i.e. what you call from `and`) * @param {Function} factory - Factory function that returns the plan to be executed. */ jasmine.addSpyStrategy = function(name, factory) { return env.addSpyStrategy(name, factory); }; /** * Set the default spy strategy for the current scope of specs. * * _Note:_ This is only callable from within a {@link beforeEach}, {@link it}, or {@link beforeAll}. * @name jasmine.setDefaultSpyStrategy * @function * @param {Function} defaultStrategyFn - a function that assigns a strategy * @example * beforeEach(function() { * jasmine.setDefaultSpyStrategy(and => and.returnValue(true)); * }); */ jasmine.setDefaultSpyStrategy = function(defaultStrategyFn) { return env.setDefaultSpyStrategy(defaultStrategyFn); }; return jasmineInterface; }; jasmine-4.5.0/src/core/util.js000066400000000000000000000047241432731766000162200ustar00rootroot00000000000000getJasmineRequireObj().util = function(j$) { const util = {}; util.isUndefined = function(obj) { return obj === void 0; }; util.clone = function(obj) { if (Object.prototype.toString.apply(obj) === '[object Array]') { return obj.slice(); } const cloned = {}; for (const prop in obj) { if (obj.hasOwnProperty(prop)) { cloned[prop] = obj[prop]; } } return cloned; }; util.cloneArgs = function(args) { return Array.from(args).map(function(arg) { const str = Object.prototype.toString.apply(arg), primitives = /^\[object (Boolean|String|RegExp|Number)/; // All falsey values are either primitives, `null`, or `undefined. if (!arg || str.match(primitives)) { return arg; } else if (str === '[object Date]') { return new Date(arg.valueOf()); } else { return j$.util.clone(arg); } }); }; util.getPropertyDescriptor = function(obj, methodName) { let descriptor, proto = obj; do { descriptor = Object.getOwnPropertyDescriptor(proto, methodName); proto = Object.getPrototypeOf(proto); } while (!descriptor && proto); return descriptor; }; util.has = function(obj, key) { return Object.prototype.hasOwnProperty.call(obj, key); }; util.errorWithStack = function errorWithStack() { // Don't throw and catch. That makes it harder for users to debug their // code with exception breakpoints, and it's unnecessary since all // supported environments populate new Error().stack return new Error(); }; function callerFile() { const trace = new j$.StackTrace(util.errorWithStack()); return trace.frames[2].file; } util.jasmineFile = (function() { let result; return function() { if (!result) { result = callerFile(); } return result; }; })(); util.validateTimeout = function(timeout, msgPrefix) { // Timeouts are implemented with setTimeout, which only supports a limited // range of values. The limit is unspecified, as is the behavior when it's // exceeded. But on all currently supported JS runtimes, setTimeout calls // the callback immediately when the timeout is greater than 2147483647 // (the maximum value of a signed 32 bit integer). const max = 2147483647; if (timeout > max) { throw new Error( (msgPrefix || 'Timeout value') + ' cannot be greater than ' + max ); } }; return util; }; jasmine-4.5.0/src/html/000077500000000000000000000000001432731766000147125ustar00rootroot00000000000000jasmine-4.5.0/src/html/HtmlReporter.js000066400000000000000000000572261432731766000177130ustar00rootroot00000000000000jasmineRequire.HtmlReporter = function(j$) { function ResultsStateBuilder() { this.topResults = new j$.ResultsNode({}, '', null); this.currentParent = this.topResults; this.specsExecuted = 0; this.failureCount = 0; this.pendingSpecCount = 0; } ResultsStateBuilder.prototype.suiteStarted = function(result) { this.currentParent.addChild(result, 'suite'); this.currentParent = this.currentParent.last(); }; ResultsStateBuilder.prototype.suiteDone = function(result) { this.currentParent.updateResult(result); if (this.currentParent !== this.topResults) { this.currentParent = this.currentParent.parent; } if (result.status === 'failed') { this.failureCount++; } }; ResultsStateBuilder.prototype.specStarted = function(result) {}; ResultsStateBuilder.prototype.specDone = function(result) { this.currentParent.addChild(result, 'spec'); if (result.status !== 'excluded') { this.specsExecuted++; } if (result.status === 'failed') { this.failureCount++; } if (result.status == 'pending') { this.pendingSpecCount++; } }; ResultsStateBuilder.prototype.jasmineDone = function(result) { if (result.failedExpectations) { this.failureCount += result.failedExpectations.length; } }; function HtmlReporter(options) { function config() { return (options.env && options.env.configuration()) || {}; } const getContainer = options.getContainer; const createElement = options.createElement; const createTextNode = options.createTextNode; const navigateWithNewParam = options.navigateWithNewParam || function() {}; const addToExistingQueryString = options.addToExistingQueryString || defaultQueryString; const filterSpecs = options.filterSpecs; let htmlReporterMain; let symbols; const deprecationWarnings = []; const failures = []; this.initialize = function() { clearPrior(); htmlReporterMain = createDom( 'div', { className: 'jasmine_html-reporter' }, createDom( 'div', { className: 'jasmine-banner' }, createDom('a', { className: 'jasmine-title', href: 'http://jasmine.github.io/', target: '_blank' }), createDom('span', { className: 'jasmine-version' }, j$.version) ), createDom('ul', { className: 'jasmine-symbol-summary' }), createDom('div', { className: 'jasmine-alert' }), createDom( 'div', { className: 'jasmine-results' }, createDom('div', { className: 'jasmine-failures' }) ) ); getContainer().appendChild(htmlReporterMain); }; let totalSpecsDefined; this.jasmineStarted = function(options) { totalSpecsDefined = options.totalSpecsDefined || 0; }; const summary = createDom('div', { className: 'jasmine-summary' }); const stateBuilder = new ResultsStateBuilder(); this.suiteStarted = function(result) { stateBuilder.suiteStarted(result); }; this.suiteDone = function(result) { stateBuilder.suiteDone(result); if (result.status === 'failed') { failures.push(failureDom(result)); } addDeprecationWarnings(result, 'suite'); }; this.specStarted = function(result) { stateBuilder.specStarted(result); }; this.specDone = function(result) { stateBuilder.specDone(result); if (noExpectations(result)) { const noSpecMsg = "Spec '" + result.fullName + "' has no expectations."; if (result.status === 'failed') { console.error(noSpecMsg); } else { console.warn(noSpecMsg); } } if (!symbols) { symbols = find('.jasmine-symbol-summary'); } symbols.appendChild( createDom('li', { className: this.displaySpecInCorrectFormat(result), id: 'spec_' + result.id, title: result.fullName }) ); if (result.status === 'failed') { failures.push(failureDom(result)); } addDeprecationWarnings(result, 'spec'); }; this.displaySpecInCorrectFormat = function(result) { return noExpectations(result) && result.status === 'passed' ? 'jasmine-empty' : this.resultStatus(result.status); }; this.resultStatus = function(status) { if (status === 'excluded') { return config().hideDisabled ? 'jasmine-excluded-no-display' : 'jasmine-excluded'; } return 'jasmine-' + status; }; this.jasmineDone = function(doneResult) { stateBuilder.jasmineDone(doneResult); const banner = find('.jasmine-banner'); const alert = find('.jasmine-alert'); const order = doneResult && doneResult.order; alert.appendChild( createDom( 'span', { className: 'jasmine-duration' }, 'finished in ' + doneResult.totalTime / 1000 + 's' ) ); banner.appendChild(optionsMenu(config())); if (stateBuilder.specsExecuted < totalSpecsDefined) { const skippedMessage = 'Ran ' + stateBuilder.specsExecuted + ' of ' + totalSpecsDefined + ' specs - run all'; // include window.location.pathname to fix issue with karma-jasmine-html-reporter in angular: see https://github.com/jasmine/jasmine/issues/1906 const skippedLink = (window.location.pathname || '') + addToExistingQueryString('spec', ''); alert.appendChild( createDom( 'span', { className: 'jasmine-bar jasmine-skipped' }, createDom( 'a', { href: skippedLink, title: 'Run all specs' }, skippedMessage ) ) ); } let statusBarMessage = ''; let statusBarClassName = 'jasmine-overall-result jasmine-bar '; const globalFailures = (doneResult && doneResult.failedExpectations) || []; const failed = stateBuilder.failureCount + globalFailures.length > 0; if (totalSpecsDefined > 0 || failed) { statusBarMessage += pluralize('spec', stateBuilder.specsExecuted) + ', ' + pluralize('failure', stateBuilder.failureCount); if (stateBuilder.pendingSpecCount) { statusBarMessage += ', ' + pluralize('pending spec', stateBuilder.pendingSpecCount); } } if (doneResult.overallStatus === 'passed') { statusBarClassName += ' jasmine-passed '; } else if (doneResult.overallStatus === 'incomplete') { statusBarClassName += ' jasmine-incomplete '; statusBarMessage = 'Incomplete: ' + doneResult.incompleteReason + ', ' + statusBarMessage; } else { statusBarClassName += ' jasmine-failed '; } let seedBar; if (order && order.random) { seedBar = createDom( 'span', { className: 'jasmine-seed-bar' }, ', randomized with seed ', createDom( 'a', { title: 'randomized with seed ' + order.seed, href: seedHref(order.seed) }, order.seed ) ); } alert.appendChild( createDom( 'span', { className: statusBarClassName }, statusBarMessage, seedBar ) ); const errorBarClassName = 'jasmine-bar jasmine-errored'; const afterAllMessagePrefix = 'AfterAll '; for (let i = 0; i < globalFailures.length; i++) { alert.appendChild( createDom( 'span', { className: errorBarClassName }, globalFailureMessage(globalFailures[i]) ) ); } function globalFailureMessage(failure) { if (failure.globalErrorType === 'load') { const prefix = 'Error during loading: ' + failure.message; if (failure.filename) { return ( prefix + ' in ' + failure.filename + ' line ' + failure.lineno ); } else { return prefix; } } else if (failure.globalErrorType === 'afterAll') { return afterAllMessagePrefix + failure.message; } else { return failure.message; } } addDeprecationWarnings(doneResult); for (let i = 0; i < deprecationWarnings.length; i++) { const children = []; let context; switch (deprecationWarnings[i].runnableType) { case 'spec': context = '(in spec: ' + deprecationWarnings[i].runnableName + ')'; break; case 'suite': context = '(in suite: ' + deprecationWarnings[i].runnableName + ')'; break; default: context = ''; } deprecationWarnings[i].message.split('\n').forEach(function(line) { children.push(line); children.push(createDom('br')); }); children[0] = 'DEPRECATION: ' + children[0]; children.push(context); if (deprecationWarnings[i].stack) { children.push(createExpander(deprecationWarnings[i].stack)); } alert.appendChild( createDom( 'span', { className: 'jasmine-bar jasmine-warning' }, children ) ); } const results = find('.jasmine-results'); results.appendChild(summary); summaryList(stateBuilder.topResults, summary); if (failures.length) { alert.appendChild( createDom( 'span', { className: 'jasmine-menu jasmine-bar jasmine-spec-list' }, createDom('span', {}, 'Spec List | '), createDom( 'a', { className: 'jasmine-failures-menu', href: '#' }, 'Failures' ) ) ); alert.appendChild( createDom( 'span', { className: 'jasmine-menu jasmine-bar jasmine-failure-list' }, createDom( 'a', { className: 'jasmine-spec-list-menu', href: '#' }, 'Spec List' ), createDom('span', {}, ' | Failures ') ) ); find('.jasmine-failures-menu').onclick = function() { setMenuModeTo('jasmine-failure-list'); return false; }; find('.jasmine-spec-list-menu').onclick = function() { setMenuModeTo('jasmine-spec-list'); return false; }; setMenuModeTo('jasmine-failure-list'); const failureNode = find('.jasmine-failures'); for (let i = 0; i < failures.length; i++) { failureNode.appendChild(failures[i]); } } }; return this; function failureDom(result) { const failure = createDom( 'div', { className: 'jasmine-spec-detail jasmine-failed' }, failureDescription(result, stateBuilder.currentParent), createDom('div', { className: 'jasmine-messages' }) ); const messages = failure.childNodes[1]; for (let i = 0; i < result.failedExpectations.length; i++) { const expectation = result.failedExpectations[i]; messages.appendChild( createDom( 'div', { className: 'jasmine-result-message' }, expectation.message ) ); messages.appendChild( createDom( 'div', { className: 'jasmine-stack-trace' }, expectation.stack ) ); } if (result.failedExpectations.length === 0) { messages.appendChild( createDom( 'div', { className: 'jasmine-result-message' }, 'Spec has no expectations' ) ); } if (result.debugLogs) { messages.appendChild(debugLogTable(result.debugLogs)); } return failure; } function debugLogTable(debugLogs) { const tbody = createDom('tbody'); debugLogs.forEach(function(entry) { tbody.appendChild( createDom( 'tr', {}, createDom('td', {}, entry.timestamp.toString()), createDom('td', {}, entry.message) ) ); }); return createDom( 'div', { className: 'jasmine-debug-log' }, createDom( 'div', { className: 'jasmine-debug-log-header' }, 'Debug logs' ), createDom( 'table', {}, createDom( 'thead', {}, createDom( 'tr', {}, createDom('th', {}, 'Time (ms)'), createDom('th', {}, 'Message') ) ), tbody ) ); } function summaryList(resultsTree, domParent) { let specListNode; for (let i = 0; i < resultsTree.children.length; i++) { const resultNode = resultsTree.children[i]; if (filterSpecs && !hasActiveSpec(resultNode)) { continue; } if (resultNode.type === 'suite') { const suiteListNode = createDom( 'ul', { className: 'jasmine-suite', id: 'suite-' + resultNode.result.id }, createDom( 'li', { className: 'jasmine-suite-detail jasmine-' + resultNode.result.status }, createDom( 'a', { href: specHref(resultNode.result) }, resultNode.result.description ) ) ); summaryList(resultNode, suiteListNode); domParent.appendChild(suiteListNode); } if (resultNode.type === 'spec') { if (domParent.getAttribute('class') !== 'jasmine-specs') { specListNode = createDom('ul', { className: 'jasmine-specs' }); domParent.appendChild(specListNode); } let specDescription = resultNode.result.description; if (noExpectations(resultNode.result)) { specDescription = 'SPEC HAS NO EXPECTATIONS ' + specDescription; } if ( resultNode.result.status === 'pending' && resultNode.result.pendingReason !== '' ) { specDescription = specDescription + ' PENDING WITH MESSAGE: ' + resultNode.result.pendingReason; } specListNode.appendChild( createDom( 'li', { className: 'jasmine-' + resultNode.result.status, id: 'spec-' + resultNode.result.id }, createDom( 'a', { href: specHref(resultNode.result) }, specDescription ) ) ); } } } function optionsMenu(config) { const optionsMenuDom = createDom( 'div', { className: 'jasmine-run-options' }, createDom('span', { className: 'jasmine-trigger' }, 'Options'), createDom( 'div', { className: 'jasmine-payload' }, createDom( 'div', { className: 'jasmine-stop-on-failure' }, createDom('input', { className: 'jasmine-fail-fast', id: 'jasmine-fail-fast', type: 'checkbox' }), createDom( 'label', { className: 'jasmine-label', for: 'jasmine-fail-fast' }, 'stop execution on spec failure' ) ), createDom( 'div', { className: 'jasmine-throw-failures' }, createDom('input', { className: 'jasmine-throw', id: 'jasmine-throw-failures', type: 'checkbox' }), createDom( 'label', { className: 'jasmine-label', for: 'jasmine-throw-failures' }, 'stop spec on expectation failure' ) ), createDom( 'div', { className: 'jasmine-random-order' }, createDom('input', { className: 'jasmine-random', id: 'jasmine-random-order', type: 'checkbox' }), createDom( 'label', { className: 'jasmine-label', for: 'jasmine-random-order' }, 'run tests in random order' ) ), createDom( 'div', { className: 'jasmine-hide-disabled' }, createDom('input', { className: 'jasmine-disabled', id: 'jasmine-hide-disabled', type: 'checkbox' }), createDom( 'label', { className: 'jasmine-label', for: 'jasmine-hide-disabled' }, 'hide disabled tests' ) ) ) ); const failFastCheckbox = optionsMenuDom.querySelector( '#jasmine-fail-fast' ); failFastCheckbox.checked = config.stopOnSpecFailure; failFastCheckbox.onclick = function() { navigateWithNewParam('stopOnSpecFailure', !config.stopOnSpecFailure); }; const throwCheckbox = optionsMenuDom.querySelector( '#jasmine-throw-failures' ); throwCheckbox.checked = config.stopSpecOnExpectationFailure; throwCheckbox.onclick = function() { navigateWithNewParam( 'stopSpecOnExpectationFailure', !config.stopSpecOnExpectationFailure ); }; const randomCheckbox = optionsMenuDom.querySelector( '#jasmine-random-order' ); randomCheckbox.checked = config.random; randomCheckbox.onclick = function() { navigateWithNewParam('random', !config.random); }; const hideDisabled = optionsMenuDom.querySelector( '#jasmine-hide-disabled' ); hideDisabled.checked = config.hideDisabled; hideDisabled.onclick = function() { navigateWithNewParam('hideDisabled', !config.hideDisabled); }; const optionsTrigger = optionsMenuDom.querySelector('.jasmine-trigger'), optionsPayload = optionsMenuDom.querySelector('.jasmine-payload'), isOpen = /\bjasmine-open\b/; optionsTrigger.onclick = function() { if (isOpen.test(optionsPayload.className)) { optionsPayload.className = optionsPayload.className.replace( isOpen, '' ); } else { optionsPayload.className += ' jasmine-open'; } }; return optionsMenuDom; } function failureDescription(result, suite) { const wrapper = createDom( 'div', { className: 'jasmine-description' }, createDom( 'a', { title: result.description, href: specHref(result) }, result.description ) ); let suiteLink; while (suite && suite.parent) { wrapper.insertBefore(createTextNode(' > '), wrapper.firstChild); suiteLink = createDom( 'a', { href: suiteHref(suite) }, suite.result.description ); wrapper.insertBefore(suiteLink, wrapper.firstChild); suite = suite.parent; } return wrapper; } function suiteHref(suite) { const els = []; while (suite && suite.parent) { els.unshift(suite.result.description); suite = suite.parent; } // include window.location.pathname to fix issue with karma-jasmine-html-reporter in angular: see https://github.com/jasmine/jasmine/issues/1906 return ( (window.location.pathname || '') + addToExistingQueryString('spec', els.join(' ')) ); } function addDeprecationWarnings(result, runnableType) { if (result && result.deprecationWarnings) { for (let i = 0; i < result.deprecationWarnings.length; i++) { const warning = result.deprecationWarnings[i].message; deprecationWarnings.push({ message: warning, stack: result.deprecationWarnings[i].stack, runnableName: result.fullName, runnableType: runnableType }); } } } function createExpander(stackTrace) { const expandLink = createDom('a', { href: '#' }, 'Show stack trace'); const root = createDom( 'div', { className: 'jasmine-expander' }, expandLink, createDom( 'div', { className: 'jasmine-expander-contents jasmine-stack-trace' }, stackTrace ) ); expandLink.addEventListener('click', function(e) { e.preventDefault(); if (root.classList.contains('jasmine-expanded')) { root.classList.remove('jasmine-expanded'); expandLink.textContent = 'Show stack trace'; } else { root.classList.add('jasmine-expanded'); expandLink.textContent = 'Hide stack trace'; } }); return root; } function find(selector) { return getContainer().querySelector('.jasmine_html-reporter ' + selector); } function clearPrior() { const oldReporter = find(''); if (oldReporter) { getContainer().removeChild(oldReporter); } } function createDom(type, attrs, childrenArrayOrVarArgs) { const el = createElement(type); let children; if (j$.isArray_(childrenArrayOrVarArgs)) { children = childrenArrayOrVarArgs; } else { children = []; for (let i = 2; i < arguments.length; i++) { children.push(arguments[i]); } } for (let i = 0; i < children.length; i++) { const child = children[i]; if (typeof child === 'string') { el.appendChild(createTextNode(child)); } else { if (child) { el.appendChild(child); } } } for (const attr in attrs) { if (attr == 'className') { el[attr] = attrs[attr]; } else { el.setAttribute(attr, attrs[attr]); } } return el; } function pluralize(singular, count) { const word = count == 1 ? singular : singular + 's'; return '' + count + ' ' + word; } function specHref(result) { // include window.location.pathname to fix issue with karma-jasmine-html-reporter in angular: see https://github.com/jasmine/jasmine/issues/1906 return ( (window.location.pathname || '') + addToExistingQueryString('spec', result.fullName) ); } function seedHref(seed) { // include window.location.pathname to fix issue with karma-jasmine-html-reporter in angular: see https://github.com/jasmine/jasmine/issues/1906 return ( (window.location.pathname || '') + addToExistingQueryString('seed', seed) ); } function defaultQueryString(key, value) { return '?' + key + '=' + value; } function setMenuModeTo(mode) { htmlReporterMain.setAttribute('class', 'jasmine_html-reporter ' + mode); } function noExpectations(result) { const allExpectations = result.failedExpectations.length + result.passedExpectations.length; return ( allExpectations === 0 && (result.status === 'passed' || result.status === 'failed') ); } function hasActiveSpec(resultNode) { if (resultNode.type == 'spec' && resultNode.result.status != 'excluded') { return true; } if (resultNode.type == 'suite') { for (let i = 0, j = resultNode.children.length; i < j; i++) { if (hasActiveSpec(resultNode.children[i])) { return true; } } } } } return HtmlReporter; }; jasmine-4.5.0/src/html/HtmlSpecFilter.js000066400000000000000000000006261432731766000201410ustar00rootroot00000000000000jasmineRequire.HtmlSpecFilter = function() { function HtmlSpecFilter(options) { const filterString = options && options.filterString() && options.filterString().replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&'); const filterPattern = new RegExp(filterString); this.matches = function(specName) { return filterPattern.test(specName); }; } return HtmlSpecFilter; }; jasmine-4.5.0/src/html/QueryString.js000066400000000000000000000026071432731766000175510ustar00rootroot00000000000000jasmineRequire.QueryString = function() { function QueryString(options) { this.navigateWithNewParam = function(key, value) { options.getWindowLocation().search = this.fullStringWithNewParam( key, value ); }; this.fullStringWithNewParam = function(key, value) { const paramMap = queryStringToParamMap(); paramMap[key] = value; return toQueryString(paramMap); }; this.getParam = function(key) { return queryStringToParamMap()[key]; }; return this; function toQueryString(paramMap) { const qStrPairs = []; for (const prop in paramMap) { qStrPairs.push( encodeURIComponent(prop) + '=' + encodeURIComponent(paramMap[prop]) ); } return '?' + qStrPairs.join('&'); } function queryStringToParamMap() { const paramStr = options.getWindowLocation().search.substring(1); let params = []; const paramMap = {}; if (paramStr.length > 0) { params = paramStr.split('&'); for (let i = 0; i < params.length; i++) { const p = params[i].split('='); let value = decodeURIComponent(p[1]); if (value === 'true' || value === 'false') { value = JSON.parse(value); } paramMap[decodeURIComponent(p[0])] = value; } } return paramMap; } } return QueryString; }; jasmine-4.5.0/src/html/ResultsNode.js000066400000000000000000000007701432731766000175230ustar00rootroot00000000000000jasmineRequire.ResultsNode = function() { function ResultsNode(result, type, parent) { this.result = result; this.type = type; this.parent = parent; this.children = []; this.addChild = function(result, type) { this.children.push(new ResultsNode(result, type, this)); }; this.last = function() { return this.children[this.children.length - 1]; }; this.updateResult = function(result) { this.result = result; }; } return ResultsNode; }; jasmine-4.5.0/src/html/_HTMLReporter.scss000066400000000000000000000172571432731766000202510ustar00rootroot00000000000000@use "sass:math"; $line-height: 14px; $margin-unit: 14px; $faint-text-color: #aaa; $light-text-color: #666; $text-color: #333; $page-background-color: #eee; $passing-color: #007069; $failing-color: #ca3a11; $pending-color: #ba9d37; $empty-color: #eff543; $neutral-color: #bababa; $jasmine-color: #8a4182; $passing-mark: "\02022"; $failing-mark: "\d7"; $pending-mark: "*"; $space: "\0020"; $font-size: 11px; $large-font-size: 14px; body { overflow-y: scroll; } .jasmine_html-reporter { width: 100%; background-color: $page-background-color; padding: 5px; margin: -8px; font-size: $font-size; font-family: Monaco, "Lucida Console", monospace; line-height: $line-height; color: $text-color; a { text-decoration: none; &:hover { text-decoration: underline; } } p, h1, h2, h3, h4, h5, h6 { margin: 0; line-height: $line-height; } .jasmine-banner, .jasmine-symbol-summary, .jasmine-summary, .jasmine-result-message, .jasmine-spec .jasmine-description, .jasmine-spec-detail .jasmine-description, .jasmine-alert .jasmine-bar, .jasmine-stack-trace { padding-left: $margin-unit - 5px; padding-right: $margin-unit - 5px; } .jasmine-banner { position: relative; } .jasmine-banner .jasmine-title { background: url('../../images/jasmine-horizontal.png') no-repeat; background: url('../../images/jasmine-horizontal.svg') no-repeat, none; background-size: 100%; display: block; float: left; width: 90px; height: 25px; } .jasmine-banner .jasmine-version { margin-left: $margin-unit; position: relative; top: 6px; } // This div is available for testing elements that must be added to the DOM. // We position it out of view, so it doesn't obstruct the runner. #jasmine_content { position: fixed; right: 100%; } .jasmine-version { color: $faint-text-color; } //--- Banner ---// .jasmine-banner { margin-top: $line-height; } .jasmine-duration { color: #fff; float: right; line-height: $line-height * 2; padding-right: 9px; } //--- Symbol summary ---// .jasmine-symbol-summary { overflow: hidden; margin: $line-height 0; li { display: inline-block; height: math.div($line-height, 2) + 3; width: $line-height; font-size: 16px; &.jasmine-passed { font-size: 14px; &:before { color: $passing-color; content: $passing-mark; } } &.jasmine-failed { line-height: math.div($line-height, 2) + 2; &:before { color: $failing-color; content: $failing-mark; font-weight: bold; margin-left: -1px; } } &.jasmine-excluded { font-size: 14px; &:before { color: $neutral-color; content: $passing-mark; } } &.jasmine-excluded-no-display { font-size: 14px; display: none; } &.jasmine-pending { line-height: 17px; &:before { color: $pending-color; content: $pending-mark; } } &.jasmine-empty { font-size: 14px; &:before { color: $pending-color; content: $passing-mark; } } } } .jasmine-run-options { float: right; margin-right: 5px; border: 1px solid $jasmine-color; color: $jasmine-color; position: relative; line-height: 20px; .jasmine-trigger { cursor: pointer; padding: 8px 16px; } .jasmine-payload { position: absolute; display: none; right: -1px; border: 1px solid $jasmine-color; background-color: $page-background-color; white-space: nowrap; padding: 4px 8px; &.jasmine-open { display: block; } } } //--- Alerts: status bars ---// .jasmine-bar { line-height: $line-height * 2; font-size: $large-font-size; display: block; color: #eee; &.jasmine-failed, &.jasmine-errored { background-color: $failing-color; border-bottom: 1px solid $page-background-color; } &.jasmine-passed { background-color: $passing-color; } &.jasmine-incomplete { background-color: $neutral-color; } &.jasmine-skipped { background-color: $neutral-color; } &.jasmine-warning { margin-top: $margin-unit; margin-bottom: $margin-unit; background-color: $pending-color; color: $text-color; } &.jasmine-menu { background-color: #fff; color: $faint-text-color; a { color: $text-color; } } a { color: white; } } // simplify toggle control between the two menu bars &.jasmine-spec-list { .jasmine-bar.jasmine-menu.jasmine-failure-list, .jasmine-results .jasmine-failures { display: none; } } &.jasmine-failure-list { .jasmine-bar.jasmine-menu.jasmine-spec-list, .jasmine-summary { display: none; } } //--- Results ---// .jasmine-results { margin-top: $line-height; } //--- Results summary: Suites and Specs names/links ---// .jasmine-summary { margin-top: $margin-unit; ul { list-style-type: none; margin-left: $margin-unit; padding-top: 0; padding-left: 0; &.jasmine-suite { margin-top: math.div($margin-unit, 2); margin-bottom: math.div($margin-unit, 2) } } li { &.jasmine-passed a { color: $passing-color; } &.jasmine-failed a { color: $failing-color; } &.jasmine-empty a { color: $pending-color; } &.jasmine-pending a { color: $pending-color; } &.jasmine-excluded a { color: $neutral-color; } } } .jasmine-specs { li { &.jasmine-passed a:before { content: $passing-mark + $space; } &.jasmine-failed a:before { content: $failing-mark + $space; } &.jasmine-empty a:before { content: $pending-mark + $space; } &.jasmine-pending a:before { content: $passing-mark + $space; } &.jasmine-excluded a:before { content: $passing-mark + $space; } } } .jasmine-description + .jasmine-suite { margin-top: 0; } .jasmine-suite { margin-top: $margin-unit; a { color: $text-color; } } //--- Failure details ---// .jasmine-failures { .jasmine-spec-detail { margin-bottom: $line-height * 2; .jasmine-description { background-color: $failing-color; color: white; a { color: white; } } } } .jasmine-result-message { padding-top: $line-height; color: $text-color; white-space: pre-wrap; } .jasmine-result-message span.jasmine-result { display: block; } .jasmine-stack-trace { margin: 5px 0 0 0; max-height: $line-height * 16; overflow: auto; line-height: 18px; color: $light-text-color; border: 1px solid #ddd; background: white; white-space: pre; } .jasmine-expander { a { display: block; margin-left: $margin-unit; color: blue; text-decoration: underline; } } .jasmine-expander-contents { display: none; } .jasmine-expanded { padding-bottom: 10px; } .jasmine-expanded .jasmine-expander-contents { display: block; margin-left: $margin-unit; padding: 5px; } .jasmine-debug-log { margin: 5px 0 0 0; padding: 5px; color: $light-text-color; border: 1px solid #ddd; background: white; table { border-spacing: 0; } table, th, td { border: 1px solid #ddd; } } } jasmine-4.5.0/src/html/jasmine.scss000066400000000000000000000000301432731766000172260ustar00rootroot00000000000000@import "HTMLReporter"; jasmine-4.5.0/src/html/requireHtml.js000066400000000000000000000005411432731766000175510ustar00rootroot00000000000000// eslint-disable-next-line no-var var jasmineRequire = window.jasmineRequire || require('./jasmine.js'); jasmineRequire.html = function(j$) { j$.ResultsNode = jasmineRequire.ResultsNode(); j$.HtmlReporter = jasmineRequire.HtmlReporter(j$); j$.QueryString = jasmineRequire.QueryString(); j$.HtmlSpecFilter = jasmineRequire.HtmlSpecFilter(); }; jasmine-4.5.0/src/templates/000077500000000000000000000000001432731766000157445ustar00rootroot00000000000000jasmine-4.5.0/src/templates/example_project_jasmine_tags.html.erb000066400000000000000000000004351432731766000253100ustar00rootroot00000000000000 jasmine-4.5.0/src/templates/version.js.erb000066400000000000000000000003171432731766000205370ustar00rootroot00000000000000 jasmine.version_= { "major": <%= major %>, "minor": <%= minor %>, "build": <%= build %>, "revision": <%= revision %><%= %Q{,\n "release_candidate": #{release_candidate}} if release_candidate %> }; jasmine-4.5.0/src/version.js000066400000000000000000000001141432731766000157650ustar00rootroot00000000000000getJasmineRequireObj().version = function() { return '<%= version %>'; };