Compare commits

...

67 Commits

Author SHA1 Message Date
Steve Gravrock
f509078020 Bump version to 4.6.0 2023-03-15 17:21:56 -07:00
Steve Gravrock
ff237f4b66 Fixed sass version pinning 2023-03-11 14:26:22 -08:00
Steve Gravrock
e42e3d9e00 Pin sass to the last version that works on Node 12 2023-03-11 14:19:16 -08:00
Steve Gravrock
166e5f4d6c Report the ID of each suite/spec's parent
This is intended to support parallel execution, which is planned for a
future release of Jasmine. Because the execution of unrelated suites will
interleave when run in parallel, reporters will not be able to assume
that the most recent `suiteStarted` event identifies the parent of the
current suite/spec. By adding this feature now, we allow reporters to
support both parallel execution and at least some 4.x versions without
having to implement two different ways of finding the parent suite.
2023-02-25 10:24:14 -08:00
Steve Gravrock
6ad8d20694 Report the path/url of the file that the spec/suite was defined in
Fixes #1884
2023-02-15 21:39:21 -08:00
Steve Gravrock
bc3a495160 Pin eslint-plugin-compat to <4.1.0 to fix import error on CI
See <https://github.com/amilajack/eslint-plugin-compat/issues/528>.
2023-02-07 18:46:17 -08:00
Steve Gravrock
b323631611 Pin Grunt to <1.6.0 for compatiblity with Node 12 2023-01-30 17:57:47 -08:00
Steve Gravrock
e8767ba660 Removed "Does the problem occur with the latest version of jasmine-core" from issue templates 2023-01-25 20:50:51 -08:00
Steve Gravrock
169a2a8ad2 Upgraded to new issue templates 2022-11-20 14:01:43 -08:00
Steve Gravrock
b267029301 Revert "Upgraded to new issue templates"
This reverts commit cf574634b8.
2022-11-20 13:58:20 -08:00
Steve Gravrock
cf574634b8 Upgraded to new issue templates 2022-11-20 13:56:47 -08:00
Steve Gravrock
f8c01574e6 Added Firefox 102 (current ESR) to browser list in README 2022-10-29 15:26:30 -07:00
Steve Gravrock
481f1e7c5c Bump version to 4.5.0 2022-10-29 14:48:32 -07:00
Steve Gravrock
5e650953cd Added Safari 16 to supported browsers 2022-10-22 13:08:10 -07:00
Steve Gravrock
87f9ab29df Fixed the jsdoc types of SuiteResult and SpecResult ids 2022-10-19 17:20:24 -07:00
Steve Gravrock
b831e81074 Include inner exceptions in stack traces 2022-09-24 12:12:21 -07:00
Steve Gravrock
4c13c2b00b Replaced var with const in API doc examples 2022-09-24 10:12:22 -07:00
Steve Gravrock
4cd190b232 Merge branch 'internal-async' 2022-09-18 13:31:43 -07:00
Steve Gravrock
d4025999b7 Report exceptions thrown by a describe before any it calls
Previously, these were masked by the "describe with no children" error.
Now they're reported as suite level errors on an empty suite.
2022-09-17 13:24:45 -07:00
Steve Gravrock
44f331f43d Updated the style of the examples
* const/let instead of var
* classes
* pass our own eslint checks
2022-09-17 12:00:20 -07:00
Steve Gravrock
59848ca151 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
2022-09-03 12:36:35 -07:00
Steve Gravrock
c14bfe3e5f Updated release process doc
* Fixed description of patch releases
* Moved -npm release docmentation to that repo
* Refer to -npm specifically rather than "binding libraries" generally,
  now that we only have one of those that versions in lockstep with core.
2022-09-03 11:02:13 -07:00
Steve Gravrock
26c48ab324 Bump version to 4.4.0 2022-09-03 09:42:39 -07:00
Steve Gravrock
cfecab9f79 Updated contributing guide 2022-08-22 18:10:33 -07:00
Steve Gravrock
b3d9435dbb Convreted TreeProcessor to async/await 2022-08-22 17:04:44 -07:00
Steve Gravrock
fec8dd37b0 Merge branch 'main' into internal-async 2022-08-22 10:46:03 -07:00
Steve Gravrock
f934e6d816 Assume that addEventListener/removeEventListener are present in browsers
Jasmine 3.0 dropped support for the last browser that didn't support
the standard event handler methods (IE 9).
2022-08-20 10:27:44 -07:00
Steve Gravrock
79c6bbc189 clearStack optimizations
* Avoid setTimeout in Node, because we don't need the overhead there.
* Still call setTimeout in browsers to prevent the tab from being killed.
* Use queueMicrotask in Safari, because it's dramatically faster than
  MessageChannel there.
* Continue to use MessageChannel in other supported browsers becuase it's
  somewhat faster than queueMicrotask there.
* Don't use setImmediate any more because there's a faster alternative in
  all supported envs.

In jasmine-core's own test suite, this yields a roughly 50-70% speedup
in Node, ~20% in Edge, and 75-90%(!) in Safari.
2022-08-15 17:50:49 -07:00
Steve Gravrock
5f3475342e Re-added missing JasmineStartedInfo jsdoc 2022-08-06 10:53:28 -07:00
Steve Gravrock
e022e6199c Bump version to 4.3.0 2022-07-23 10:27:19 -07:00
Steve Gravrock
140c12e8fc Added Firefox 102 (current ESR) to CI 2022-07-23 10:17:21 -07:00
Steve Gravrock
21f25972bb Converted ReportDispatcher to promises 2022-07-01 17:25:22 -07:00
Steve Gravrock
d0e1bd96fb README updates
* Removed redundancy
* Added a link to the FAQ
* Removed obsolete support channels
2022-07-01 16:58:38 -07:00
Steve Gravrock
6c56ebc984 Added jasmine.spyOnGlobalErrorsAsync
* Allows testing code that's expected to prodeuce global errors or
  unhandled promise rejections
* Fixes #1843
* Fixes #1453
2022-06-30 18:09:56 -07:00
Steve Gravrock
d0a9931ae6 Separated reporter- and runable-specific queue runner configuration 2022-06-12 15:52:14 -07:00
Steve Gravrock
93c5f654d9 Extracted most suite-running code out of Env 2022-06-12 15:46:03 -07:00
Steve Gravrock
d8b65028a1 Pass queue runner factory to Spec#execute, not ctor 2022-06-12 12:34:46 -07:00
Steve Gravrock
d6cdc1841c Extracted suite building out of Env 2022-06-12 09:49:01 -07:00
Steve Gravrock
72b39220e5 Runable, not runnable 2022-06-11 15:41:29 -07:00
Steve Gravrock
55dce7d119 Extracted runnable resource management out of Env 2022-06-11 15:28:37 -07:00
Steve Gravrock
789736dd02 Additional test coverage for default spy strategies 2022-06-11 15:08:02 -07:00
Steve Gravrock
c7ca3b0101 Converted integration specs to async/await 2022-06-11 13:43:44 -07:00
Steve Gravrock
96000220b1 Use arrow fns rather than self = this 2022-06-11 12:12:11 -07:00
Steve Gravrock
e2e2275d41 Removed obsolete and unused utility fns 2022-06-11 11:17:16 -07:00
Steve Gravrock
135ff20123 Replaced uses of var with const/let 2022-06-09 20:00:23 -07:00
Steve Gravrock
4af86f5398 Added supported envs to releasen notes 2022-06-09 18:25:04 -07:00
Steve Gravrock
e5e0e6481d Bump version to 4.2.0 2022-06-09 18:18:45 -07:00
Steve Gravrock
bcf69b86b4 Removed duplicate Suite and Spec jsdocs 2022-06-03 12:22:58 -07:00
Steve Gravrock
a5f79fac81 Removed remaining jshint config comments 2022-06-02 18:22:23 -07:00
Steve Gravrock
18a00822c5 Built distribution 2022-06-02 11:37:10 -07:00
Steve Gravrock
4cc8437f79 Call buildExpectationResult directly from Suite and Spec
This removes quite a bit of indirection from result processing, at the
cost of making a few of the tests more awkward.
2022-06-01 10:18:23 -07:00
Steve Gravrock
8e58305b0a ExpectationResult.js -> buildExpectationResult.js 2022-06-01 09:26:21 -07:00
Steve Gravrock
bd368aceee Replaced var with const and let in expectation related code 2022-06-01 09:22:03 -07:00
Steve Gravrock
8f16021887 Replaced var with const and let in ExpectationResult 2022-06-01 09:02:43 -07:00
Steve Gravrock
bbb1b69b2e 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.
2022-05-28 18:10:55 -07:00
Steve Gravrock
9ea8a2096f Additional integration tests for existing async error handling 2022-05-28 18:01:19 -07:00
Steve Gravrock
66340e2b19 Updated browserslist to match 4.0 envs 2022-05-28 18:00:45 -07:00
Steve Gravrock
fe29dfa89c Update release process instructions
* Need to tag the release manually since the Ruby tooling for that was
  removed
* Windows CI works again, so no need to manually test on Windows
2022-05-21 09:43:43 -07:00
Steve Gravrock
41f7fabe2f Renamed jasmine.exactly to jasmine.is, for similarity with toBe 2022-05-21 08:30:53 -07:00
Steve Gravrock
856a040a2d Fixed flaky spec 2022-05-19 16:39:48 -07:00
Steve Gravrock
f7eaa5ec29 Fixed failing CI builds for Node 12 and 14
See https://github.com/npm/cli/issues/4896
2022-05-16 19:53:20 -07:00
Steve Gravrock
0c87d47318 Added a jasmine.exactly asymmetric equality tester 2022-05-14 17:01:38 -07:00
Steve Gravrock
c24b2f5a73 Converted some integration specs to async/await 2022-05-14 12:05:53 -07:00
Steve Gravrock
774c83a36e 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
(and broken).
2022-05-14 11:21:40 -07:00
Steve Gravrock
751cf6ab5b Converted DiffBuilder, ObjectPath, MismatchTree, and SinglePrettyPrintRun to ES6 classes 2022-05-14 11:15:08 -07:00
Steve Gravrock
2fd76c954c Replaced var with let and const in PrettyPrinter, DiffBuilder, and friends 2022-05-14 09:42:07 -07:00
Steve Gravrock
bb4d18f959 Include property getter values in pretty-printed objects
We already call getters when comparing objects for equality and generating
diffs, so it should be safe to do it here too.

See #1966.
2022-05-12 17:14:13 -07:00
135 changed files with 9838 additions and 7096 deletions

View File

@@ -1,19 +1,10 @@
# Developing for Jasmine Core
# Contributing to Jasmine
We welcome your contributions! Thanks for helping make Jasmine a better project
for everyone. Please review the backlog and discussion lists before starting
work. What you're looking for may already have been done. If it hasn't, the
community can help make your contribution better. If you want to contribute but
don't know what to work on,
[issues tagged help needed](https://github.com/jasmine/jasmine/labels/help%20needed)
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.
## Links
- [Jasmine Google Group](http://groups.google.com/group/jasmine-js)
- [Jasmine-dev Google Group](http://groups.google.com/group/jasmine-js-dev)
- [Jasmine backlog](https://www.pivotaltracker.com/n/projects/10606)
## Before Submitting a Pull Request
1. Ensure all specs are green in browsers *and* node.
@@ -94,14 +85,7 @@ Or, How to make a successful pull request
* _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.
Key exceptions:
* Use `const` or `let` for new variable declarations, even if nearby code
uses `var`.
* New async specs should usually be async/await or promise-returning, not
callback based.
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.
@@ -119,3 +103,10 @@ chromedriver), you can also use Jasmine's CI tooling:
$ JASMINE_BROWSER=<name of 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.

View File

@@ -1,47 +0,0 @@
## 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)
<!--- Provide a general summary of the issue in the Title above -->
## Expected Behavior
<!--- If you're describing a bug, tell us what should happen -->
<!--- If you're suggesting a change/improvement, tell us how it should work -->
## Current Behavior
<!--- If describing a bug, tell us what happens instead of the expected behavior -->
<!--- If suggesting a change/improvement, explain the difference from current behavior -->
## Possible Solution
<!--- Not obligatory, but suggest a fix/reason for the bug, -->
<!--- or ideas how to implement the addition or change -->
## Suite that reproduces the behavior (for bugs)
<!--- Provide a sample suite that reproduces the bug. -->
```javascript
describe("sample", function() {
});
```
## Context
<!--- How has this issue affected you? What are you trying to accomplish? -->
<!--- Providing context helps us come up with a solution that is most useful in the real world -->
## Your Environment
<!--- Include as many relevant details about the environment you experienced the bug in -->
* 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:

98
.github/ISSUE_TEMPLATE/bug_report.yml vendored Normal file
View File

@@ -0,0 +1,98 @@
name: Bug Report
description: I think I've found a bug in Jasmine
labels: ["unconfirmed bug"]
body:
- type: markdown
attributes:
value: |
Thanks for taking the time to report a bug. Please follow these steps first.
## Troubleshooting
Please take the time to rule out issues with your code or third party libraries before filing a bug report. If you are reporting an error, try to determine whether the error is coming from Jasmine, another library, or your own code.
Check the [FAQ](https://jasmine.github.io/pages/faq.html) and any other relevant [documentation](https://jasmine.github.io/pages/docs_home.html) to see if your issue has already been addressed.
## Special troubleshooting steps for asynchronous scenarios
If the issue has to do with testing asynchronous code, please read the [async tutorial](https://jasmine.github.io/tutorials/async) and the async section of the FAQ. In particular, check for the following common errors:
* Are you trying to write a synchronous test for asynchronous code?
* Does the test signal completion before the code under test finishes?
* Do expectations run before the code that they're trying to verify?
## Try the latest version of Jasmine
If at all possible, upgrade to the latest versions of `jasmine-core` and any other relevant packages (e.g. `jasmine`, `jasmine-browser-runner`). If you can't do that, please check the [release notes](https://github.com/jasmine/jasmine/tree/main/release_notes) for all newer versions to make sure that the bug hasn't already been fixed.
## Put together a [minimal, reproducible example](https://stackoverflow.com/help/minimal-reproducible-example)
Please help us help you by creating a minimal but complete setup that demonstrates the problem. Remove any code and libraries that aren't absolutely necessary, but make sure it doesn't depend on any code you haven't included. In many cases a simple code snippet is enough. In cases involving external libraries, *especially* Karma or Angular, we're likely to need a runable Git repository or jsbin/stackblitz/etc.
**If we can't reproduce it, we can't fix it. Bug reports without a minimal, reproducible example are very likely to be closed.**
- type: textarea
id: steps-to-reproduce
attributes:
label: Steps to Reproduce
placeholder: |
Example steps:
1. Paste the example code below into `mySpec.js`.
2. Run `npx jasmine@<some version> mySpec.js`
validations:
required: true
- type: textarea
id: expected-behavior
attributes:
label: Expected Behavior
description: What do you think should have happened?
validations:
required: true
- type: textarea
id: actual-behavior
attributes:
label: Actual Behavior
description: What happened instead?
validations:
required: true
- type: textarea
id: code-sample
attributes:
label: Example code that reproduces the problem
description: Please include either a code snippet that reproduces the problem or a link to a repository or jsbin/stackblitz/etc containing a minimal, reproducible example as described above.
render: JavaScript
validations:
required: true
- type: textarea
id: possible-solution
attributes:
label: Possible Solution
description: This is optional, but if you have an idea for how to fix the bug we'd like to hear it.
- type: textarea
id: context
attributes:
label: Context
description: How has this issue affected you? What are you trying to accomplish? By providing context, you can help us come up with a solution that is most useful in the real world.
- type: input
id: jasmine-core-version
attributes:
label: jasmine-core version
validations:
required: true
- type: textarea
id: other-versions
attributes:
label: Versions of other relevant packages
placeholder: |
jasmine-browser-runner 1.2.0
fancy-reporter 132.4.8
- type: input
id: browser-or-node-version
attributes:
label: Node.js or browser version
placeholder: E.g. "node 16.2.0" or "Safari 15"
validations:
required: true
- type: input
id: os
attributes:
label: Operating System
placeholder: E.g. "Windows 10", "MacOS 12.5", "MCC Interim Linux 0.99.p8"
validations:
required: true

14
.github/ISSUE_TEMPLATE/config.yml vendored Normal file
View File

@@ -0,0 +1,14 @@
blank_issues_enabled: false
contact_links:
- name: Issues with the `jasmine` CLI
url: https://github.com/jasmine/jasmine-npm/issues
about: Please create issues related to the `jasmine` package in its repository.
- name: Issues with jasmine-browser-runner
url: https://github.com/jasmine/jasmine-browser-runner/issues
about: Please create issues related to the `jasmine-browser-runner` package in its repository.
- name: Documentation issues
url: https://github.com/jasmine/jasmine.github.io/issues
about: Please create documentation issues in the docs repository.
- name: TypeScript issues
url: https://github.com/DefinitelyTyped/DefinitelyTyped/discussions
about: Please create issues related to TypeScript compilation errors or other problems with type definitions at DefinitelyTyped.

View File

@@ -0,0 +1,31 @@
name: Feature Proposal
description: I'd like to propose a new feature
labels: ["feature request"]
body:
- type: markdown
attributes:
value: Thanks for taking the time to propose a new feature. Although Jasmine is mostly feature complete, we're always open to hearing new ideas.
- type: textarea
id: description
attributes:
label: Feature Proposal
validations:
required: true
- type: textarea
id: context
attributes:
label: Context
description: How would this feature be useful to you? What are you trying to accomplish? By providing context, you can help us come up with a solution that is most useful in the real world.
validations:
required: true
- type: textarea
id: example
attributes:
label: Example
description: If you're proposing a new API or something similar, please show an example of how it would be used.
render: JavaScript
- type: textarea
id: other-info
attributes:
label: Other Information
description: Anything else that you think would be helpful.

View File

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

View File

@@ -2,17 +2,12 @@
[![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)
[![FOSSA Status](https://app.fossa.io/api/projects/git%2Bgithub.com%2Fjasmine%2Fjasmine.svg?type=shield)](https://app.fossa.io/projects/git%2Bgithub.com%2Fjasmine%2Fjasmine?ref=badge_shield)
# 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.
Documentation & guides live here: [http://jasmine.github.io](http://jasmine.github.io/)
For a quick start guide of Jasmine, see the beginning of [http://jasmine.github.io/edge/introduction.html](http://jasmine.github.io/edge/introduction.html).
Upgrading from Jasmine 3.x? Check out the 4.0 release notes for a list of
what's new (including breaking changes). You can also read the [upgrade guide](https://jasmine.github.io/tutorials/upgrading_to_Jasmine_4.0).
Upgrading from Jasmine 3.x? Check out the [upgrade guide](https://jasmine.github.io/tutorials/upgrading_to_Jasmine_4.0).
## Contributing
@@ -28,7 +23,7 @@ for details.
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.
for information on writing specs, and [the FAQ](https://jasmine.github.io/pages/faq.html).
## Supported environments
@@ -38,24 +33,16 @@ Microsoft Edge) as well as Node.
| Environment | Supported versions |
|-------------------|--------------------|
| Node | 12.17+, 14, 16, 18 |
| Safari | 14-15 |
| Safari | 14-16 |
| Chrome | Evergreen |
| Firefox | Evergreen, 91 |
| Firefox | Evergreen, 91, 102 |
| Edge | Evergreen |
For evergreen browsers, each version of Jasmine is tested against the version of the browser that is available to us
at the time of release. Other browsers, as well as older & newer versions of some supported browsers, are likely to work.
However, Jasmine isn't tested against them and they aren't actively supported.
See the [release notes](https://github.com/jasmine/jasmine/tree/main/release_notes)
for the supported environments for each Jasmine release.
## Support
* Search past discussions: [http://groups.google.com/group/jasmine-js](http://groups.google.com/group/jasmine-js).
* Send an email to the list: [jasmine-js@googlegroups.com](mailto:jasmine-js@googlegroups.com).
* View the project backlog at Pivotal Tracker: [http://www.pivotaltracker.com/projects/10606](http://www.pivotaltracker.com/projects/10606).
* Follow us on Twitter: [@JasmineBDD](http://twitter.com/JasmineBDD).
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
@@ -71,8 +58,4 @@ for the supported environments for each Jasmine release.
* [Christian Williams](mailto:antixian666@gmail.com)
* Sheel Choksi
Copyright (c) 2008-2022 Jasmine Maintainers. This software is licensed under the MIT License.
## License
[![FOSSA Status](https://app.fossa.io/api/projects/git%2Bgithub.com%2Fjasmine%2Fjasmine.svg?type=large)](https://app.fossa.io/projects/git%2Bgithub.com%2Fjasmine%2Fjasmine?ref=badge_large)
Copyright (c) 2008-2022 Jasmine Maintainers. This software is licensed under the [MIT License](https://github.com/jasmine/jasmine/blob/main/MIT.LICENSE).

View File

@@ -18,12 +18,11 @@ 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 in the following cases:
Note that Jasmine should only use the "patch" version number if the new release
contains only bug fixes.
* Changes related to packaging for a specific binding library (npm or browser-runner)
* Fixes for regressions.
When jasmine-core revs its major or minor version, the binding libraries should also rev to that version.
When `jasmine-core` revs its major or minor version, the `jasmine` NPM package
should also rev to that version.
## Release
@@ -35,10 +34,10 @@ When ready to release - specs are all green and the stories are done:
### Commit and push core changes
1. Run the browser tests using `scripts/run-all-browsers`.
1. Commit release notes and version changes (jasmine.js, package.json)
1. Push
1. Wait for Circle CI to go green
2. Push
3. Tag the release and push the tag.
4. Wait for Circle CI to go green
### Build standalone distribution
@@ -47,34 +46,27 @@ When ready to release - specs are all green and the stories are done:
### Release the core NPM module
1. Run the tests on Windows. (CI only tests on Linux.)
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.
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 binding libraries
### Release the `jasmine` NPM package
#### NPM
See <https://github.com/jasmine/jasmine-npm/blob/main/RELEASE.md>.
1. Create release notes using Anchorman as above
1. In `package.json`, update both the package version and the jasmine-core dependency version
1. Commit and push.
1. Wait for Circle CI to go green again.
1. Run the tests on Windows locally.
1. `grunt release `. (Note: This will publish the package by running `npm publish`.)
### Publish the GitHub release
### Finally
For each of the above GitHub repos:
1. Visit the releases page and find the tag just published.
1. Paste in a link to the correct release notes for this release. The link should reference the blob and tag correctly, and the markdown file for the notes.
1. If it is a pre-release, mark it as such.
1. For core, attach the standalone zipfile.
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.

View File

@@ -37,16 +37,16 @@ module.exports.noGlobals = (function() {
};
}());
var path = require('path'),
fs = require('fs');
const path = require('path'),
fs = require('fs');
var 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);
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()) {

View File

@@ -1,5 +1,5 @@
/*
Copyright (c) 2008-2022 Pivotal Labs
Copyright (c) 2008-2023 Pivotal Labs
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
@@ -27,14 +27,14 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
source files or spec files are loaded.
*/
(function() {
var jasmineRequire = window.jasmineRequire || require('./jasmine.js');
const jasmineRequire = window.jasmineRequire || require('./jasmine.js');
/**
* ## Require &amp; Instantiate
*
* Require Jasmine's core files. Specifically, this requires and attaches all of Jasmine's code to the `jasmine` reference.
*/
var jasmine = jasmineRequire.core(jasmineRequire),
const jasmine = jasmineRequire.core(jasmineRequire),
global = jasmine.getGlobal();
global.jasmine = jasmine;
@@ -46,19 +46,19 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
/**
* Create the Jasmine environment. This is used to run all specs in a project.
*/
var env = jasmine.getEnv();
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.
*/
var jasmineInterface = jasmineRequire.interface(jasmine, env);
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 (var property in jasmineInterface) {
for (const property in jasmineInterface) {
global[property] = jasmineInterface[property];
}
})();

View File

@@ -1,5 +1,5 @@
/*
Copyright (c) 2008-2022 Pivotal Labs
Copyright (c) 2008-2023 Pivotal Labs
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
@@ -34,7 +34,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
(function() {
var env = jasmine.getEnv();
const env = jasmine.getEnv();
/**
* ## Runner Parameters
@@ -42,15 +42,15 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
* More browser specific code - wrap the query string in an object and to allow for getting/setting parameters from the runner user interface.
*/
var queryString = new jasmine.QueryString({
const queryString = new jasmine.QueryString({
getWindowLocation: function() {
return window.location;
}
});
var filterSpecs = !!queryString.getParam('spec');
const filterSpecs = !!queryString.getParam('spec');
var config = {
const config = {
stopOnSpecFailure: queryString.getParam('stopOnSpecFailure'),
stopSpecOnExpectationFailure: queryString.getParam(
'stopSpecOnExpectationFailure'
@@ -58,13 +58,13 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
hideDisabled: queryString.getParam('hideDisabled')
};
var random = queryString.getParam('random');
const random = queryString.getParam('random');
if (random !== undefined && random !== '') {
config.random = random;
}
var seed = queryString.getParam('seed');
const seed = queryString.getParam('seed');
if (seed) {
config.seed = seed;
}
@@ -73,7 +73,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
* ## 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).
*/
var htmlReporter = new jasmine.HtmlReporter({
const htmlReporter = new jasmine.HtmlReporter({
env: env,
navigateWithNewParam: function(key, value) {
return queryString.navigateWithNewParam(key, value);
@@ -103,7 +103,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
/**
* Filter which specs will be run by matching the start of the full name against the `spec` query param.
*/
var specFilter = new jasmine.HtmlSpecFilter({
const specFilter = new jasmine.HtmlSpecFilter({
filterString: function() {
return queryString.getParam('spec');
}
@@ -120,7 +120,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* 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.
*/
var currentWindowOnload = window.onload;
const currentWindowOnload = window.onload;
window.onload = function() {
if (currentWindowOnload) {

View File

@@ -1,24 +1,24 @@
function Player() {
}
Player.prototype.play = function(song) {
this.currentlyPlayingSong = song;
this.isPlaying = true;
};
Player.prototype.pause = function() {
this.isPlaying = false;
};
Player.prototype.resume = function() {
if (this.isPlaying) {
throw new Error("song is already playing");
class Player {
play(song) {
this.currentlyPlayingSong = song;
this.isPlaying = true;
}
this.isPlaying = true;
};
pause() {
this.isPlaying = false;
}
Player.prototype.makeFavorite = function() {
this.currentlyPlayingSong.persistFavoriteStatus(true);
};
resume() {
if (this.isPlaying) {
throw new Error('song is already playing');
}
this.isPlaying = true;
}
makeFavorite() {
this.currentlyPlayingSong.persistFavoriteStatus(true);
}
}
module.exports = Player;

View File

@@ -1,9 +1,8 @@
function Song() {
class Song {
persistFavoriteStatus(value) {
// something complicated
throw new Error('not yet implemented');
}
}
Song.prototype.persistFavoriteStatus = function(value) {
// something complicated
throw new Error("not yet implemented");
};
module.exports = Song;

View File

@@ -3,11 +3,11 @@ beforeEach(function () {
toBePlaying: function () {
return {
compare: function (actual, expected) {
var player = actual;
const player = actual;
return {
pass: player.currentlyPlayingSong === expected && player.isPlaying
}
};
}
};
}

View File

@@ -1,36 +1,37 @@
describe("Player", function() {
var Player = require('../../lib/jasmine_examples/Player');
var Song = require('../../lib/jasmine_examples/Song');
var player;
var song;
const 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() {
it('should be able to play a Song', function() {
player.play(song);
expect(player.currentlyPlayingSong).toEqual(song);
//demonstrates use of custom matcher
// demonstrates use of custom matcher
expect(player).toBePlaying(song);
});
describe("when song has been paused", function() {
describe('when song has been paused', function() {
beforeEach(function() {
player.play(song);
player.pause();
});
it("should indicate that the song is currently paused", function() {
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() {
it('should be possible to resume', function() {
player.resume();
expect(player.isPlaying).toBeTruthy();
expect(player.currentlyPlayingSong).toEqual(song);
@@ -38,7 +39,7 @@ describe("Player", function() {
});
// demonstrates use of spies to intercept and test method calls
it("tells the current song if the user has made it a favorite", function() {
it('tells the current song if the user has made it a favorite', function() {
spyOn(song, 'persistFavoriteStatus');
player.play(song);
@@ -48,13 +49,13 @@ describe("Player", function() {
});
//demonstrates use of expected exceptions
describe("#resume", function() {
it("should throw an exception if song is already playing", function() {
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");
}).toThrowError('song is already playing');
});
});
});

View File

@@ -1,34 +1,34 @@
describe("Player", function() {
var player;
var 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() {
it('should be able to play a Song', function() {
player.play(song);
expect(player.currentlyPlayingSong).toEqual(song);
//demonstrates use of custom matcher
// demonstrates use of custom matcher
expect(player).toBePlaying(song);
});
describe("when song has been paused", function() {
describe('when song has been paused', function() {
beforeEach(function() {
player.play(song);
player.pause();
});
it("should indicate that the song is currently paused", function() {
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() {
it('should be possible to resume', function() {
player.resume();
expect(player.isPlaying).toBeTruthy();
expect(player.currentlyPlayingSong).toEqual(song);
@@ -36,7 +36,7 @@ describe("Player", function() {
});
// demonstrates use of spies to intercept and test method calls
it("tells the current song if the user has made it a favorite", function() {
it('tells the current song if the user has made it a favorite', function() {
spyOn(song, 'persistFavoriteStatus');
player.play(song);
@@ -46,13 +46,13 @@ describe("Player", function() {
});
//demonstrates use of expected exceptions
describe("#resume", function() {
it("should throw an exception if song is already playing", function() {
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");
}).toThrowError('song is already playing');
});
});
});

View File

@@ -3,7 +3,7 @@ beforeEach(function () {
toBePlaying: function () {
return {
compare: function (actual, expected) {
var player = actual;
const player = actual;
return {
pass: player.currentlyPlayingSong === expected && player.isPlaying

View File

@@ -1,22 +1,22 @@
function Player() {
}
Player.prototype.play = function(song) {
this.currentlyPlayingSong = song;
this.isPlaying = true;
};
Player.prototype.pause = function() {
this.isPlaying = false;
};
Player.prototype.resume = function() {
if (this.isPlaying) {
throw new Error("song is already playing");
class Player {
play(song) {
this.currentlyPlayingSong = song;
this.isPlaying = true;
}
this.isPlaying = true;
};
pause() {
this.isPlaying = false;
}
Player.prototype.makeFavorite = function() {
this.currentlyPlayingSong.persistFavoriteStatus(true);
};
resume() {
if (this.isPlaying) {
throw new Error('song is already playing');
}
this.isPlaying = true;
}
makeFavorite() {
this.currentlyPlayingSong.persistFavoriteStatus(true);
}
}

View File

@@ -1,7 +1,6 @@
function Song() {
class Song {
persistFavoriteStatus(value) {
// something complicated
throw new Error('not yet implemented');
}
}
Song.prototype.persistFavoriteStatus = function(value) {
// something complicated
throw new Error("not yet implemented");
};

View File

@@ -1,5 +1,5 @@
/*
Copyright (c) 2008-2022 Pivotal Labs
Copyright (c) 2008-2023 Pivotal Labs
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
@@ -20,6 +20,7 @@ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
// eslint-disable-next-line no-var
var jasmineRequire = window.jasmineRequire || require('./jasmine.js');
jasmineRequire.html = function(j$) {
@@ -79,19 +80,21 @@ jasmineRequire.HtmlReporter = function(j$) {
};
function HtmlReporter(options) {
var config = function() {
return (options.env && options.env.configuration()) || {};
},
getContainer = options.getContainer,
createElement = options.createElement,
createTextNode = options.createTextNode,
navigateWithNewParam = options.navigateWithNewParam || function() {},
addToExistingQueryString =
options.addToExistingQueryString || defaultQueryString,
filterSpecs = options.filterSpecs,
htmlReporterMain,
symbols,
deprecationWarnings = [];
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();
@@ -119,14 +122,14 @@ jasmineRequire.HtmlReporter = function(j$) {
getContainer().appendChild(htmlReporterMain);
};
var totalSpecsDefined;
let totalSpecsDefined;
this.jasmineStarted = function(options) {
totalSpecsDefined = options.totalSpecsDefined || 0;
};
var summary = createDom('div', { className: 'jasmine-summary' });
const summary = createDom('div', { className: 'jasmine-summary' });
var stateBuilder = new ResultsStateBuilder();
const stateBuilder = new ResultsStateBuilder();
this.suiteStarted = function(result) {
stateBuilder.suiteStarted(result);
@@ -145,12 +148,11 @@ jasmineRequire.HtmlReporter = function(j$) {
stateBuilder.specStarted(result);
};
var failures = [];
this.specDone = function(result) {
stateBuilder.specDone(result);
if (noExpectations(result)) {
var noSpecMsg = "Spec '" + result.fullName + "' has no expectations.";
const noSpecMsg = "Spec '" + result.fullName + "' has no expectations.";
if (result.status === 'failed') {
console.error(noSpecMsg);
} else {
@@ -194,10 +196,10 @@ jasmineRequire.HtmlReporter = function(j$) {
this.jasmineDone = function(doneResult) {
stateBuilder.jasmineDone(doneResult);
var banner = find('.jasmine-banner');
var alert = find('.jasmine-alert');
var order = doneResult && doneResult.order;
var i;
const banner = find('.jasmine-banner');
const alert = find('.jasmine-alert');
const order = doneResult && doneResult.order;
alert.appendChild(
createDom(
'span',
@@ -209,14 +211,14 @@ jasmineRequire.HtmlReporter = function(j$) {
banner.appendChild(optionsMenu(config()));
if (stateBuilder.specsExecuted < totalSpecsDefined) {
var skippedMessage =
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
var skippedLink =
const skippedLink =
(window.location.pathname || '') +
addToExistingQueryString('spec', '');
alert.appendChild(
@@ -231,10 +233,11 @@ jasmineRequire.HtmlReporter = function(j$) {
)
);
}
var statusBarMessage = '';
var statusBarClassName = 'jasmine-overall-result jasmine-bar ';
var globalFailures = (doneResult && doneResult.failedExpectations) || [];
var failed = stateBuilder.failureCount + globalFailures.length > 0;
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 +=
@@ -260,7 +263,7 @@ jasmineRequire.HtmlReporter = function(j$) {
statusBarClassName += ' jasmine-failed ';
}
var seedBar;
let seedBar;
if (order && order.random) {
seedBar = createDom(
'span',
@@ -286,10 +289,10 @@ jasmineRequire.HtmlReporter = function(j$) {
)
);
var errorBarClassName = 'jasmine-bar jasmine-errored';
var afterAllMessagePrefix = 'AfterAll ';
const errorBarClassName = 'jasmine-bar jasmine-errored';
const afterAllMessagePrefix = 'AfterAll ';
for (i = 0; i < globalFailures.length; i++) {
for (let i = 0; i < globalFailures.length; i++) {
alert.appendChild(
createDom(
'span',
@@ -301,7 +304,7 @@ jasmineRequire.HtmlReporter = function(j$) {
function globalFailureMessage(failure) {
if (failure.globalErrorType === 'load') {
var prefix = 'Error during loading: ' + failure.message;
const prefix = 'Error during loading: ' + failure.message;
if (failure.filename) {
return (
@@ -319,9 +322,9 @@ jasmineRequire.HtmlReporter = function(j$) {
addDeprecationWarnings(doneResult);
for (i = 0; i < deprecationWarnings.length; i++) {
var children = [],
context;
for (let i = 0; i < deprecationWarnings.length; i++) {
const children = [];
let context;
switch (deprecationWarnings[i].runnableType) {
case 'spec':
@@ -355,7 +358,7 @@ jasmineRequire.HtmlReporter = function(j$) {
);
}
var results = find('.jasmine-results');
const results = find('.jasmine-results');
results.appendChild(summary);
summaryList(stateBuilder.topResults, summary);
@@ -397,8 +400,8 @@ jasmineRequire.HtmlReporter = function(j$) {
setMenuModeTo('jasmine-failure-list');
var failureNode = find('.jasmine-failures');
for (i = 0; i < failures.length; i++) {
const failureNode = find('.jasmine-failures');
for (let i = 0; i < failures.length; i++) {
failureNode.appendChild(failures[i]);
}
}
@@ -407,16 +410,16 @@ jasmineRequire.HtmlReporter = function(j$) {
return this;
function failureDom(result) {
var failure = createDom(
const failure = createDom(
'div',
{ className: 'jasmine-spec-detail jasmine-failed' },
failureDescription(result, stateBuilder.currentParent),
createDom('div', { className: 'jasmine-messages' })
);
var messages = failure.childNodes[1];
const messages = failure.childNodes[1];
for (var i = 0; i < result.failedExpectations.length; i++) {
var expectation = result.failedExpectations[i];
for (let i = 0; i < result.failedExpectations.length; i++) {
const expectation = result.failedExpectations[i];
messages.appendChild(
createDom(
'div',
@@ -451,7 +454,7 @@ jasmineRequire.HtmlReporter = function(j$) {
}
function debugLogTable(debugLogs) {
var tbody = createDom('tbody');
const tbody = createDom('tbody');
debugLogs.forEach(function(entry) {
tbody.appendChild(
@@ -491,14 +494,14 @@ jasmineRequire.HtmlReporter = function(j$) {
}
function summaryList(resultsTree, domParent) {
var specListNode;
for (var i = 0; i < resultsTree.children.length; i++) {
var resultNode = resultsTree.children[i];
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') {
var suiteListNode = createDom(
const suiteListNode = createDom(
'ul',
{ className: 'jasmine-suite', id: 'suite-' + resultNode.result.id },
createDom(
@@ -523,7 +526,7 @@ jasmineRequire.HtmlReporter = function(j$) {
specListNode = createDom('ul', { className: 'jasmine-specs' });
domParent.appendChild(specListNode);
}
var specDescription = resultNode.result.description;
let specDescription = resultNode.result.description;
if (noExpectations(resultNode.result)) {
specDescription = 'SPEC HAS NO EXPECTATIONS ' + specDescription;
}
@@ -555,7 +558,7 @@ jasmineRequire.HtmlReporter = function(j$) {
}
function optionsMenu(config) {
var optionsMenuDom = createDom(
const optionsMenuDom = createDom(
'div',
{ className: 'jasmine-run-options' },
createDom('span', { className: 'jasmine-trigger' }, 'Options'),
@@ -621,13 +624,15 @@ jasmineRequire.HtmlReporter = function(j$) {
)
);
var failFastCheckbox = optionsMenuDom.querySelector('#jasmine-fail-fast');
const failFastCheckbox = optionsMenuDom.querySelector(
'#jasmine-fail-fast'
);
failFastCheckbox.checked = config.stopOnSpecFailure;
failFastCheckbox.onclick = function() {
navigateWithNewParam('stopOnSpecFailure', !config.stopOnSpecFailure);
};
var throwCheckbox = optionsMenuDom.querySelector(
const throwCheckbox = optionsMenuDom.querySelector(
'#jasmine-throw-failures'
);
throwCheckbox.checked = config.stopSpecOnExpectationFailure;
@@ -638,7 +643,7 @@ jasmineRequire.HtmlReporter = function(j$) {
);
};
var randomCheckbox = optionsMenuDom.querySelector(
const randomCheckbox = optionsMenuDom.querySelector(
'#jasmine-random-order'
);
randomCheckbox.checked = config.random;
@@ -646,13 +651,15 @@ jasmineRequire.HtmlReporter = function(j$) {
navigateWithNewParam('random', !config.random);
};
var hideDisabled = optionsMenuDom.querySelector('#jasmine-hide-disabled');
const hideDisabled = optionsMenuDom.querySelector(
'#jasmine-hide-disabled'
);
hideDisabled.checked = config.hideDisabled;
hideDisabled.onclick = function() {
navigateWithNewParam('hideDisabled', !config.hideDisabled);
};
var optionsTrigger = optionsMenuDom.querySelector('.jasmine-trigger'),
const optionsTrigger = optionsMenuDom.querySelector('.jasmine-trigger'),
optionsPayload = optionsMenuDom.querySelector('.jasmine-payload'),
isOpen = /\bjasmine-open\b/;
@@ -671,7 +678,7 @@ jasmineRequire.HtmlReporter = function(j$) {
}
function failureDescription(result, suite) {
var wrapper = createDom(
const wrapper = createDom(
'div',
{ className: 'jasmine-description' },
createDom(
@@ -680,7 +687,7 @@ jasmineRequire.HtmlReporter = function(j$) {
result.description
)
);
var suiteLink;
let suiteLink;
while (suite && suite.parent) {
wrapper.insertBefore(createTextNode(' > '), wrapper.firstChild);
@@ -698,7 +705,7 @@ jasmineRequire.HtmlReporter = function(j$) {
}
function suiteHref(suite) {
var els = [];
const els = [];
while (suite && suite.parent) {
els.unshift(suite.result.description);
@@ -714,8 +721,8 @@ jasmineRequire.HtmlReporter = function(j$) {
function addDeprecationWarnings(result, runnableType) {
if (result && result.deprecationWarnings) {
for (var i = 0; i < result.deprecationWarnings.length; i++) {
var warning = result.deprecationWarnings[i].message;
for (let i = 0; i < result.deprecationWarnings.length; i++) {
const warning = result.deprecationWarnings[i].message;
deprecationWarnings.push({
message: warning,
stack: result.deprecationWarnings[i].stack,
@@ -727,8 +734,8 @@ jasmineRequire.HtmlReporter = function(j$) {
}
function createExpander(stackTrace) {
var expandLink = createDom('a', { href: '#' }, 'Show stack trace');
var root = createDom(
const expandLink = createDom('a', { href: '#' }, 'Show stack trace');
const root = createDom(
'div',
{ className: 'jasmine-expander' },
expandLink,
@@ -759,8 +766,7 @@ jasmineRequire.HtmlReporter = function(j$) {
}
function clearPrior() {
// return the reporter
var oldReporter = find('');
const oldReporter = find('');
if (oldReporter) {
getContainer().removeChild(oldReporter);
@@ -768,22 +774,21 @@ jasmineRequire.HtmlReporter = function(j$) {
}
function createDom(type, attrs, childrenArrayOrVarArgs) {
var el = createElement(type),
children,
i;
const el = createElement(type);
let children;
if (j$.isArray_(childrenArrayOrVarArgs)) {
children = childrenArrayOrVarArgs;
} else {
children = [];
for (i = 2; i < arguments.length; i++) {
for (let i = 2; i < arguments.length; i++) {
children.push(arguments[i]);
}
}
for (i = 0; i < children.length; i++) {
var child = children[i];
for (let i = 0; i < children.length; i++) {
const child = children[i];
if (typeof child === 'string') {
el.appendChild(createTextNode(child));
@@ -794,7 +799,7 @@ jasmineRequire.HtmlReporter = function(j$) {
}
}
for (var attr in attrs) {
for (const attr in attrs) {
if (attr == 'className') {
el[attr] = attrs[attr];
} else {
@@ -806,7 +811,7 @@ jasmineRequire.HtmlReporter = function(j$) {
}
function pluralize(singular, count) {
var word = count == 1 ? singular : singular + 's';
const word = count == 1 ? singular : singular + 's';
return '' + count + ' ' + word;
}
@@ -836,7 +841,7 @@ jasmineRequire.HtmlReporter = function(j$) {
}
function noExpectations(result) {
var allExpectations =
const allExpectations =
result.failedExpectations.length + result.passedExpectations.length;
return (
@@ -851,7 +856,7 @@ jasmineRequire.HtmlReporter = function(j$) {
}
if (resultNode.type == 'suite') {
for (var i = 0, j = resultNode.children.length; i < j; i++) {
for (let i = 0, j = resultNode.children.length; i < j; i++) {
if (hasActiveSpec(resultNode.children[i])) {
return true;
}
@@ -865,11 +870,11 @@ jasmineRequire.HtmlReporter = function(j$) {
jasmineRequire.HtmlSpecFilter = function() {
function HtmlSpecFilter(options) {
var filterString =
const filterString =
options &&
options.filterString() &&
options.filterString().replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&');
var filterPattern = new RegExp(filterString);
const filterPattern = new RegExp(filterString);
this.matches = function(specName) {
return filterPattern.test(specName);
@@ -913,7 +918,7 @@ jasmineRequire.QueryString = function() {
};
this.fullStringWithNewParam = function(key, value) {
var paramMap = queryStringToParamMap();
const paramMap = queryStringToParamMap();
paramMap[key] = value;
return toQueryString(paramMap);
};
@@ -925,8 +930,8 @@ jasmineRequire.QueryString = function() {
return this;
function toQueryString(paramMap) {
var qStrPairs = [];
for (var prop in paramMap) {
const qStrPairs = [];
for (const prop in paramMap) {
qStrPairs.push(
encodeURIComponent(prop) + '=' + encodeURIComponent(paramMap[prop])
);
@@ -935,15 +940,15 @@ jasmineRequire.QueryString = function() {
}
function queryStringToParamMap() {
var paramStr = options.getWindowLocation().search.substring(1),
params = [],
paramMap = {};
const paramStr = options.getWindowLocation().search.substring(1);
let params = [];
const paramMap = {};
if (paramStr.length > 0) {
params = paramStr.split('&');
for (var i = 0; i < params.length; i++) {
var p = params[i].split('=');
var value = decodeURIComponent(p[1]);
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);
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +1,5 @@
/*
Copyright (c) 2008-2022 Pivotal Labs
Copyright (c) 2008-2023 Pivotal Labs
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
@@ -21,16 +21,16 @@ 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) {
var jasmine = jasmineRequire.core(jasmineRequire);
const jasmine = jasmineRequire.core(jasmineRequire);
var env = jasmine.getEnv({ suppressLoadErrors: true });
const env = jasmine.getEnv({ suppressLoadErrors: true });
var jasmineInterface = jasmineRequire.interface(jasmine, env);
const jasmineInterface = jasmineRequire.interface(jasmine, env);
extend(global, jasmineInterface);
function extend(destination, source) {
for (var property in source) destination[property] = source[property];
for (const property in source) destination[property] = source[property];
return destination;
}

View File

@@ -1,7 +1,7 @@
{
"name": "jasmine-core",
"license": "MIT",
"version": "4.1.1",
"version": "4.6.0",
"repository": {
"type": "git",
"url": "https://github.com/jasmine/jasmine.git"
@@ -35,20 +35,20 @@
],
"devDependencies": {
"eslint": "^7.32.0",
"eslint-plugin-compat": "^4.0.0",
"eslint-plugin-compat": ">=4.0.0 <4.1.0",
"glob": "^7.2.0",
"grunt": "^1.0.4",
"grunt": ">=1.0.4 <1.6.0",
"grunt-cli": "^1.3.2",
"grunt-contrib-compress": "^2.0.0",
"grunt-contrib-concat": "^2.0.0",
"grunt-css-url-embed": "^1.11.1",
"grunt-sass": "^3.0.2",
"jasmine": "github:jasmine/jasmine-npm#main",
"jasmine-browser-runner": "github:jasmine/jasmine-browser#main",
"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",
"sass": "1.58.3",
"shelljs": "^0.8.3",
"temp": "^0.9.0"
},
@@ -95,14 +95,16 @@
"error",
"always"
],
"space-before-blocks": "error"
"space-before-blocks": "error",
"no-eval": "error",
"no-var": "error"
}
},
"browserslist": [
"Safari >= 13",
"Safari >= 14",
"last 2 Chrome versions",
"last 2 Firefox versions",
"Firefox >= 68",
"Firefox >= 91",
"last 2 Edge versions"
]
}

41
release_notes/4.2.0.md Normal file
View File

@@ -0,0 +1,41 @@
# 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)_

40
release_notes/4.3.0.md Normal file
View File

@@ -0,0 +1,40 @@
# 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)_

28
release_notes/4.4.0.md Normal file
View File

@@ -0,0 +1,28 @@
# 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)_

40
release_notes/4.5.0.md Normal file
View File

@@ -0,0 +1,40 @@
# 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)_

19
release_notes/4.6.0.md Normal file
View File

@@ -0,0 +1,19 @@
# Jasmine Core 4.6.0 Release Notes
## New Features
* Report the ID of each suite/spec's parent
* Report the path/url of the file that the spec/suite was defined in
* Fixes [#1884](https://github.com/jasmine/jasmine/issues/1884)
* Added Firefox 102 (current ESR) to supported browsers
## Internal improvements
* Pinned sass to 1.58.3 for compatibility with Node 12
* Pinned eslint-plugin-compat to <4.1.0 for compatibility with Node 12
* Pinned Grunt to <1.6.0 for compatibility with Node 12
------
_Release Notes generated with _[Anchorman](http://github.com/infews/anchorman)_

View File

@@ -25,7 +25,9 @@ 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

View File

@@ -14,8 +14,6 @@ module.exports = {
// TODO: consider doing this in src files as well as specs
'no-unused-vars': ['error', { args: 'after-used' }],
'no-var': 'error',
// 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.

View File

@@ -9,160 +9,216 @@ describe('ClearStack', function() {
});
});
it('uses setImmediate when available', function() {
const setImmediate = jasmine
.createSpy('setImmediate')
.and.callFake(function(fn) {
fn();
}),
global = { setImmediate: setImmediate },
clearStack = jasmineUnderTest.getClearStack(global);
let called = false;
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
};
});
});
clearStack(function() {
called = true;
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'
}
};
});
expect(called).toBe(true);
expect(setImmediate).toHaveBeenCalled();
describe('when MessageChannel is unavailable', function() {
usesQueueMicrotaskWithSetTimeout(function() {
return {
navigator: {
userAgent: 'CERN-LineMode/2.15 libwww/2.17b3',
MessageChannel: undefined
}
};
});
});
});
it('uses setTimeout instead of setImmediate every 10 calls to make sure we release the CPU', function() {
const setImmediate = jasmine.createSpy('setImmediate'),
setTimeout = jasmine.createSpy('setTimeout'),
global = { setImmediate: setImmediate, setTimeout: setTimeout },
clearStack = jasmineUnderTest.getClearStack(global);
clearStack(function() {});
clearStack(function() {});
clearStack(function() {});
clearStack(function() {});
clearStack(function() {});
clearStack(function() {});
clearStack(function() {});
clearStack(function() {});
clearStack(function() {});
expect(setImmediate).toHaveBeenCalled();
expect(setTimeout).not.toHaveBeenCalled();
clearStack(function() {});
expect(setImmediate.calls.count()).toEqual(9);
expect(setTimeout.calls.count()).toEqual(1);
clearStack(function() {});
expect(setImmediate.calls.count()).toEqual(10);
expect(setTimeout.calls.count()).toEqual(1);
});
it('uses MessageChannels when available', function() {
const fakeChannel = {
port1: {},
port2: {
postMessage: function() {
fakeChannel.port1.onmessage();
describe('in Node', function() {
usesQueueMicrotaskWithoutSetTimeout(function() {
return {
process: {
versions: {
node: '3.1415927'
}
}
},
global = {
MessageChannel: function() {
return fakeChannel;
}
},
clearStack = jasmineUnderTest.getClearStack(global);
let called = false;
};
});
});
clearStack(function() {
called = true;
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);
});
expect(called).toBe(true);
});
it('uses setTimeout instead of MessageChannel every 10 calls to make sure we release the CPU', function() {
const fakeChannel = {
port1: {},
port2: {
postMessage: jasmine
.createSpy('postMessage')
.and.callFake(function() {
fakeChannel.port1.onmessage();
})
}
},
setTimeout = jasmine.createSpy('setTimeout'),
global = {
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;
},
setTimeout: setTimeout
},
clearStack = jasmineUnderTest.getClearStack(global);
clearStack(function() {});
clearStack(function() {});
clearStack(function() {});
clearStack(function() {});
clearStack(function() {});
clearStack(function() {});
clearStack(function() {});
clearStack(function() {});
clearStack(function() {});
expect(fakeChannel.port2.postMessage).toHaveBeenCalled();
expect(setTimeout).not.toHaveBeenCalled();
clearStack(function() {});
expect(fakeChannel.port2.postMessage.calls.count()).toEqual(9);
expect(setTimeout.calls.count()).toEqual(1);
clearStack(function() {});
expect(fakeChannel.port2.postMessage.calls.count()).toEqual(10);
expect(setTimeout.calls.count()).toEqual(1);
});
it('calls setTimeout when onmessage is called recursively', function() {
const fakeChannel = {
port1: {},
port2: {
postMessage: function() {
fakeChannel.port1.onmessage();
}
}
},
setTimeout = jasmine.createSpy('setTimeout'),
global = {
MessageChannel: function() {
return fakeChannel;
},
setTimeout: setTimeout
},
clearStack = jasmineUnderTest.getClearStack(global),
fn = jasmine.createSpy('second clearStack function');
};
const clearStack = jasmineUnderTest.getClearStack(global);
clearStack(function() {
clearStack(fn);
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);
});
expect(fn).not.toHaveBeenCalled();
expect(setTimeout).toHaveBeenCalledWith(fn, 0);
});
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');
it('falls back to setTimeout', function() {
const setTimeout = jasmine
.createSpy('setTimeout')
.and.callFake(function(fn) {
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();
}),
global = { setTimeout: setTimeout },
clearStack = jasmineUnderTest.getClearStack(global);
let called = false;
}
};
const clearStack = jasmineUnderTest.getClearStack(global);
let called = false;
clearStack(function() {
called = true;
clearStack(function() {
called = true;
});
expect(called).toBe(true);
});
expect(called).toBe(true);
expect(setTimeout).toHaveBeenCalledWith(jasmine.any(Function), 0);
});
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;
}
});

View File

@@ -316,7 +316,7 @@ describe('Env', function() {
it('calls spec.exclude with "Temporarily disabled with xit"', function() {
const excludeSpy = jasmine.createSpy();
spyOn(env, 'it_').and.returnValue({
spyOn(jasmineUnderTest.SuiteBuilder.prototype, 'it_').and.returnValue({
exclude: excludeSpy
});
env.xit('foo', function() {});
@@ -327,7 +327,7 @@ describe('Env', function() {
const pendSpy = jasmine.createSpy();
const realExclude = jasmineUnderTest.Spec.prototype.exclude;
spyOn(env, 'it_').and.returnValue({
spyOn(jasmineUnderTest.SuiteBuilder.prototype, 'it_').and.returnValue({
exclude: realExclude,
pend: pendSpy
});
@@ -468,7 +468,8 @@ describe('Env', function() {
'install',
'uninstall',
'pushListener',
'popListener'
'popListener',
'removeOverrideListener'
]);
spyOn(jasmineUnderTest, 'GlobalErrors').and.returnValue(globalErrors);
env.cleanup_();
@@ -483,7 +484,8 @@ describe('Env', function() {
'install',
'uninstall',
'pushListener',
'popListener'
'popListener',
'removeOverrideListener'
]);
spyOn(jasmineUnderTest, 'GlobalErrors').and.returnValue(globalErrors);
env.cleanup_();
@@ -494,7 +496,7 @@ describe('Env', function() {
});
});
it('creates an expectationFactory that uses the current custom equality testers and object formatters', function(done) {
it('creates an expectationFactory that uses the current custom equality testers and object formatters', async function() {
function customEqualityTester() {}
function customObjectFormatter() {}
function prettyPrinter() {}
@@ -515,19 +517,17 @@ describe('Env', function() {
expectationFactory('actual', specInstance);
});
env.execute(null, function() {
expect(jasmineUnderTest.makePrettyPrinter).toHaveBeenCalledWith([
customObjectFormatter
]);
expect(jasmineUnderTest.MatchersUtil).toHaveBeenCalledWith({
customTesters: [customEqualityTester],
pp: prettyPrinter
});
done();
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', function(done) {
it('creates an asyncExpectationFactory that uses the current custom equality testers and object formatters', async function() {
function customEqualityTester() {}
function customObjectFormatter() {}
function prettyPrinter() {}
@@ -548,15 +548,14 @@ describe('Env', function() {
asyncExpectationFactory('actual', specInstance);
});
env.execute(null, function() {
expect(jasmineUnderTest.makePrettyPrinter).toHaveBeenCalledWith([
customObjectFormatter
]);
expect(jasmineUnderTest.MatchersUtil).toHaveBeenCalledWith({
customTesters: [customEqualityTester],
pp: prettyPrinter
});
done();
await env.execute();
expect(jasmineUnderTest.makePrettyPrinter).toHaveBeenCalledWith([
customObjectFormatter
]);
expect(jasmineUnderTest.MatchersUtil).toHaveBeenCalledWith({
customTesters: [customEqualityTester],
pp: prettyPrinter
});
});
@@ -594,4 +593,19 @@ describe('Env', function() {
});
});
});
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);
});
});
});

View File

@@ -256,5 +256,51 @@ describe('ExceptionFormatter', function() {
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');
});
});
});
});

View File

@@ -9,7 +9,7 @@ describe('Exceptions:', function() {
env.cleanup_();
});
it('should handle exceptions thrown, but continue', function(done) {
it('should handle exceptions thrown, but continue', async function() {
const secondTest = jasmine.createSpy('second test');
env.describe('Suite for handles exceptions', function() {
env.it(
@@ -24,15 +24,12 @@ describe('Exceptions:', function() {
);
});
const expectations = function() {
expect(secondTest).toHaveBeenCalled();
done();
};
await env.execute();
env.execute(null, expectations);
expect(secondTest).toHaveBeenCalled();
});
it('should handle exceptions thrown directly in top-level describe blocks and continue', function(done) {
it('should handle exceptions thrown directly in top-level describe blocks and continue', async function() {
const secondDescribe = jasmine
.createSpy('second describe')
.and.callFake(function() {
@@ -47,11 +44,8 @@ describe('Exceptions:', function() {
});
env.describe("a suite that doesn't throw an exception", secondDescribe);
const expectations = function() {
expect(secondDescribe).toHaveBeenCalled();
done();
};
await env.execute();
env.execute(null, expectations);
expect(secondDescribe).toHaveBeenCalled();
});
});

View File

@@ -1,8 +1,8 @@
describe('GlobalErrors', function() {
it('calls the added handler on error', function() {
const fakeGlobal = { onerror: null },
handler = jasmine.createSpy('errorHandler'),
errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
const fakeGlobal = minimalBrowserGlobal();
const handler = jasmine.createSpy('errorHandler');
const errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
errors.install();
errors.pushListener(handler);
@@ -13,10 +13,10 @@ describe('GlobalErrors', function() {
});
it('enables external interception of error by overriding global.onerror', function() {
const fakeGlobal = { onerror: null },
handler = jasmine.createSpy('errorHandler'),
hijackHandler = jasmine.createSpy('hijackErrorHandler'),
errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
const fakeGlobal = minimalBrowserGlobal();
const handler = jasmine.createSpy('errorHandler');
const hijackHandler = jasmine.createSpy('hijackErrorHandler');
const errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
errors.install();
errors.pushListener(handler);
@@ -30,10 +30,10 @@ describe('GlobalErrors', function() {
});
it('calls the global error handler with all parameters', function() {
const fakeGlobal = { onerror: null },
handler = jasmine.createSpy('errorHandler'),
errors = new jasmineUnderTest.GlobalErrors(fakeGlobal),
fooError = new Error('foo');
const fakeGlobal = minimalBrowserGlobal();
const handler = jasmine.createSpy('errorHandler');
const errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
const fooError = new Error('foo');
errors.install();
errors.pushListener(handler);
@@ -50,10 +50,10 @@ describe('GlobalErrors', function() {
});
it('only calls the most recent handler', function() {
const fakeGlobal = { onerror: null },
handler1 = jasmine.createSpy('errorHandler1'),
handler2 = jasmine.createSpy('errorHandler2'),
errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
const fakeGlobal = minimalBrowserGlobal();
const handler1 = jasmine.createSpy('errorHandler1');
const handler2 = jasmine.createSpy('errorHandler2');
const errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
errors.install();
errors.pushListener(handler1);
@@ -66,10 +66,10 @@ describe('GlobalErrors', function() {
});
it('calls previous handlers when one is removed', function() {
const fakeGlobal = { onerror: null },
handler1 = jasmine.createSpy('errorHandler1'),
handler2 = jasmine.createSpy('errorHandler2'),
errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
const fakeGlobal = minimalBrowserGlobal();
const handler1 = jasmine.createSpy('errorHandler1');
const handler2 = jasmine.createSpy('errorHandler2');
const errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
errors.install();
errors.pushListener(handler1);
@@ -91,9 +91,12 @@ describe('GlobalErrors', function() {
});
it('uninstalls itself, putting back a previous callback', function() {
const originalCallback = jasmine.createSpy('error'),
fakeGlobal = { onerror: originalCallback },
errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
const originalCallback = jasmine.createSpy('error');
const fakeGlobal = {
...minimalBrowserGlobal(),
onerror: originalCallback
};
const errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
expect(fakeGlobal.onerror).toBe(originalCallback);
@@ -107,9 +110,9 @@ describe('GlobalErrors', function() {
});
it('rethrows the original error when there is no handler', function() {
const fakeGlobal = {},
errors = new jasmineUnderTest.GlobalErrors(fakeGlobal),
originalError = new Error('nope');
const fakeGlobal = minimalBrowserGlobal();
const errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
const originalError = new Error('nope');
errors.install();
@@ -404,4 +407,163 @@ describe('GlobalErrors', function() {
});
});
});
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
};
}
});

View File

@@ -323,16 +323,16 @@ describe('PrettyPrinter', function() {
);
});
it('should indicate getters on objects as such', function() {
it('should use the return value of getters', function() {
const pp = jasmineUnderTest.makePrettyPrinter();
const sampleValue = {
id: 1,
get calculatedValue() {
throw new Error("don't call me!");
return 'the getter return value';
}
};
expect(pp(sampleValue)).toEqual(
'Object({ id: 1, calculatedValue: <getter> })'
"Object({ id: 1, calculatedValue: 'the getter return value' })"
);
});

View File

@@ -632,7 +632,8 @@ describe('QueueRunner', function() {
});
it('issues a more specific error if the function is `async`', function() {
eval('var fn = async function(done){};');
// eslint-disable-next-line no-unused-vars
async function fn(done) {}
const onException = jasmine.createSpy('onException'),
queueRunner = new jasmineUnderTest.QueueRunner({
queueableFns: [{ fn: fn }],

View File

@@ -18,13 +18,12 @@ describe('ReportDispatcher', function() {
queueRunnerFactory
),
reporter = jasmine.createSpyObj('reporter', ['foo', 'bar']),
anotherReporter = jasmine.createSpyObj('reporter', ['foo', 'bar']),
completeCallback = jasmine.createSpy('complete');
anotherReporter = jasmine.createSpyObj('reporter', ['foo', 'bar']);
dispatcher.addReporter(reporter);
dispatcher.addReporter(anotherReporter);
dispatcher.foo(123, 456, completeCallback);
dispatcher.foo(123, 456);
expect(queueRunnerFactory).toHaveBeenCalledWith(
jasmine.objectContaining({
@@ -47,7 +46,7 @@ describe('ReportDispatcher', function() {
queueRunnerFactory.calls.reset();
dispatcher.bar('a', 'b', completeCallback);
dispatcher.bar('a', 'b');
expect(queueRunnerFactory).toHaveBeenCalledWith(
jasmine.objectContaining({
@@ -91,11 +90,10 @@ describe('ReportDispatcher', function() {
['foo', 'bar'],
queueRunnerFactory
),
reporter = jasmine.createSpyObj('reporter', ['foo', 'bar']),
completeCallback = jasmine.createSpy('complete');
reporter = jasmine.createSpyObj('reporter', ['foo', 'bar']);
dispatcher.provideFallbackReporter(reporter);
dispatcher.foo(123, 456, completeCallback);
dispatcher.foo(123, 456);
expect(queueRunnerFactory).toHaveBeenCalledWith(
jasmine.objectContaining({
@@ -116,12 +114,11 @@ describe('ReportDispatcher', function() {
queueRunnerFactory
),
reporter = jasmine.createSpyObj('reporter', ['foo', 'bar']),
fallbackReporter = jasmine.createSpyObj('otherReporter', ['foo', 'bar']),
completeCallback = jasmine.createSpy('complete');
fallbackReporter = jasmine.createSpyObj('otherReporter', ['foo', 'bar']);
dispatcher.provideFallbackReporter(fallbackReporter);
dispatcher.addReporter(reporter);
dispatcher.foo(123, 456, completeCallback);
dispatcher.foo(123, 456);
expect(queueRunnerFactory).toHaveBeenCalledWith(
jasmine.objectContaining({
@@ -143,11 +140,10 @@ describe('ReportDispatcher', function() {
queueRunnerFactory
),
reporter1 = jasmine.createSpyObj('reporter1', ['foo', 'bar']),
reporter2 = jasmine.createSpyObj('reporter2', ['foo', 'bar']),
completeCallback = jasmine.createSpy('complete');
reporter2 = jasmine.createSpyObj('reporter2', ['foo', 'bar']);
dispatcher.addReporter(reporter1);
dispatcher.foo(123, completeCallback);
dispatcher.foo(123);
expect(queueRunnerFactory).toHaveBeenCalledWith(
jasmine.objectContaining({
queueableFns: [{ fn: jasmine.any(Function) }],
@@ -161,7 +157,7 @@ describe('ReportDispatcher', function() {
dispatcher.clearReporters();
dispatcher.addReporter(reporter2);
dispatcher.bar(456, completeCallback);
dispatcher.bar(456);
expect(queueRunnerFactory).toHaveBeenCalledWith(
jasmine.objectContaining({

View File

@@ -0,0 +1,557 @@
describe('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 '<same instance as ' + pp(expected) + '>';
}
};
}
});
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() {}
};
}
});

View File

@@ -38,11 +38,10 @@ describe('Spec', function() {
spec = new jasmineUnderTest.Spec({
description: 'my test',
id: 'some-id',
queueableFn: { fn: function() {} },
queueRunnerFactory: fakeQueueRunner
queueableFn: { fn: function() {} }
});
spec.execute();
spec.execute(fakeQueueRunner);
expect(fakeQueueRunner).toHaveBeenCalled();
});
@@ -54,11 +53,10 @@ describe('Spec', function() {
id: 123,
description: 'foo bar',
queueableFn: { fn: function() {} },
onStart: startCallback,
queueRunnerFactory: fakeQueueRunner
onStart: startCallback
});
spec.execute();
spec.execute(fakeQueueRunner);
fakeQueueRunner.calls.mostRecent().args[0].queueableFns[0].fn();
expect(startCallback).toHaveBeenCalled();
@@ -82,11 +80,10 @@ describe('Spec', function() {
}
];
},
onStart: startCallback,
queueRunnerFactory: fakeQueueRunner
onStart: startCallback
});
spec.execute();
spec.execute(fakeQueueRunner);
fakeQueueRunner.calls.mostRecent().args[0].queueableFns[0].fn();
expect(startCallback).toHaveBeenCalled();
@@ -106,11 +103,10 @@ describe('Spec', function() {
queueableFn: queueableFn,
beforeAndAfterFns: function() {
return { befores: [before], afters: [after] };
},
queueRunnerFactory: fakeQueueRunner
}
});
spec.execute();
spec.execute(fakeQueueRunner);
const options = fakeQueueRunner.calls.mostRecent().args[0];
expect(options.queueableFns).toEqual([
@@ -131,11 +127,10 @@ describe('Spec', function() {
queueableFn: { fn: function() {} },
beforeAndAfterFns: function() {
return { befores: [], afters: [] };
},
queueRunnerFactory: fakeQueueRunner
}
});
spec.execute();
spec.execute(fakeQueueRunner);
expect(fakeQueueRunner).toHaveBeenCalledWith(
jasmine.objectContaining({
@@ -145,14 +140,12 @@ describe('Spec', function() {
});
it('is marked pending if created without a function body', function() {
const fakeQueueRunner = jasmine.createSpy('fakeQueueRunner'),
startCallback = jasmine.createSpy('startCallback'),
const startCallback = jasmine.createSpy('startCallback'),
resultCallback = jasmine.createSpy('resultCallback'),
spec = new jasmineUnderTest.Spec({
onStart: startCallback,
queueableFn: { fn: null },
resultCallback: resultCallback,
queueRunnerFactory: fakeQueueRunner
resultCallback: resultCallback
});
expect(spec.status()).toBe('pending');
@@ -166,11 +159,10 @@ describe('Spec', function() {
spec = new jasmineUnderTest.Spec({
onStart: startCallback,
queueableFn: { fn: specBody },
resultCallback: resultCallback,
queueRunnerFactory: fakeQueueRunner
resultCallback: resultCallback
});
spec.execute('cally-back', true);
spec.execute(fakeQueueRunner, 'cally-back', true);
expect(fakeQueueRunner).toHaveBeenCalledWith(
jasmine.objectContaining({
@@ -203,10 +195,11 @@ describe('Spec', function() {
onStart: startCallback,
resultCallback: resultCallback,
description: 'with a spec',
parentSuiteId: 'suite1',
filename: 'someSpecFile.js',
getSpecName: function() {
return 'a suite with a spec';
},
queueRunnerFactory: fakeQueueRunner,
queueableFn: { fn: null }
});
@@ -214,7 +207,7 @@ describe('Spec', function() {
expect(spec.status()).toBe('pending');
spec.execute();
spec.execute(fakeQueueRunner);
expect(fakeQueueRunner).toHaveBeenCalled();
@@ -228,6 +221,8 @@ describe('Spec', function() {
status: 'pending',
description: 'with a spec',
fullName: 'a suite with a spec',
parentSuiteId: 'suite1',
filename: 'someSpecFile.js',
failedExpectations: [],
passedExpectations: [],
deprecationWarnings: [],
@@ -247,13 +242,10 @@ describe('Spec', function() {
catchExceptions: function() {
return false;
},
resultCallback: function() {},
queueRunnerFactory: function(attrs) {
attrs.onComplete();
}
resultCallback: function() {}
});
spec.execute(done);
spec.execute(attrs => attrs.onComplete(), done);
expect(done).toHaveBeenCalled();
});
@@ -265,14 +257,14 @@ describe('Spec', function() {
catchExceptions: function() {
return false;
},
resultCallback: function() {},
queueRunnerFactory: function(attrs) {
spec.result.status = 'failed';
attrs.onComplete();
}
resultCallback: function() {}
});
spec.execute(done);
function queueRunnerFactory(attrs) {
spec.result.status = 'failed';
attrs.onComplete();
}
spec.execute(queueRunnerFactory, done);
expect(done).toHaveBeenCalledWith(
jasmine.any(jasmineUnderTest.StopExecutionError)
@@ -293,16 +285,17 @@ describe('Spec', function() {
resultCallback: function(result) {
duration = result.duration;
},
queueRunnerFactory: function(config) {
config.queueableFns.forEach(function(qf) {
qf.fn();
});
config.onComplete();
},
timer: timer
});
spec.execute(function() {});
function queueRunnerFactory(config) {
config.queueableFns.forEach(function(qf) {
qf.fn();
});
config.onComplete();
}
spec.execute(queueRunnerFactory, function() {});
expect(duration).toBe(77000);
});
@@ -313,13 +306,10 @@ describe('Spec', function() {
catchExceptions: function() {
return false;
},
resultCallback: function() {},
queueRunnerFactory: function(attrs) {
attrs.onComplete();
}
resultCallback: function() {}
});
spec.setSpecProperty('a', 4);
spec.execute(done);
spec.execute(attrs => attrs.onComplete(), done);
expect(spec.result.properties).toEqual({ a: 4 });
});
@@ -334,7 +324,7 @@ describe('Spec', function() {
const spec = new jasmineUnderTest.Spec({
queueableFn: { fn: jasmine.createSpy('spec body') }
});
spec.addExpectationResult(true);
spec.addExpectationResult(true, {});
expect(spec.status()).toBe('passed');
});
@@ -342,8 +332,8 @@ describe('Spec', function() {
const spec = new jasmineUnderTest.Spec({
queueableFn: { fn: jasmine.createSpy('spec body') }
});
spec.addExpectationResult(true);
spec.addExpectationResult(false);
spec.addExpectationResult(true, {});
spec.addExpectationResult(false, {});
expect(spec.status()).toBe('failed');
});
@@ -352,25 +342,21 @@ describe('Spec', function() {
resultCallback = jasmine.createSpy('resultCallback'),
spec = new jasmineUnderTest.Spec({
queueableFn: { fn: jasmine.createSpy('spec body') },
expectationResultFactory: function(data) {
return data;
},
queueRunnerFactory: fakeQueueRunner,
resultCallback: resultCallback
});
spec.addExpectationResult(true, 'expectation1');
spec.addExpectationResult(false, 'expectation2');
spec.addExpectationResult(true, { message: 'expectation1' });
spec.addExpectationResult(false, { message: 'expectation2' });
spec.execute();
spec.execute(fakeQueueRunner);
const fns = fakeQueueRunner.calls.mostRecent().args[0].queueableFns;
fns[fns.length - 1].fn();
expect(resultCallback.calls.first().args[0].passedExpectations).toEqual([
'expectation1'
jasmine.objectContaining({ message: 'expectation1' })
]);
expect(resultCallback.calls.first().args[0].failedExpectations).toEqual([
'expectation2'
jasmine.objectContaining({ message: 'expectation2' })
]);
});
@@ -379,46 +365,123 @@ describe('Spec', function() {
resultCallback = jasmine.createSpy('resultCallback'),
spec = new jasmineUnderTest.Spec({
queueableFn: { fn: function() {} },
expectationResultFactory: function(data) {
return data;
},
queueRunnerFactory: fakeQueueRunner,
resultCallback: resultCallback,
throwOnExpectationFailure: true
});
spec.addExpectationResult(true, 'passed');
spec.addExpectationResult(true, { message: 'passed' });
expect(function() {
spec.addExpectationResult(false, 'failed');
spec.addExpectationResult(false, { message: 'failed' });
}).toThrowError(jasmineUnderTest.errors.ExpectationFailed);
spec.execute();
spec.execute(fakeQueueRunner);
const fns = fakeQueueRunner.calls.mostRecent().args[0].queueableFns;
fns[fns.length - 1].fn();
expect(resultCallback.calls.first().args[0].passedExpectations).toEqual([
'passed'
jasmine.objectContaining({ message: 'passed' })
]);
expect(resultCallback.calls.first().args[0].failedExpectations).toEqual([
'failed'
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() {} },
expectationResultFactory: function(data) {
return data;
},
queueRunnerFactory: function(attrs) {
attrs.onComplete();
},
resultCallback: resultCallback,
throwOnExpectationFailure: true
});
spec.onException('failing exception');
spec.handleException('failing exception');
});
it('can return its full name', function() {
@@ -445,11 +508,10 @@ describe('Spec', function() {
spec = new jasmineUnderTest.Spec({
description: 'my test',
id: 'some-id',
queueableFn: { fn: function() {} },
queueRunnerFactory: fakeQueueRunner
queueableFn: { fn: function() {} }
});
spec.execute();
spec.execute(fakeQueueRunner);
expect(spec.status()).toEqual('pending');
expect(spec.result.pendingReason).toEqual('');
@@ -467,11 +529,10 @@ describe('Spec', function() {
spec = new jasmineUnderTest.Spec({
description: 'my test',
id: 'some-id',
queueableFn: { fn: function() {} },
queueRunnerFactory: fakeQueueRunner
queueableFn: { fn: function() {} }
});
spec.execute();
spec.execute(fakeQueueRunner);
expect(spec.status()).toEqual('pending');
expect(spec.result.pendingReason).toEqual('custom message');
@@ -483,25 +544,22 @@ describe('Spec', function() {
resultCallback = jasmine.createSpy('resultCallback'),
spec = new jasmineUnderTest.Spec({
queueableFn: { fn: function() {} },
expectationResultFactory: function(data) {
return data;
},
queueRunnerFactory: fakeQueueRunner,
resultCallback: resultCallback
});
spec.onException('foo');
spec.execute();
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([
{
error: 'foo',
message: 'foo thrown',
matcherName: '',
passed: false,
expected: '',
actual: ''
actual: '',
stack: null
}
]);
});
@@ -511,15 +569,11 @@ describe('Spec', function() {
resultCallback = jasmine.createSpy('resultCallback'),
spec = new jasmineUnderTest.Spec({
queueableFn: { fn: function() {} },
expectationResultFactory: function(data) {
return data;
},
queueRunnerFactory: fakeQueueRunner,
resultCallback: resultCallback
});
spec.onException(new jasmineUnderTest.errors.ExpectationFailed());
spec.execute();
spec.handleException(new jasmineUnderTest.errors.ExpectationFailed());
spec.execute(fakeQueueRunner);
const args = fakeQueueRunner.calls.mostRecent().args[0];
args.queueableFns[args.queueableFns.length - 1].fn();
@@ -532,13 +586,12 @@ describe('Spec', function() {
spec = new jasmineUnderTest.Spec({
onLateError: onLateError,
queueableFn: { fn: function() {} },
queueRunnerFactory: queueRunnerFactory,
getSpecName: function() {
return 'a spec';
}
});
spec.execute();
spec.execute(queueRunnerFactory);
expect(queueRunnerFactory).toHaveBeenCalled();
queueRunnerFactory.calls.argsFor(0)[0].onMultipleDone();
@@ -558,13 +611,12 @@ describe('Spec', function() {
queueableFn: {
fn: function() {}
},
queueRunnerFactory: function() {},
timer: timer
}),
t1 = 123,
t2 = 456;
spec.execute();
spec.execute(() => {});
expect(spec.result.debugLogs).toBeNull();
timer.elapsed.and.returnValue(t1);
spec.debugLog('msg 1');
@@ -586,17 +638,18 @@ describe('Spec', function() {
queueableFn: {
fn: function() {}
},
resultCallback: resultCallback,
queueRunnerFactory: function(config) {
spec.debugLog('msg');
for (const fn of config.queueableFns) {
fn.fn();
}
config.onComplete(false);
}
resultCallback: resultCallback
});
spec.execute(function() {});
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
@@ -609,17 +662,18 @@ describe('Spec', function() {
queueableFn: {
fn: function() {}
},
resultCallback: resultCallback,
queueRunnerFactory: function(config) {
spec.debugLog('msg');
for (const fn of config.queueableFns) {
fn.fn();
}
config.onComplete(false);
}
resultCallback: resultCallback
});
spec.execute(function() {});
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();
});
@@ -634,21 +688,22 @@ describe('Spec', function() {
fn: function() {}
},
resultCallback: resultCallback,
queueRunnerFactory: function(config) {
spec.debugLog('msg');
spec.onException(new Error('nope'));
for (const fn of config.queueableFns) {
fn.fn();
}
config.onComplete(true);
},
timer: timer
}),
timestamp = 12345;
timer.elapsed.and.returnValue(timestamp);
spec.execute(function() {});
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 }]

View File

@@ -106,9 +106,9 @@ describe('SpyStrategy', function() {
it('allows a fake async function to be called instead', function(done) {
const originalFn = jasmine.createSpy('original'),
fakeFn = jasmine
.createSpy('fake')
.and.callFake(eval('async () => { return 67; }')),
fakeFn = jasmine.createSpy('fake').and.callFake(async () => {
return 67;
}),
spyStrategy = new jasmineUnderTest.SpyStrategy({ fn: originalFn });
spyStrategy.callFake(fakeFn);

View File

@@ -0,0 +1,178 @@
describe('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 '<same instance as ' + pp(expected) + '>';
}
};
}
});

View File

@@ -71,20 +71,9 @@ describe('Suite', function() {
suite.beforeAll(outerBefore);
suite.beforeAll(innerBefore);
function sameInstance(expected) {
return {
asymmetricMatch: function(actual) {
return actual === expected;
},
jasmineToString: function() {
return `<same instance as ${expected}>`;
}
};
}
expect(suite.beforeAllFns).toEqual([
{ fn: outerBefore.fn, type: 'beforeAll', suite: sameInstance(suite) },
{ fn: innerBefore.fn, type: 'beforeAll', suite: sameInstance(suite) }
{ fn: outerBefore.fn, type: 'beforeAll', suite: jasmine.is(suite) },
{ fn: innerBefore.fn, type: 'beforeAll', suite: jasmine.is(suite) }
]);
});
@@ -123,13 +112,9 @@ describe('Suite', function() {
});
it('has a status of failed if any expectations have failed', function() {
const suite = new jasmineUnderTest.Suite({
expectationResultFactory: function() {
return 'hi';
}
});
const suite = new jasmineUnderTest.Suite({});
suite.addExpectationResult(false);
suite.addExpectationResult(false, {});
expect(suite.status()).toBe('failed');
});
@@ -148,28 +133,110 @@ describe('Suite', function() {
it('throws an ExpectationFailed when receiving a failed expectation when throwOnExpectationFailure is set', function() {
const suite = new jasmineUnderTest.Suite({
expectationResultFactory: function(data) {
return data;
},
throwOnExpectationFailure: true
});
expect(function() {
suite.addExpectationResult(false, 'failed');
suite.addExpectationResult(false, { message: 'failed' });
}).toThrowError(jasmineUnderTest.errors.ExpectationFailed);
expect(suite.status()).toBe('failed');
expect(suite.result.failedExpectations).toEqual(['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.onException(new jasmineUnderTest.errors.ExpectationFailed());
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,
@@ -261,12 +328,8 @@ describe('Suite', function() {
});
it('should reset the failedExpectations', function() {
const suite = new jasmineUnderTest.Suite({
expectationResultFactory: function(error) {
return error;
}
});
suite.onException(new Error());
const suite = new jasmineUnderTest.Suite({});
suite.handleException(new Error());
suite.reset();

View File

@@ -274,7 +274,7 @@ describe('TreeProcessor', function() {
expect(result.valid).toBe(true);
});
it('runs a single leaf', function() {
it('runs a single leaf', async function() {
const leaf = new Leaf(),
node = new Node({ children: [leaf], userContext: { root: 'context' } }),
queueRunner = jasmine.createSpy('queueRunner'),
@@ -282,25 +282,27 @@ describe('TreeProcessor', function() {
tree: node,
runnableIds: [leaf.id],
queueRunnerFactory: queueRunner
}),
treeComplete = jasmine.createSpy('treeComplete');
});
processor.execute(treeComplete);
const promise = processor.execute();
expect(queueRunner).toHaveBeenCalledWith({
onComplete: treeComplete,
onComplete: jasmine.any(Function),
onException: jasmine.any(Function),
userContext: { root: 'context' },
queueableFns: [{ fn: jasmine.any(Function) }],
onMultipleDone: null
});
queueRunner.calls.mostRecent().args[0].queueableFns[0].fn('foo');
const queueRunnerArgs = queueRunner.calls.mostRecent().args[0];
queueRunnerArgs.queueableFns[0].fn('foo');
expect(leaf.execute).toHaveBeenCalledWith(queueRunner, 'foo', false, false);
expect(leaf.execute).toHaveBeenCalledWith('foo', false, false);
queueRunnerArgs.onComplete();
await expectAsync(promise).toBeResolvedTo(undefined);
});
it('runs a node with no children', function() {
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'),
@@ -313,21 +315,20 @@ describe('TreeProcessor', function() {
nodeComplete: nodeComplete,
queueRunnerFactory: queueRunner
}),
treeComplete = jasmine.createSpy('treeComplete'),
nodeDone = jasmine.createSpy('nodeDone');
processor.execute(treeComplete);
const promise = processor.execute();
expect(queueRunner).toHaveBeenCalledWith({
onComplete: treeComplete,
onComplete: jasmine.any(Function),
onException: jasmine.any(Function),
userContext: { root: 'context' },
queueableFns: [{ fn: jasmine.any(Function) }],
onMultipleDone: null
});
queueRunner.calls.mostRecent().args[0].queueableFns[0].fn(nodeDone);
const queueRunnerArgs = queueRunner.calls.mostRecent().args[0];
queueRunnerArgs.queueableFns[0].fn(nodeDone);
expect(queueRunner).toHaveBeenCalledWith({
onComplete: jasmine.any(Function),
onMultipleDone: null,
@@ -348,6 +349,9 @@ describe('TreeProcessor', function() {
{ my: 'result' },
jasmine.any(Function)
);
queueRunnerArgs.onComplete();
await expectAsync(promise).toBeResolvedTo(undefined);
});
it('runs a node with children', function() {
@@ -372,10 +376,20 @@ describe('TreeProcessor', function() {
expect(queueableFns.length).toBe(3);
queueableFns[1].fn('foo');
expect(leaf1.execute).toHaveBeenCalledWith('foo', false, false);
expect(leaf1.execute).toHaveBeenCalledWith(
queueRunner,
'foo',
false,
false
);
queueableFns[2].fn('bar');
expect(leaf2.execute).toHaveBeenCalledWith('bar', false, false);
expect(leaf2.execute).toHaveBeenCalledWith(
queueRunner,
'bar',
false,
false
);
});
it('cascades errors up the tree', function() {
@@ -401,7 +415,7 @@ describe('TreeProcessor', function() {
expect(queueableFns.length).toBe(2);
queueableFns[1].fn('foo');
expect(leaf.execute).toHaveBeenCalledWith('foo', false, false);
expect(leaf.execute).toHaveBeenCalledWith(queueRunner, 'foo', false, false);
queueRunner.calls.mostRecent().args[0].onComplete('things');
expect(nodeComplete).toHaveBeenCalled();
@@ -437,7 +451,7 @@ describe('TreeProcessor', function() {
expect(nodeStart).toHaveBeenCalledWith(node, 'bar');
queueableFns[1].fn('foo');
expect(leaf1.execute).toHaveBeenCalledWith('foo', true, false);
expect(leaf1.execute).toHaveBeenCalledWith(queueRunner, 'foo', true, false);
node.getResult.and.returnValue({ im: 'disabled' });
@@ -475,7 +489,7 @@ describe('TreeProcessor', function() {
expect(queueableFns.length).toBe(2);
queueableFns[1].fn('foo');
expect(leaf.execute).toHaveBeenCalledWith('foo', true, true);
expect(leaf.execute).toHaveBeenCalledWith(queueRunner, 'foo', true, true);
});
it('runs beforeAlls for a node with children', function() {
@@ -637,11 +651,21 @@ describe('TreeProcessor', function() {
queueableFns[0].fn();
expect(nonSpecified.execute).not.toHaveBeenCalled();
expect(specified.execute).toHaveBeenCalledWith(undefined, false, false);
expect(specified.execute).toHaveBeenCalledWith(
queueRunner,
undefined,
false,
false
);
queueableFns[1].fn();
expect(nonSpecified.execute).toHaveBeenCalledWith(undefined, true, false);
expect(nonSpecified.execute).toHaveBeenCalledWith(
queueRunner,
undefined,
true,
false
);
});
it('runs nodes and leaves with a specified order', function() {

View File

@@ -0,0 +1,30 @@
describe('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.is(Object({ foo: [ 'bar' ] }))>"
);
});
});

View File

@@ -22,56 +22,38 @@ describe('buildExpectationResult', function() {
expect(result.message).toBe('some-value');
});
it('delegates message formatting to the provided formatter if there was an Error', function() {
const fakeError = { message: 'foo' },
messageFormatter = jasmine
.createSpy('exception message formatter')
.and.returnValue(fakeError.message);
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' }
});
const result = jasmineUnderTest.buildExpectationResult({
passed: false,
error: fakeError,
messageFormatter: messageFormatter
expect(result.message).toEqual('foo in somefile.js');
});
expect(messageFormatter).toHaveBeenCalledWith(fakeError);
expect(result.message).toEqual('foo');
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"
);
});
});
it('delegates stack formatting to the provided formatter if there was an Error', function() {
const fakeError = { stack: 'foo' },
stackFormatter = jasmine
.createSpy('stack formatter')
.and.returnValue(fakeError.stack);
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' }
});
const result = jasmineUnderTest.buildExpectationResult({
passed: false,
error: fakeError,
stackFormatter: stackFormatter
expect(result.stack).toEqual('bar');
});
expect(stackFormatter).toHaveBeenCalledWith(fakeError, {
omitMessage: true
});
expect(result.stack).toEqual('foo');
});
it('delegates stack formatting to the provided formatter if there was a provided errorForStack', function() {
const fakeError = { stack: 'foo' },
stackFormatter = jasmine
.createSpy('stack formatter')
.and.returnValue(fakeError.stack);
const result = jasmineUnderTest.buildExpectationResult({
passed: false,
errorForStack: fakeError,
stackFormatter: stackFormatter
});
expect(stackFormatter).toHaveBeenCalledWith(fakeError, {
omitMessage: true
});
expect(result.stack).toEqual('foo');
});
it('matcherName returns passed matcherName', function() {

View File

@@ -1,55 +1,57 @@
describe('Asymmetric equality testers (Integration)', function() {
function verifyPasses(expectations) {
it('passes', function(done) {
it('passes', async function() {
const env = new jasmineUnderTest.Env();
env.it('a spec', function() {
expectations(env);
});
const specExpectations = function(result) {
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();
};
const reporter = jasmine.createSpyObj('reporter', ['specDone']);
env.addReporter(reporter);
await env.execute();
env.addReporter({ specDone: specExpectations });
env.execute(null, done);
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', function(done) {
it('fails', async function() {
const env = new jasmineUnderTest.Env();
env.it('a spec', function() {
expectations(env);
});
const specExpectations = function(result) {
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('');
};
const reporter = jasmine.createSpyObj('reporter', ['specDone']);
env.addReporter(reporter);
await env.execute();
env.addReporter({ specDone: specExpectations });
env.execute(null, done);
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('');
});
}

View File

@@ -10,7 +10,7 @@ describe('Custom Async Matchers (Integration)', function() {
env.cleanup_();
});
it('passes the spec if the custom async matcher passes', function(done) {
it('passes the spec if the custom async matcher passes', async function() {
env.it('spec using custom async matcher', function() {
env.addAsyncMatchers({
toBeReal: function() {
@@ -30,10 +30,10 @@ describe('Custom Async Matchers (Integration)', function() {
};
env.addReporter({ specDone: specExpectations });
env.execute(null, done);
await env.execute();
});
it('uses the negative compare function for a negative comparison, if provided', function(done) {
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() {
@@ -56,10 +56,10 @@ describe('Custom Async Matchers (Integration)', function() {
};
env.addReporter({ specDone: specExpectations });
env.execute(null, done);
await env.execute();
});
it('generates messages with the same rules as built in matchers absent a custom message', function(done) {
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() {
@@ -81,10 +81,10 @@ describe('Custom Async Matchers (Integration)', function() {
};
env.addReporter({ specDone: specExpectations });
env.execute(null, done);
await env.execute();
});
it('passes the jasmine utility to the matcher factory', function(done) {
it('passes the jasmine utility to the matcher factory', async function() {
const matcherFactory = function() {
return {
compare: function() {
@@ -112,10 +112,10 @@ describe('Custom Async Matchers (Integration)', function() {
};
env.addReporter({ specDone: specExpectations });
env.execute(null, done);
await env.execute();
});
it('provides custom equality testers to the matcher factory via matchersUtil', function(done) {
it('provides custom equality testers to the matcher factory via matchersUtil', async function() {
const matcherFactory = function(matchersUtil) {
return {
compare: function(actual, expected) {
@@ -146,6 +146,6 @@ describe('Custom Async Matchers (Integration)', function() {
};
env.addReporter({ specDone: specExpectations });
env.execute(null, done);
await env.execute();
});
});

View File

@@ -10,7 +10,7 @@ describe('Custom Matchers (Integration)', function() {
env.cleanup_();
});
it('allows adding more matchers local to a spec', function(done) {
it('allows adding more matchers local to a spec', async function() {
env.it('spec defining a custom matcher', function() {
env.addMatchers({
matcherForSpec: function() {
@@ -37,20 +37,18 @@ describe('Custom Matchers (Integration)', function() {
});
const specDoneSpy = jasmine.createSpy('specDoneSpy');
const expectations = function() {
const firstSpecResult = specDoneSpy.calls.first().args[0];
expect(firstSpecResult.status).toEqual('failed');
expect(firstSpecResult.failedExpectations[0].message).toEqual(
'matcherForSpec: actual: zzz; expected: yyy'
);
done();
};
env.addReporter({ specDone: specDoneSpy });
env.execute(null, expectations);
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', function(done) {
it('passes the spec if the custom matcher passes', async function() {
env.it('spec using custom matcher', function() {
env.addMatchers({
toBeReal: function() {
@@ -65,15 +63,16 @@ describe('Custom Matchers (Integration)', function() {
env.expect(true).toBeReal();
});
const specExpectations = function(result) {
expect(result.status).toEqual('passed');
};
const reporter = jasmine.createSpyObj('reporter', ['specDone']);
env.addReporter(reporter);
await env.execute();
env.addReporter({ specDone: specExpectations });
env.execute(null, done);
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', function(done) {
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.
@@ -99,15 +98,16 @@ describe('Custom Matchers (Integration)', function() {
.toEqual(jasmineUnderTest.arrayWithExactContents(['fooBar']));
});
const specExpectations = function(result) {
expect(result.status).toEqual('passed');
};
const reporter = jasmine.createSpyObj('reporter', ['specDone']);
env.addReporter(reporter);
await env.execute();
env.addReporter({ specDone: specExpectations });
env.execute(null, done);
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', function(done) {
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
@@ -120,18 +120,19 @@ describe('Custom Matchers (Integration)', function() {
env.expect({ foo: 'foo' }).toEqual({ foo: 'foo' });
});
const specExpectations = function(result) {
expect(result.status).toEqual('failed');
expect(result.failedExpectations[0].message).toEqual(
"Expected $.foo = 'foo' to equal 'foo'."
);
};
const reporter = jasmine.createSpyObj('reporter', ['specDone']);
env.addReporter(reporter);
await env.execute();
env.addReporter({ specDone: specExpectations });
env.execute(null, done);
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', function(done) {
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() {
@@ -149,15 +150,16 @@ describe('Custom Matchers (Integration)', function() {
env.expect(true).not.toBeReal();
});
const specExpectations = function(result) {
expect(result.status).toEqual('passed');
};
const reporter = jasmine.createSpyObj('reporter', ['specDone']);
env.addReporter(reporter);
await env.execute();
env.addReporter({ specDone: specExpectations });
env.execute(null, done);
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', function(done) {
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() {
@@ -172,17 +174,18 @@ describe('Custom Matchers (Integration)', function() {
env.expect('a').toBeReal();
});
const specExpectations = function(result) {
expect(result.failedExpectations[0].message).toEqual(
"Expected 'a' to be real."
);
};
const reporter = jasmine.createSpyObj('reporter', ['specDone']);
env.addReporter(reporter);
await env.execute();
env.addReporter({ specDone: specExpectations });
env.execute(null, done);
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', function(done) {
it('passes the expected and actual arguments to the comparison function', async function() {
const argumentSpy = jasmine
.createSpy('argument spy')
.and.returnValue({ pass: true });
@@ -199,17 +202,13 @@ describe('Custom Matchers (Integration)', function() {
env.expect(true).toBeReal('arg1', 'arg2');
});
const specExpectations = function() {
expect(argumentSpy).toHaveBeenCalledWith(true);
expect(argumentSpy).toHaveBeenCalledWith(true, 'arg');
expect(argumentSpy).toHaveBeenCalledWith(true, 'arg1', 'arg2');
};
env.addReporter({ specDone: specExpectations });
env.execute(null, done);
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', function(done) {
it('passes the jasmine utility to the matcher factory', async function() {
const matcherFactory = function() {
return {
compare: function() {
@@ -229,17 +228,13 @@ describe('Custom Matchers (Integration)', function() {
env.expect(true).toBeReal();
});
const specExpectations = function() {
expect(matcherFactorySpy).toHaveBeenCalledWith(
jasmine.any(jasmineUnderTest.MatchersUtil)
);
};
env.addReporter({ specDone: specExpectations });
env.execute(null, done);
await env.execute();
expect(matcherFactorySpy).toHaveBeenCalledWith(
jasmine.any(jasmineUnderTest.MatchersUtil)
);
});
it('provides custom equality testers to the matcher factory via matchersUtil', function(done) {
it('provides custom equality testers to the matcher factory via matchersUtil', async function() {
const matcherFactory = function(matchersUtil) {
return {
compare: function(actual, expected) {
@@ -262,12 +257,13 @@ describe('Custom Matchers (Integration)', function() {
env.expect([1, 2]).toBeArrayWithFirstElement('1');
});
const specExpectations = function(result) {
expect(customEqualityFn).toHaveBeenCalledWith(1, '1');
expect(result.failedExpectations).toEqual([]);
};
const reporter = jasmine.createSpyObj('reporter', ['specDone']);
env.addReporter(reporter);
await env.execute();
env.addReporter({ specDone: specExpectations });
env.execute(null, done);
expect(reporter.specDone).toHaveBeenCalledTimes(1);
const result = reporter.specDone.calls.argsFor(0)[0];
expect(customEqualityFn).toHaveBeenCalledWith(1, '1');
expect(result.failedExpectations).toEqual([]);
});
});

View File

@@ -6,7 +6,7 @@ describe('Custom object formatters', function() {
env.configure({ random: false });
});
it('scopes custom object formatters to a spec', function(done) {
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 + ')';
@@ -22,21 +22,19 @@ describe('Custom object formatters', function() {
const specDone = function(result) {
specResults.push(result);
};
const expectations = function() {
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.'
);
done();
};
env.addReporter({ specDone: specDone });
env.execute(null, expectations);
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', function(done) {
it('scopes custom object formatters to a suite', async function() {
env.it('a spec without custom pretty-printer', function() {
env.expect(42).toBeUndefined();
});
@@ -57,18 +55,16 @@ describe('Custom object formatters', function() {
const specDone = function(result) {
specResults.push(result);
};
const expectations = function() {
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.'
);
done();
};
env.addReporter({ specDone: specDone });
env.execute(null, expectations);
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() {

View File

@@ -33,6 +33,67 @@ describe('Default Spy Strategy (Integration)', function() {
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');

View File

@@ -10,7 +10,7 @@ describe('Deprecation (integration)', function() {
env.cleanup_();
});
it('reports a deprecation on the top suite', function(done) {
it('reports a deprecation on the top suite', async function() {
const reporter = jasmine.createSpyObj('reporter', ['jasmineDone']);
env.addReporter(reporter);
spyOn(console, 'error');
@@ -20,24 +20,23 @@ describe('Deprecation (integration)', function() {
});
env.it('a spec', function() {});
env.execute(null, function() {
expect(reporter.jasmineDone).toHaveBeenCalledWith(
jasmine.objectContaining({
deprecationWarnings: [
jasmine.objectContaining({
message: jasmine.stringMatching(/^the message/)
})
]
})
);
expect(console.error).toHaveBeenCalledWith(
jasmine.stringMatching(/^DEPRECATION: the message/)
);
done();
});
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', function(done) {
it('reports a deprecation on a descendent suite', async function() {
const reporter = jasmine.createSpyObj('reporter', ['suiteDone']);
env.addReporter(reporter);
spyOn(console, 'error');
@@ -49,26 +48,23 @@ describe('Deprecation (integration)', function() {
env.it('a spec', function() {});
});
env.execute(null, function() {
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\)/
)
);
done();
});
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', function(done) {
it('reports a deprecation on a spec', async function() {
const reporter = jasmine.createSpyObj('reporter', ['specDone']);
env.addReporter(reporter);
spyOn(console, 'error');
@@ -79,26 +75,25 @@ describe('Deprecation (integration)', function() {
});
});
env.execute(null, function() {
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\)/
)
);
done();
});
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', function(done) {
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');
@@ -107,27 +102,26 @@ describe('Deprecation (integration)', function() {
env.deprecated('the message', { ignoreRunnable: true });
});
env.execute(null, function() {
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/)
);
done();
});
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', function(done) {
it('includes the stack trace', async function() {
const reporter = jasmine.createSpyObj('reporter', ['specDone']);
env.addReporter(reporter);
spyOn(console, 'error');
@@ -138,25 +132,24 @@ describe('Deprecation (integration)', function() {
});
});
env.execute(null, function() {
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/
);
done();
});
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', function(done) {
it('excludes the stack trace when omitStackTrace is true', async function() {
const reporter = jasmine.createSpyObj('reporter', ['specDone']);
env.addReporter(reporter);
spyOn(console, 'error');
@@ -167,25 +160,24 @@ describe('Deprecation (integration)', function() {
});
});
env.execute(null, function() {
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/)
);
done();
});
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', function(done) {
it('emits a given deprecation only once', async function() {
const reporter = jasmine.createSpyObj('reporter', [
'specDone',
'suiteDone'
@@ -205,43 +197,40 @@ describe('Deprecation (integration)', function() {
});
});
env.execute(null, function() {
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\)/
)
);
done();
});
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', function(done) {
it('emits a given deprecation each time when config.verboseDeprecations is true', async function() {
const reporter = jasmine.createSpyObj('reporter', [
'specDone',
'suiteDone'
@@ -262,46 +251,45 @@ describe('Deprecation (integration)', function() {
});
});
env.execute(null, function() {
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\)/
);
done();
});
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', function(done) {
it('handles deprecations that occur before execute() is called', async function() {
const reporter = jasmine.createSpyObj('reporter', ['jasmineDone']);
env.addReporter(reporter);
spyOn(console, 'error');
@@ -309,20 +297,19 @@ describe('Deprecation (integration)', function() {
env.deprecated('the message');
env.it('a spec', function() {});
env.execute(null, function() {
expect(reporter.jasmineDone).toHaveBeenCalledWith(
jasmine.objectContaining({
deprecationWarnings: [
jasmine.objectContaining({
message: jasmine.stringMatching(/^the message/)
})
]
})
);
expect(console.error).toHaveBeenCalledWith(
jasmine.stringMatching(/^DEPRECATION: the message/)
);
done();
});
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/)
);
});
});

File diff suppressed because it is too large Load Diff

View File

@@ -10,156 +10,158 @@ describe('Matchers (Integration)', function() {
});
function verifyPasses(expectations) {
it('passes', function(done) {
it('passes', async function() {
env.it('a spec', function() {
expectations(env);
});
const specExpectations = function(result) {
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();
};
const reporter = jasmine.createSpyObj('reporter', ['specDone']);
env.addReporter(reporter);
await env.execute();
env.addReporter({ specDone: specExpectations });
env.execute(null, done);
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', function(done) {
it('fails', async function() {
env.it('a spec', function() {
expectations(env);
});
const specExpectations = function(result) {
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('');
};
const reporter = jasmine.createSpyObj('reporter', ['specDone']);
env.addReporter(reporter);
await env.execute();
env.addReporter({ specDone: specExpectations });
env.execute(null, done);
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', function(done) {
it('uses custom object formatters', async function() {
env.it('a spec', function() {
env.addCustomObjectFormatter(config.formatter);
config.expectations(env);
});
const specExpectations = function(result) {
expect(result.status).toEqual('failed');
expect(result.failedExpectations.length)
.withContext('Number of failed expectations')
.toEqual(1);
expect(result.failedExpectations[0].message).toEqual(
config.expectedMessage
);
};
const reporter = jasmine.createSpyObj('reporter', ['specDone']);
env.addReporter(reporter);
await env.execute();
env.addReporter({ specDone: specExpectations });
env.execute(null, done);
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', function(done) {
it('passes', async function() {
env.it('a spec', function() {
return expectations(env);
});
const specExpectations = function(result) {
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();
};
const reporter = jasmine.createSpyObj('reporter', ['specDone']);
env.addReporter(reporter);
await env.execute();
env.addReporter({ specDone: specExpectations });
env.execute(null, done);
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', function(done) {
it('fails', async function() {
env.it('a spec', function() {
return expectations(env);
});
const specExpectations = function(result) {
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('');
};
const reporter = jasmine.createSpyObj('reporter', ['specDone']);
env.addReporter(reporter);
await env.execute();
env.addReporter({ specDone: specExpectations });
env.execute(null, done);
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', function(done) {
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 specExpectations = function(result) {
expect(result.status).toEqual('failed');
expect(result.failedExpectations.length)
.withContext('Number of failed expectations')
.toEqual(1);
expect(result.failedExpectations[0].message).toEqual(
config.expectedMessage
);
};
const reporter = jasmine.createSpyObj('reporter', ['specDone']);
env.addReporter(reporter);
await env.execute();
env.addReporter({ specDone: specExpectations });
env.execute(null, done);
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
);
});
}
@@ -753,76 +755,79 @@ describe('Matchers (Integration)', function() {
});
describe('When an async matcher is used with .already()', function() {
it('propagates the matcher result when the promise is resolved', function(done) {
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 specExpectations = function(result) {
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('');
};
const reporter = jasmine.createSpyObj('reporter', ['specDone']);
env.addReporter(reporter);
await env.execute();
env.addReporter({ specDone: specExpectations });
env.execute(null, done);
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', function(done) {
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 specExpectations = function(result) {
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('');
};
const reporter = jasmine.createSpyObj('reporter', ['specDone']);
env.addReporter(reporter);
await env.execute();
env.addReporter({ specDone: specExpectations });
env.execute(null, done);
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', function(done) {
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 specExpectations = function(result) {
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('');
};
const reporter = jasmine.createSpyObj('reporter', ['specDone']);
env.addReporter(reporter);
await env.execute();
env.addReporter({ specDone: specExpectations });
env.execute(null, done);
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('');
});
});
});

View File

@@ -30,7 +30,7 @@ describe('spec running', function() {
expect(it4.id).toEqual('spec4');
});
it('nested suites', function(done) {
it('nested suites', async function() {
let foo = 0;
let bar = 0;
let baz = 0;
@@ -61,16 +61,15 @@ describe('spec running', function() {
expect(baz).toEqual(0);
expect(quux).toEqual(0);
env.execute(null, function() {
expect(foo).toEqual(1);
expect(bar).toEqual(1);
expect(baz).toEqual(1);
expect(quux).toEqual(1);
done();
});
await env.execute();
expect(foo).toEqual(1);
expect(bar).toEqual(1);
expect(baz).toEqual(1);
expect(quux).toEqual(1);
});
it('should permit nested describes', function(done) {
it('should permit nested describes', async function() {
const actions = [];
env.beforeEach(function() {
@@ -127,42 +126,41 @@ describe('spec running', function() {
});
});
env.execute(null, function() {
const expected = [
'topSuite beforeEach',
'outer beforeEach',
'outer it 1',
'outer afterEach',
'topSuite afterEach',
await env.execute();
'topSuite beforeEach',
'outer beforeEach',
'inner 1 beforeEach',
'inner 1 it',
'inner 1 afterEach',
'outer afterEach',
'topSuite afterEach',
const expected = [
'topSuite beforeEach',
'outer beforeEach',
'outer it 1',
'outer afterEach',
'topSuite afterEach',
'topSuite beforeEach',
'outer beforeEach',
'outer it 2',
'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',
'inner 2 beforeEach',
'inner 2 it',
'inner 2 afterEach',
'outer afterEach',
'topSuite afterEach'
];
expect(actions).toEqual(expected);
done();
});
'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', function(done) {
it('should run multiple befores and afters ordered so functions declared later are treated as more specific', async function() {
const actions = [];
env.beforeAll(function() {
@@ -219,28 +217,27 @@ describe('spec running', function() {
});
});
env.execute(null, function() {
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);
done();
});
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', function(done) {
it('should run beforeAlls before beforeEachs and afterAlls after afterEachs', async function() {
const actions = [];
env.beforeAll(function() {
@@ -281,24 +278,23 @@ describe('spec running', function() {
});
});
env.execute(null, function() {
const expected = [
'runner beforeAll',
'inner beforeAll',
'runner beforeEach',
'inner beforeEach',
'it',
'inner afterEach',
'runner afterEach',
'inner afterAll',
'runner afterAll'
];
expect(actions).toEqual(expected);
done();
});
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', function(done) {
it('should run beforeAlls and afterAlls in the order declared when runnablesToRun is provided', async function() {
const actions = [];
let spec;
let spec2;
@@ -345,30 +341,29 @@ describe('spec running', function() {
});
});
env.execute([spec2.id, spec.id], function() {
const expected = [
'runner beforeAll',
'inner beforeAll',
'runner beforeEach',
'inner beforeEach',
'it2',
'inner afterEach',
'runner afterEach',
await env.execute([spec2.id, spec.id]);
'runner beforeEach',
'inner beforeEach',
'it',
'inner afterEach',
'runner afterEach',
'inner afterAll',
'runner afterAll'
];
expect(actions).toEqual(expected);
done();
});
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', function(done) {
it('only runs *Alls once in a focused suite', async function() {
const actions = [];
env.fdescribe('Suite', function() {
@@ -383,14 +378,13 @@ describe('spec running', function() {
});
});
env.execute(null, function() {
expect(actions).toEqual(['beforeAll', 'spec', 'afterAll']);
done();
});
await env.execute();
expect(actions).toEqual(['beforeAll', 'spec', 'afterAll']);
});
describe('focused runnables', function() {
it('runs the relevant alls and eachs for each runnable', function(done) {
it('runs the relevant alls and eachs for each runnable', async function() {
const actions = [];
env.beforeAll(function() {
actions.push('beforeAll');
@@ -417,24 +411,23 @@ describe('spec running', function() {
});
});
env.execute(null, function() {
const expected = [
'beforeAll',
'beforeEach',
'spec in fdescribe',
'afterEach',
await env.execute();
'beforeEach',
'focused spec',
'afterEach',
'afterAll'
];
expect(actions).toEqual(expected);
done();
});
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', function(done) {
it('focused specs in focused suites cause non-focused siblings to not run', async function() {
const actions = [];
env.fdescribe('focused suite', function() {
@@ -446,14 +439,13 @@ describe('spec running', function() {
});
});
env.execute(null, function() {
const expected = ['focused spec'];
expect(actions).toEqual(expected);
done();
});
await env.execute();
const expected = ['focused spec'];
expect(actions).toEqual(expected);
});
it('focused suites in focused suites cause non-focused siblings to not run', function(done) {
it('focused suites in focused suites cause non-focused siblings to not run', async function() {
const actions = [];
env.fdescribe('focused suite', function() {
@@ -467,14 +459,13 @@ describe('spec running', function() {
});
});
env.execute(null, function() {
const expected = ['inner spec'];
expect(actions).toEqual(expected);
done();
});
await env.execute();
const expected = ['inner spec'];
expect(actions).toEqual(expected);
});
it('focused runnables unfocus ancestor focused suites', function(done) {
it('focused runnables unfocus ancestor focused suites', async function() {
const actions = [];
env.fdescribe('focused suite', function() {
@@ -488,15 +479,14 @@ describe('spec running', function() {
});
});
env.execute(null, function() {
const expected = ['focused spec'];
expect(actions).toEqual(expected);
done();
});
await env.execute();
const expected = ['focused spec'];
expect(actions).toEqual(expected);
});
});
it("shouldn't run disabled suites", function(done) {
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() {
@@ -504,13 +494,12 @@ describe('spec running', function() {
});
});
env.execute(null, function() {
expect(specInADisabledSuite).not.toHaveBeenCalled();
done();
});
await env.execute();
expect(specInADisabledSuite).not.toHaveBeenCalled();
});
it("shouldn't run before/after functions in disabled suites", function(done) {
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.
@@ -522,13 +511,12 @@ describe('spec running', function() {
env.it('spec inside a disabled suite', shouldNotRun);
});
env.execute(null, function() {
expect(shouldNotRun).not.toHaveBeenCalled();
done();
});
await env.execute();
expect(shouldNotRun).not.toHaveBeenCalled();
});
it('should allow top level suites to be disabled', function(done) {
it('should allow top level suites to be disabled', async function() {
const specInADisabledSuite = jasmine.createSpy('specInADisabledSuite'),
otherSpec = jasmine.createSpy('otherSpec');
@@ -539,14 +527,13 @@ describe('spec running', function() {
env.it('another spec', otherSpec);
});
env.execute(null, function() {
expect(specInADisabledSuite).not.toHaveBeenCalled();
expect(otherSpec).toHaveBeenCalled();
done();
});
await env.execute();
expect(specInADisabledSuite).not.toHaveBeenCalled();
expect(otherSpec).toHaveBeenCalled();
});
it('should set all pending specs to pending when a suite is run', function(done) {
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');
});
@@ -554,17 +541,16 @@ describe('spec running', function() {
env.addReporter(reporter);
env.execute(null, function() {
expect(reporter.specDone).toHaveBeenCalledWith(
jasmine.objectContaining({
status: 'pending'
})
);
done();
});
await env.execute();
expect(reporter.specDone).toHaveBeenCalledWith(
jasmine.objectContaining({
status: 'pending'
})
);
});
it('should recover gracefully when there are errors in describe functions', function(done) {
it('should recover gracefully when there are errors in describe functions', async function() {
const specs = [],
reporter = jasmine.createSpyObj(['specDone', 'suiteDone']);
@@ -599,24 +585,23 @@ describe('spec running', function() {
});
env.addReporter(reporter);
env.execute(null, function() {
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/
]);
done();
});
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', function(done) {
it('re-enters suites that have no *Alls', async function() {
const actions = [];
let spec1;
let spec2;
@@ -636,10 +621,9 @@ describe('spec running', function() {
actions.push('spec3');
});
env.execute([spec2.id, spec3.id, spec1.id], function() {
expect(actions).toEqual(['spec2', 'spec3', 'spec1']);
done();
});
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() {
@@ -698,7 +682,7 @@ describe('spec running', function() {
expect(actions).toEqual([]);
});
it('should run the tests in a consistent order when a seed is supplied', function(done) {
it('should run the tests in a consistent order when a seed is supplied', async function() {
const actions = [];
env.configure({ random: true, seed: '123456' });
@@ -756,39 +740,38 @@ describe('spec running', function() {
});
});
env.execute(null, function() {
const expected = [
'topSuite beforeEach',
'outer beforeEach',
'outer it 2',
'outer afterEach',
'topSuite afterEach',
await env.execute();
'topSuite beforeEach',
'outer beforeEach',
'inner 2 beforeEach',
'inner 2 it',
'inner 2 afterEach',
'outer afterEach',
'topSuite afterEach',
const expected = [
'topSuite beforeEach',
'outer beforeEach',
'outer it 2',
'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',
'inner 2 beforeEach',
'inner 2 it',
'inner 2 afterEach',
'outer afterEach',
'topSuite afterEach',
'topSuite beforeEach',
'outer beforeEach',
'outer it 1',
'outer afterEach',
'topSuite afterEach'
];
expect(actions).toEqual(expected);
done();
});
'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() {
@@ -1298,7 +1281,7 @@ describe('spec running', function() {
});
describe('when stopOnSpecFailure is on', function() {
it('does not run further specs when one fails', function(done) {
it('does not run further specs when one fails', async function() {
const actions = [];
env.describe('wrapper', function() {
@@ -1317,10 +1300,9 @@ describe('spec running', function() {
env.configure({ random: false });
env.configure({ stopOnSpecFailure: true });
env.execute(null, function() {
expect(actions).toEqual(['fails']);
done();
});
await env.execute();
expect(actions).toEqual(['fails']);
});
it('runs afterAll functions', async function() {
@@ -1364,7 +1346,7 @@ describe('spec running', function() {
env.configure({ autoCleanClosures: false, random: false });
});
it('should be able to run multiple times', function(done) {
it('should be able to run multiple times', async function() {
const actions = [];
env.describe('Suite', function() {
@@ -1378,16 +1360,14 @@ describe('spec running', function() {
});
});
env.execute(null, function() {
expect(actions).toEqual(['spec1', 'spec2']);
env.execute(null, function() {
expect(actions).toEqual(['spec1', 'spec2', 'spec1', 'spec2']);
done();
});
});
await env.execute();
expect(actions).toEqual(['spec1', 'spec2']);
await env.execute();
expect(actions).toEqual(['spec1', 'spec2', 'spec1', 'spec2']);
});
it('should reset results between runs', function(done) {
it('should reset results between runs', async function() {
const specResults = {};
const suiteResults = {};
let firstExecution = true;
@@ -1440,48 +1420,46 @@ describe('spec running', function() {
});
});
env.execute(null, function() {
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'
});
env.execute(null, function() {
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'
});
done();
});
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', function(done) {
it('should execute before and after hooks per run', async function() {
let timeline = [];
const timelineFn = function(hookName) {
return function() {
@@ -1507,17 +1485,15 @@ describe('spec running', function() {
env.it('spec1', timelineFn('spec1'));
env.it('spec2', timelineFn('spec2'));
});
env.execute(null, function() {
expect(timeline).toEqual(expectedTimeLine);
timeline = [];
env.execute(null, function() {
expect(timeline).toEqual(expectedTimeLine);
done();
});
});
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', function(done) {
it('should be able to filter out different tests in subsequent runs', async function() {
const specResults = {};
let focussedSpec = 'spec1';
@@ -1539,29 +1515,27 @@ describe('spec running', function() {
env.it('spec3', function() {});
});
env.execute(null, function() {
expect(specResults).toEqual({
spec1: 'passed',
spec2: 'excluded',
spec3: 'excluded'
});
focussedSpec = 'spec2';
env.execute(null, function() {
expect(specResults).toEqual({
spec1: 'excluded',
spec2: 'passed',
spec3: 'excluded'
});
focussedSpec = 'spec3';
env.execute(null, function() {
expect(specResults).toEqual({
spec1: 'excluded',
spec2: 'excluded',
spec3: 'passed'
});
done();
});
});
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'
});
});
});

View File

@@ -1,6 +1,6 @@
describe('DiffBuilder', function() {
it('records the actual and expected objects', function() {
const diffBuilder = jasmineUnderTest.DiffBuilder();
const diffBuilder = new jasmineUnderTest.DiffBuilder();
diffBuilder.setRoots({ x: 'actual' }, { x: 'expected' });
diffBuilder.recordMismatch();
@@ -10,7 +10,7 @@ describe('DiffBuilder', function() {
});
it('prints the path at which the difference was found', function() {
const diffBuilder = jasmineUnderTest.DiffBuilder();
const diffBuilder = new jasmineUnderTest.DiffBuilder();
diffBuilder.setRoots({ foo: { x: 'actual' } }, { foo: { x: 'expected' } });
diffBuilder.withPath('foo', function() {
@@ -23,7 +23,7 @@ describe('DiffBuilder', function() {
});
it('prints multiple messages, separated by newlines', function() {
const diffBuilder = jasmineUnderTest.DiffBuilder();
const diffBuilder = new jasmineUnderTest.DiffBuilder();
diffBuilder.setRoots({ foo: 1, bar: 3 }, { foo: 2, bar: 4 });
diffBuilder.withPath('foo', function() {
@@ -40,7 +40,7 @@ describe('DiffBuilder', function() {
});
it('allows customization of the message', function() {
const diffBuilder = jasmineUnderTest.DiffBuilder();
const diffBuilder = new jasmineUnderTest.DiffBuilder();
diffBuilder.setRoots({ x: 'bar' }, { x: 'foo' });
function darthVaderFormatter(actual, expected, path) {
@@ -68,7 +68,7 @@ describe('DiffBuilder', function() {
const prettyPrinter = function(val) {
return '|' + val + '|';
},
diffBuilder = jasmineUnderTest.DiffBuilder({
diffBuilder = new jasmineUnderTest.DiffBuilder({
prettyPrinter: prettyPrinter
});
prettyPrinter.customFormat_ = function() {};
@@ -86,7 +86,7 @@ describe('DiffBuilder', function() {
it('passes the injected pretty-printer to the diff formatter', function() {
const diffFormatter = jasmine.createSpy('diffFormatter'),
prettyPrinter = function() {},
diffBuilder = jasmineUnderTest.DiffBuilder({
diffBuilder = new jasmineUnderTest.DiffBuilder({
prettyPrinter: prettyPrinter
});
prettyPrinter.customFormat_ = function() {};

View File

@@ -104,7 +104,9 @@ describe('toBeInstanceOf', function() {
});
it('passes for an async function', function() {
const fn = eval("(async function fn() { return 'foo'; })");
async function fn() {
return 'foo';
}
const matcher = jasmineUnderTest.matchers.toBeInstanceOf();
const result = matcher.compare(fn, Function);

View File

@@ -3,7 +3,7 @@ const path = require('path'),
jasmineBrowser = require('jasmine-browser-runner'),
jasmineCore = require('../../lib/jasmine-core');
var config = require(path.resolve('spec/support/jasmine-browser.js'));
const config = require(path.resolve('spec/support/jasmine-browser.js'));
config.clearReporters = true;
config.jasmineCore = jasmineCore;

View File

@@ -5,14 +5,14 @@
source files or spec files are loaded.
*/
(function() {
var jasmineRequire = window.jasmineRequire || require('./jasmine.js');
const jasmineRequire = window.jasmineRequire || require('./jasmine.js');
/**
* ## Require &amp; Instantiate
*
* Require Jasmine's core files. Specifically, this requires and attaches all of Jasmine's code to the `jasmine` reference.
*/
var jasmine = jasmineRequire.core(jasmineRequire),
const jasmine = jasmineRequire.core(jasmineRequire),
global = jasmine.getGlobal();
global.jasmine = jasmine;
@@ -24,19 +24,19 @@
/**
* Create the Jasmine environment. This is used to run all specs in a project.
*/
var env = jasmine.getEnv();
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.
*/
var jasmineInterface = jasmineRequire.interface(jasmine, env);
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 (var property in jasmineInterface) {
for (const property in jasmineInterface) {
global[property] = jasmineInterface[property];
}
})();

View File

@@ -12,7 +12,7 @@
*/
(function() {
var env = jasmine.getEnv();
const env = jasmine.getEnv();
/**
* ## Runner Parameters
@@ -20,15 +20,15 @@
* More browser specific code - wrap the query string in an object and to allow for getting/setting parameters from the runner user interface.
*/
var queryString = new jasmine.QueryString({
const queryString = new jasmine.QueryString({
getWindowLocation: function() {
return window.location;
}
});
var filterSpecs = !!queryString.getParam('spec');
const filterSpecs = !!queryString.getParam('spec');
var config = {
const config = {
stopOnSpecFailure: queryString.getParam('stopOnSpecFailure'),
stopSpecOnExpectationFailure: queryString.getParam(
'stopSpecOnExpectationFailure'
@@ -36,13 +36,13 @@
hideDisabled: queryString.getParam('hideDisabled')
};
var random = queryString.getParam('random');
const random = queryString.getParam('random');
if (random !== undefined && random !== '') {
config.random = random;
}
var seed = queryString.getParam('seed');
const seed = queryString.getParam('seed');
if (seed) {
config.seed = seed;
}
@@ -51,7 +51,7 @@
* ## 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).
*/
var htmlReporter = new jasmine.HtmlReporter({
const htmlReporter = new jasmine.HtmlReporter({
env: env,
navigateWithNewParam: function(key, value) {
return queryString.navigateWithNewParam(key, value);
@@ -81,7 +81,7 @@
/**
* Filter which specs will be run by matching the start of the full name against the `spec` query param.
*/
var specFilter = new jasmine.HtmlSpecFilter({
const specFilter = new jasmine.HtmlSpecFilter({
filterString: function() {
return queryString.getParam('spec');
}
@@ -98,7 +98,7 @@
*
* 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.
*/
var currentWindowOnload = window.onload;
const currentWindowOnload = window.onload;
window.onload = function() {
if (currentWindowOnload) {

View File

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

View File

@@ -4,8 +4,8 @@ getJasmineRequireObj().CallTracker = function(j$) {
* @since 2.0.0
*/
function CallTracker() {
var calls = [];
var opts = {};
let calls = [];
const opts = {};
this.track = function(context) {
if (opts.cloneArgs) {
@@ -45,7 +45,7 @@ getJasmineRequireObj().CallTracker = function(j$) {
* @return {Array}
*/
this.argsFor = function(index) {
var call = calls[index];
const call = calls[index];
return call ? call.args : [];
};
@@ -58,7 +58,7 @@ getJasmineRequireObj().CallTracker = function(j$) {
* @return {Object?}
*/
this.thisFor = function(index) {
var call = calls[index];
const call = calls[index];
return call ? call.object : undefined;
};
@@ -81,12 +81,7 @@ getJasmineRequireObj().CallTracker = function(j$) {
* @return {Array}
*/
this.allArgs = function() {
var callArgs = [];
for (var i = 0; i < calls.length; i++) {
callArgs.push(calls[i].args);
}
return callArgs;
return calls.map(c => c.args);
};
/**

View File

@@ -1,19 +1,43 @@
getJasmineRequireObj().clearStack = function(j$) {
var maxInlineCallCount = 10;
const maxInlineCallCount = 10;
function messageChannelImpl(global, setTimeout) {
var channel = new global.MessageChannel(),
head = {},
tail = head;
function browserQueueMicrotaskImpl(global) {
const { setTimeout, queueMicrotask } = global;
let currentCallCount = 0;
return function clearStack(fn) {
currentCallCount++;
var taskRunning = false;
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;
var task = head.task;
const task = head.task;
delete head.task;
if (taskRunning) {
global.setTimeout(task, 0);
setTimeout(task, 0);
} else {
try {
taskRunning = true;
@@ -24,7 +48,7 @@ getJasmineRequireObj().clearStack = function(j$) {
}
};
var currentCallCount = 0;
let currentCallCount = 0;
return function clearStack(fn) {
currentCallCount++;
@@ -39,29 +63,32 @@ getJasmineRequireObj().clearStack = function(j$) {
}
function getClearStack(global) {
var currentCallCount = 0;
var realSetTimeout = global.setTimeout;
var setTimeoutImpl = function clearStack(fn) {
Function.prototype.apply.apply(realSetTimeout, [global, [fn, 0]]);
};
const NODE_JS =
global.process &&
global.process.versions &&
typeof global.process.versions.node === 'string';
if (j$.isFunction_(global.setImmediate)) {
var realSetImmediate = global.setImmediate;
return function(fn) {
currentCallCount++;
const SAFARI =
global.navigator &&
/^((?!chrome|android).)*safari/i.test(global.navigator.userAgent);
if (currentCallCount < maxInlineCallCount) {
realSetImmediate(fn);
} else {
currentCallCount = 0;
setTimeoutImpl(fn);
}
};
} else if (!j$.util.isUndefined(global.MessageChannel)) {
return messageChannelImpl(global, setTimeoutImpl);
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 {
return setTimeoutImpl;
// MessageChannel is faster than queueMicrotask in supported browsers
// other than Safari.
return messageChannelImpl(global);
}
}

View File

@@ -1,6 +1,6 @@
getJasmineRequireObj().Clock = function() {
/* global process */
var NODE_JS =
const NODE_JS =
typeof process !== 'undefined' &&
process.versions &&
typeof process.versions.node === 'string';
@@ -14,24 +14,23 @@ getJasmineRequireObj().Clock = function() {
* @hideconstructor
*/
function Clock(global, delayedFunctionSchedulerFactory, mockDate) {
var self = this,
realTimingFunctions = {
setTimeout: global.setTimeout,
clearTimeout: global.clearTimeout,
setInterval: global.setInterval,
clearInterval: global.clearInterval
},
fakeTimingFunctions = {
setTimeout: setTimeout,
clearTimeout: clearTimeout,
setInterval: setInterval,
clearInterval: clearInterval
},
installed = false,
delayedFunctionScheduler,
timer;
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;
self.FakeTimeout = FakeTimeout;
this.FakeTimeout = FakeTimeout;
/**
* Install the mock clock over the built-in methods.
@@ -40,7 +39,7 @@ getJasmineRequireObj().Clock = function() {
* @function
* @return {Clock}
*/
self.install = function() {
this.install = function() {
if (!originalTimingFunctionsIntact()) {
throw new Error(
'Jasmine Clock was unable to install over custom global timer functions. Is the clock already installed?'
@@ -51,7 +50,7 @@ getJasmineRequireObj().Clock = function() {
delayedFunctionScheduler = delayedFunctionSchedulerFactory();
installed = true;
return self;
return this;
};
/**
@@ -60,7 +59,7 @@ getJasmineRequireObj().Clock = function() {
* @since 2.0.0
* @function
*/
self.uninstall = function() {
this.uninstall = function() {
delayedFunctionScheduler = null;
mockDate.uninstall();
replace(global, realTimingFunctions);
@@ -78,7 +77,7 @@ getJasmineRequireObj().Clock = function() {
* @function
* @param {Function} closure The function to be called.
*/
self.withMock = function(closure) {
this.withMock = function(closure) {
this.install();
try {
closure();
@@ -94,29 +93,29 @@ getJasmineRequireObj().Clock = function() {
* @function
* @param {Date} [initialDate=now] The `Date` to provide.
*/
self.mockDate = function(initialDate) {
this.mockDate = function(initialDate) {
mockDate.install(initialDate);
};
self.setTimeout = function(fn, delay, params) {
this.setTimeout = function(fn, delay, params) {
return Function.prototype.apply.apply(timer.setTimeout, [
global,
arguments
]);
};
self.setInterval = function(fn, delay, params) {
this.setInterval = function(fn, delay, params) {
return Function.prototype.apply.apply(timer.setInterval, [
global,
arguments
]);
};
self.clearTimeout = function(id) {
this.clearTimeout = function(id) {
return Function.prototype.call.apply(timer.clearTimeout, [global, id]);
};
self.clearInterval = function(id) {
this.clearInterval = function(id) {
return Function.prototype.call.apply(timer.clearInterval, [global, id]);
};
@@ -127,7 +126,7 @@ getJasmineRequireObj().Clock = function() {
* @function
* @param {int} millis The number of milliseconds to tick.
*/
self.tick = function(millis) {
this.tick = function(millis) {
if (installed) {
delayedFunctionScheduler.tick(millis, function(millis) {
mockDate.tick(millis);
@@ -139,7 +138,7 @@ getJasmineRequireObj().Clock = function() {
}
};
return self;
return this;
function originalTimingFunctionsIntact() {
return (
@@ -151,7 +150,7 @@ getJasmineRequireObj().Clock = function() {
}
function replace(dest, source) {
for (var prop in source) {
for (const prop in source) {
dest[prop] = source[prop];
}
}
@@ -165,7 +164,7 @@ getJasmineRequireObj().Clock = function() {
);
}
var timeout = new FakeTimeout();
const timeout = new FakeTimeout();
delayedFunctionScheduler.scheduleFunction(
fn,
@@ -192,7 +191,7 @@ getJasmineRequireObj().Clock = function() {
);
}
var timeout = new FakeTimeout();
const timeout = new FakeTimeout();
delayedFunctionScheduler.scheduleFunction(
fn,

View File

@@ -1,20 +1,19 @@
getJasmineRequireObj().DelayedFunctionScheduler = function(j$) {
function DelayedFunctionScheduler() {
var self = this;
var scheduledLookup = [];
var scheduledFunctions = {};
var currentTime = 0;
var delayedFnCount = 0;
var deletedKeys = [];
this.scheduledLookup_ = [];
this.scheduledFunctions_ = {};
this.currentTime_ = 0;
this.delayedFnCount_ = 0;
this.deletedKeys_ = [];
self.tick = function(millis, tickDate) {
this.tick = function(millis, tickDate) {
millis = millis || 0;
var endTime = currentTime + millis;
const endTime = this.currentTime_ + millis;
runScheduledFunctions(endTime, tickDate);
this.runScheduledFunctions_(endTime, tickDate);
};
self.scheduleFunction = function(
this.scheduleFunction = function(
funcToCall,
millis,
params,
@@ -22,22 +21,21 @@ getJasmineRequireObj().DelayedFunctionScheduler = function(j$) {
timeoutKey,
runAtMillis
) {
var f;
let f;
if (typeof funcToCall === 'string') {
/* jshint evil: true */
f = function() {
// eslint-disable-next-line no-eval
return eval(funcToCall);
};
/* jshint evil: false */
} else {
f = funcToCall;
}
millis = millis || 0;
timeoutKey = timeoutKey || ++delayedFnCount;
runAtMillis = runAtMillis || currentTime + millis;
timeoutKey = timeoutKey || ++this.delayedFnCount_;
runAtMillis = runAtMillis || this.currentTime_ + millis;
var funcToSchedule = {
const funcToSchedule = {
runAtMillis: runAtMillis,
funcToCall: f,
recurring: recurring,
@@ -46,12 +44,12 @@ getJasmineRequireObj().DelayedFunctionScheduler = function(j$) {
millis: millis
};
if (runAtMillis in scheduledFunctions) {
scheduledFunctions[runAtMillis].push(funcToSchedule);
if (runAtMillis in this.scheduledFunctions_) {
this.scheduledFunctions_[runAtMillis].push(funcToSchedule);
} else {
scheduledFunctions[runAtMillis] = [funcToSchedule];
scheduledLookup.push(runAtMillis);
scheduledLookup.sort(function(a, b) {
this.scheduledFunctions_[runAtMillis] = [funcToSchedule];
this.scheduledLookup_.push(runAtMillis);
this.scheduledLookup_.sort(function(a, b) {
return a - b;
});
}
@@ -59,19 +57,19 @@ getJasmineRequireObj().DelayedFunctionScheduler = function(j$) {
return timeoutKey;
};
self.removeFunctionWithId = function(timeoutKey) {
deletedKeys.push(timeoutKey);
this.removeFunctionWithId = function(timeoutKey) {
this.deletedKeys_.push(timeoutKey);
for (var runAtMillis in scheduledFunctions) {
var funcs = scheduledFunctions[runAtMillis];
var i = indexOfFirstToPass(funcs, function(func) {
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 scheduledFunctions[runAtMillis];
deleteFromLookup(runAtMillis);
delete this.scheduledFunctions_[runAtMillis];
this.deleteFromLookup_(runAtMillis);
} else {
funcs.splice(i, 1);
}
@@ -83,99 +81,99 @@ getJasmineRequireObj().DelayedFunctionScheduler = function(j$) {
}
};
return self;
return this;
}
function indexOfFirstToPass(array, testFn) {
var index = -1;
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;
}
for (var i = 0; i < array.length; ++i) {
if (testFn(array[i])) {
index = i;
break;
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);
}
}
return index;
}
function deleteFromLookup(key) {
var value = Number(key);
var i = indexOfFirstToPass(scheduledLookup, function(millis) {
return millis === value;
});
if (i > -1) {
scheduledLookup.splice(i, 1);
}
}
function reschedule(scheduledFn) {
self.scheduleFunction(
scheduledFn.funcToCall,
scheduledFn.millis,
scheduledFn.params,
true,
scheduledFn.timeoutKey,
scheduledFn.runAtMillis + scheduledFn.millis
);
}
function forEachFunction(funcsToRun, callback) {
for (var i = 0; i < funcsToRun.length; ++i) {
callback(funcsToRun[i]);
}
}
function runScheduledFunctions(endTime, tickDate) {
tickDate = tickDate || function() {};
if (scheduledLookup.length === 0 || scheduledLookup[0] > endTime) {
if (endTime >= currentTime) {
tickDate(endTime - currentTime);
currentTime = endTime;
for (const fn of funcsToRun) {
if (this.deletedKeys_.includes(fn.timeoutKey)) {
// skip a timeoutKey deleted whilst we were running
return;
}
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
);
do {
deletedKeys = [];
var newCurrentTime = scheduledLookup.shift();
if (newCurrentTime >= currentTime) {
tickDate(newCurrentTime - currentTime);
currentTime = newCurrentTime;
}
// ran out of functions to call, but still time left on the clock
if (endTime >= this.currentTime_) {
tickDate(endTime - this.currentTime_);
this.currentTime_ = endTime;
}
};
var funcsToRun = scheduledFunctions[currentTime];
DelayedFunctionScheduler.prototype.reschedule_ = function(scheduledFn) {
this.scheduleFunction(
scheduledFn.funcToCall,
scheduledFn.millis,
scheduledFn.params,
true,
scheduledFn.timeoutKey,
scheduledFn.runAtMillis + scheduledFn.millis
);
};
delete scheduledFunctions[currentTime];
DelayedFunctionScheduler.prototype.deleteFromLookup_ = function(key) {
const value = Number(key);
const i = indexOfFirstToPass(this.scheduledLookup_, function(millis) {
return millis === value;
});
forEachFunction(funcsToRun, function(funcToRun) {
if (funcToRun.recurring) {
reschedule(funcToRun);
}
});
if (i > -1) {
this.scheduledLookup_.splice(i, 1);
}
};
forEachFunction(funcsToRun, function(funcToRun) {
if (j$.util.arrayContains(deletedKeys, funcToRun.timeoutKey)) {
// skip a timeoutKey deleted whilst we were running
return;
}
funcToRun.funcToCall.apply(null, funcToRun.params || []);
});
deletedKeys = [];
} while (
scheduledLookup.length > 0 &&
// checking first if we're out of time prevents setTimeout(0)
// scheduled in a funcToRun from forcing an extra iteration
currentTime !== endTime &&
scheduledLookup[0] <= endTime
);
function indexOfFirstToPass(array, testFn) {
let index = -1;
// ran out of functions to call, but still time left on the clock
if (endTime >= currentTime) {
tickDate(endTime - currentTime);
currentTime = endTime;
for (let i = 0; i < array.length; ++i) {
if (testFn(array[i])) {
index = i;
break;
}
}
return index;
}
return DelayedFunctionScheduler;

View File

@@ -5,7 +5,7 @@ getJasmineRequireObj().Deprecator = function(j$) {
this.toSuppress_ = [];
}
var verboseNote =
const verboseNote =
'Note: This message will be shown only once. Set the verboseDeprecations ' +
'config property to true to see every occurrence.';
@@ -35,13 +35,13 @@ getJasmineRequireObj().Deprecator = function(j$) {
};
Deprecator.prototype.log_ = function(runnable, deprecation, options) {
var context;
if (j$.isError_(deprecation)) {
console.error(deprecation);
return;
}
let context;
if (runnable === this.topSuite_ || options.ignoreRunnable) {
context = '';
} else if (runnable.children) {
@@ -62,7 +62,7 @@ getJasmineRequireObj().Deprecator = function(j$) {
};
Deprecator.prototype.stackTrace_ = function() {
var formatter = new j$.ExceptionFormatter();
const formatter = new j$.ExceptionFormatter();
return formatter.stack(j$.util.errorWithStack()).replace(/^Error\n/m, '');
};

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +1,5 @@
getJasmineRequireObj().ExceptionFormatter = function(j$) {
var ignoredProperties = [
const ignoredProperties = [
'name',
'message',
'stack',
@@ -13,9 +13,10 @@ getJasmineRequireObj().ExceptionFormatter = function(j$) {
];
function ExceptionFormatter(options) {
var jasmineFile = (options && options.jasmineFile) || j$.util.jasmineFile();
const jasmineFile =
(options && options.jasmineFile) || j$.util.jasmineFile();
this.message = function(error) {
var message = '';
let message = '';
if (error.jasmineMessage) {
message += error.jasmineMessage;
@@ -43,24 +44,44 @@ getJasmineRequireObj().ExceptionFormatter = function(j$) {
return null;
}
var stackTrace = new j$.StackTrace(error);
var lines = filterJasmine(stackTrace);
var result = '';
const lines = this.stack_(error, {
messageHandling: omitMessage ? 'omit' : undefined
});
return lines.join('\n');
};
if (stackTrace.message && !omitMessage) {
// 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);
}
result += formatProperties(error);
result += lines.join('\n');
if (error.cause) {
const substack = this.stack_(error.cause, {
messageHandling: 'require'
});
substack[0] = 'Caused by: ' + substack[0];
lines = lines.concat(substack);
}
return result;
return lines;
};
function filterJasmine(stackTrace) {
var result = [],
jasmineMarker =
stackTrace.style === 'webkit' ? '<Jasmine>' : ' at <Jasmine>';
const result = [];
const jasmineMarker =
stackTrace.style === 'webkit' ? '<Jasmine>' : ' at <Jasmine>';
stackTrace.frames.forEach(function(frame) {
if (frame.file !== jasmineFile) {
@@ -78,11 +99,11 @@ getJasmineRequireObj().ExceptionFormatter = function(j$) {
return;
}
var result = {};
var empty = true;
const result = {};
let empty = true;
for (var prop in error) {
if (j$.util.arrayContains(ignoredProperties, prop)) {
for (const prop in error) {
if (ignoredProperties.includes(prop)) {
continue;
}
result[prop] = error[prop];

View File

@@ -6,8 +6,8 @@ getJasmineRequireObj().Expectation = function(j$) {
function Expectation(options) {
this.expector = new j$.Expector(options);
var customMatchers = options.customMatchers || {};
for (var matcherName in customMatchers) {
const customMatchers = options.customMatchers || {};
for (const matcherName in customMatchers) {
this[matcherName] = wrapSyncCompare(
matcherName,
customMatchers[matcherName]
@@ -77,8 +77,8 @@ getJasmineRequireObj().Expectation = function(j$) {
function AsyncExpectation(options) {
this.expector = new j$.Expector(options);
var customAsyncMatchers = options.customAsyncMatchers || {};
for (var matcherName in customAsyncMatchers) {
const customAsyncMatchers = options.customAsyncMatchers || {};
for (const matcherName in customAsyncMatchers) {
this[matcherName] = wrapAsyncCompare(
matcherName,
customAsyncMatchers[matcherName]
@@ -134,36 +134,34 @@ getJasmineRequireObj().Expectation = function(j$) {
function wrapSyncCompare(name, matcherFactory) {
return function() {
var result = this.expector.compare(name, matcherFactory, arguments);
const result = this.expector.compare(name, matcherFactory, arguments);
this.expector.processResult(result);
};
}
function wrapAsyncCompare(name, matcherFactory) {
return function() {
var self = this;
// 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.
var errorForStack = j$.util.errorWithStack();
const errorForStack = j$.util.errorWithStack();
return this.expector
.compare(name, matcherFactory, arguments)
.then(function(result) {
self.expector.processResult(result, errorForStack);
.then(result => {
this.expector.processResult(result, errorForStack);
});
};
}
function addCoreMatchers(prototype, matchers, wrapper) {
for (var matcherName in matchers) {
var matcher = matchers[matcherName];
for (const matcherName in matchers) {
const matcher = matchers[matcherName];
prototype[matcherName] = wrapper(matcherName, matcher);
}
}
function addFilter(source, filter) {
var result = Object.create(source);
const result = Object.create(source);
result.expector = source.expector.addFilter(filter);
return result;
}
@@ -188,7 +186,7 @@ getJasmineRequireObj().Expectation = function(j$) {
return result;
}
var syncNegatingFilter = {
const syncNegatingFilter = {
selectComparisonFunc: function(matcher) {
function defaultNegativeCompare() {
return negate(matcher.compare.apply(null, arguments));
@@ -199,7 +197,7 @@ getJasmineRequireObj().Expectation = function(j$) {
buildFailureMessage: negatedFailureMessage
};
var asyncNegatingFilter = {
const asyncNegatingFilter = {
selectComparisonFunc: function(matcher) {
function defaultNegativeCompare() {
return matcher.compare.apply(this, arguments).then(negate);
@@ -210,10 +208,10 @@ getJasmineRequireObj().Expectation = function(j$) {
buildFailureMessage: negatedFailureMessage
};
var expectSettledPromiseFilter = {
const expectSettledPromiseFilter = {
selectComparisonFunc: function(matcher) {
return function(actual) {
var matcherArgs = arguments;
const matcherArgs = arguments;
return j$.isPending_(actual).then(function(isPending) {
if (isPending) {
@@ -236,9 +234,7 @@ getJasmineRequireObj().Expectation = function(j$) {
}
ContextAddingFilter.prototype.modifyFailureMessage = function(msg) {
var nl = msg.indexOf('\n');
if (nl === -1) {
if (msg.indexOf('\n') === -1) {
return this.message + ': ' + msg;
} else {
return this.message + ':\n' + indent(msg);

View File

@@ -22,15 +22,13 @@ getJasmineRequireObj().ExpectationFilterChain = function() {
};
ExpectationFilterChain.prototype.modifyFailureMessage = function(msg) {
var result = this.callFirst_('modifyFailureMessage', arguments).result;
const result = this.callFirst_('modifyFailureMessage', arguments).result;
return result || msg;
};
ExpectationFilterChain.prototype.callFirst_ = function(fname, args) {
var prevResult;
if (this.prev_) {
prevResult = this.prev_.callFirst_(fname, args);
const prevResult = this.prev_.callFirst_(fname, args);
if (prevResult.found) {
return prevResult;

View File

@@ -19,35 +19,24 @@ getJasmineRequireObj().Expector = function(j$) {
this.args.unshift(this.actual);
var matcher = matcherFactory(this.matchersUtil);
const matcher = matcherFactory(this.matchersUtil);
var comparisonFunc = this.filters.selectComparisonFunc(matcher);
const comparisonFunc = this.filters.selectComparisonFunc(matcher);
return comparisonFunc || matcher.compare;
};
Expector.prototype.buildMessage = function(result) {
var self = this;
if (result.pass) {
return '';
}
var msg = this.filters.buildFailureMessage(
result,
this.matcherName,
this.args,
this.matchersUtil,
defaultMessage
);
return this.filters.modifyFailureMessage(msg || defaultMessage());
function defaultMessage() {
const defaultMessage = () => {
if (!result.message) {
var args = self.args.slice();
const args = this.args.slice();
args.unshift(false);
args.unshift(self.matcherName);
return self.matchersUtil.buildFailureMessage.apply(
self.matchersUtil,
args.unshift(this.matcherName);
return this.matchersUtil.buildFailureMessage.apply(
this.matchersUtil,
args
);
} else if (j$.isFunction_(result.message)) {
@@ -55,11 +44,20 @@ getJasmineRequireObj().Expector = function(j$) {
} 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) {
var matcherCompare = this.instantiateMatcher(
const matcherCompare = this.instantiateMatcher(
matcherName,
matcherFactory,
args
@@ -68,13 +66,13 @@ getJasmineRequireObj().Expector = function(j$) {
};
Expector.prototype.addFilter = function(filter) {
var result = Object.create(this);
const result = Object.create(this);
result.filters = this.filters.addFilter(filter);
return result;
};
Expector.prototype.processResult = function(result, errorForStack) {
var message = this.buildMessage(result);
const message = this.buildMessage(result);
if (this.expected.length === 1) {
this.expected = this.expected[0];

View File

@@ -1,27 +1,35 @@
getJasmineRequireObj().GlobalErrors = function(j$) {
function GlobalErrors(global) {
var handlers = [];
global = global || j$.getGlobal();
var onerror = function onerror() {
var handler = handlers[handlers.length - 1];
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) {
var substituteMsg;
if (j$.isError_(error)) {
error.jasmineMessage = jasmineMessage + ': ' + error;
} else {
let substituteMsg;
if (error) {
substituteMsg = jasmineMessage + ': ' + error;
} else {
@@ -40,7 +48,12 @@ getJasmineRequireObj().GlobalErrors = function(j$) {
error = new Error(substituteMsg);
}
var handler = handlers[handlers.length - 1];
const handler = handlers[handlers.length - 1];
if (overrideHandler) {
overrideHandler(error);
return;
}
if (handler) {
handler(error);
@@ -56,14 +69,14 @@ getJasmineRequireObj().GlobalErrors = function(j$) {
global.process.on(errorType, taggedOnError);
this.uninstall = function uninstall() {
var errorTypes = Object.keys(this.originalHandlers);
for (var iType = 0; iType < errorTypes.length; iType++) {
var errorType = errorTypes[iType];
const errorTypes = Object.keys(this.originalHandlers);
for (const errorType of errorTypes) {
global.process.removeListener(
errorType,
this.jasmineHandlers[errorType]
);
for (var i = 0; i < this.originalHandlers[errorType].length; i++) {
for (let i = 0; i < this.originalHandlers[errorType].length; i++) {
global.process.on(errorType, this.originalHandlers[errorType][i]);
}
delete this.originalHandlers[errorType];
@@ -81,10 +94,12 @@ getJasmineRequireObj().GlobalErrors = function(j$) {
this.installOne_('uncaughtException', 'Uncaught exception');
this.installOne_('unhandledRejection', 'Unhandled promise rejection');
} else {
var originalHandler = global.onerror;
const originalHandler = global.onerror;
global.onerror = onerror;
var browserRejectionHandler = function browserRejectionHandler(event) {
const browserRejectionHandler = function browserRejectionHandler(
event
) {
if (j$.isError_(event.reason)) {
event.reason.jasmineMessage =
'Unhandled promise rejection: ' + event.reason;
@@ -94,21 +109,14 @@ getJasmineRequireObj().GlobalErrors = function(j$) {
}
};
if (global.addEventListener) {
global.addEventListener(
'unhandledrejection',
browserRejectionHandler
);
}
global.addEventListener('unhandledrejection', browserRejectionHandler);
this.uninstall = function uninstall() {
global.onerror = originalHandler;
if (global.removeEventListener) {
global.removeEventListener(
'unhandledrejection',
browserRejectionHandler
);
}
global.removeEventListener(
'unhandledrejection',
browserRejectionHandler
);
};
}
};
@@ -124,6 +132,24 @@ getJasmineRequireObj().GlobalErrors = function(j$) {
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;

View File

@@ -6,8 +6,8 @@ getJasmineRequireObj().JsApiReporter = function(j$) {
* @hideconstructor
*/
function JsApiReporter(options) {
var timer = options.timer || new j$.Timer(),
status = 'loaded';
const timer = options.timer || new j$.Timer();
let status = 'loaded';
this.started = false;
this.finished = false;
@@ -19,7 +19,7 @@ getJasmineRequireObj().JsApiReporter = function(j$) {
timer.start();
};
var executionTime;
let executionTime;
this.jasmineDone = function(runDetails) {
this.finished = true;
@@ -39,7 +39,7 @@ getJasmineRequireObj().JsApiReporter = function(j$) {
return status;
};
var suites = [],
const suites = [],
suites_hash = {};
this.suiteStarted = function(result) {
@@ -81,7 +81,7 @@ getJasmineRequireObj().JsApiReporter = function(j$) {
return suites_hash;
};
var specs = [];
const specs = [];
this.specDone = function(result) {
specs.push(result);

View File

@@ -1,18 +1,17 @@
getJasmineRequireObj().MockDate = function(j$) {
function MockDate(global) {
var self = this;
var currentTime = 0;
let currentTime = 0;
if (!global || !global.Date) {
self.install = function() {};
self.tick = function() {};
self.uninstall = function() {};
return self;
this.install = function() {};
this.tick = function() {};
this.uninstall = function() {};
return this;
}
var GlobalDate = global.Date;
const GlobalDate = global.Date;
self.install = function(mockDate) {
this.install = function(mockDate) {
if (mockDate instanceof GlobalDate) {
currentTime = mockDate.getTime();
} else {
@@ -29,19 +28,19 @@ getJasmineRequireObj().MockDate = function(j$) {
global.Date = FakeDate;
};
self.tick = function(millis) {
this.tick = function(millis) {
millis = millis || 0;
currentTime = currentTime + millis;
};
self.uninstall = function() {
this.uninstall = function() {
currentTime = 0;
global.Date = GlobalDate;
};
createDateProperties();
return self;
return this;
function FakeDate() {
switch (arguments.length) {

View File

@@ -1,9 +1,7 @@
/*jshint bitwise: false*/
getJasmineRequireObj().Order = function() {
function Order(options) {
this.random = 'random' in options ? options.random : true;
var seed = (this.seed = options.seed || generateSeed());
const seed = (this.seed = options.seed || generateSeed());
this.sort = this.random ? randomOrder : naturalOrder;
function naturalOrder(items) {
@@ -11,7 +9,7 @@ getJasmineRequireObj().Order = function() {
}
function randomOrder(items) {
var copy = items.slice();
const copy = items.slice();
copy.sort(function(a, b) {
return jenkinsHash(seed + a.id) - jenkinsHash(seed + b.id);
});
@@ -28,7 +26,7 @@ getJasmineRequireObj().Order = function() {
// used in conjunction with a seed
function jenkinsHash(key) {
var hash, i;
let hash, i;
for (hash = i = 0; i < key.length; ++i) {
hash += key.charCodeAt(i);
hash += hash << 10;

View File

@@ -1,11 +1,298 @@
getJasmineRequireObj().makePrettyPrinter = function(j$) {
function SinglePrettyPrintRun(customObjectFormatters, pp) {
this.customObjectFormatters_ = customObjectFormatters;
this.ppNestLevel_ = 0;
this.seen = [];
this.length = 0;
this.stringParts = [];
this.pp_ = pp;
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('<global>');
} 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(
'<circular reference: ' +
(j$.isArray_(value) ? 'Array' : 'Object') +
'>'
);
} 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 += '...</' + tagName + '>';
}
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) {
@@ -23,315 +310,6 @@ getJasmineRequireObj().makePrettyPrinter = function(j$) {
}
}
SinglePrettyPrintRun.prototype.format = function(value) {
this.ppNestLevel_++;
try {
var 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('<global>');
} 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 (j$.util.arrayContains(this.seen, value)) {
this.emitScalar(
'<circular reference: ' +
(j$.isArray_(value) ? 'Array' : 'Object') +
'>'
);
} 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_--;
}
};
SinglePrettyPrintRun.prototype.applyCustomFormatters_ = function(value) {
return customFormat(value, this.customObjectFormatters_);
};
SinglePrettyPrintRun.prototype.iterateObject = function(obj, fn) {
var objKeys = j$.MatchersUtil.keys(obj, j$.isArray_(obj));
var isGetter = function isGetter(prop) {};
if (obj.__lookupGetter__) {
isGetter = function isGetter(prop) {
var getter = obj.__lookupGetter__(prop);
return !j$.util.isUndefined(getter) && getter !== null;
};
}
var length = Math.min(objKeys.length, j$.MAX_PRETTY_PRINT_ARRAY_LENGTH);
for (var i = 0; i < length; i++) {
var property = objKeys[i];
fn(property, isGetter(property));
}
return objKeys.length > length;
};
SinglePrettyPrintRun.prototype.emitScalar = function(value) {
this.append(value);
};
SinglePrettyPrintRun.prototype.emitString = function(value) {
this.append("'" + value + "'");
};
SinglePrettyPrintRun.prototype.emitArray = function(array) {
if (this.ppNestLevel_ > j$.MAX_PRETTY_PRINT_DEPTH) {
this.append('Array');
return;
}
var length = Math.min(array.length, j$.MAX_PRETTY_PRINT_ARRAY_LENGTH);
this.append('[ ');
for (var i = 0; i < length; i++) {
if (i > 0) {
this.append(', ');
}
this.format(array[i]);
}
if (array.length > length) {
this.append(', ...');
}
var self = this;
var first = array.length === 0;
var truncated = this.iterateObject(array, function(property, isGetter) {
if (first) {
first = false;
} else {
self.append(', ');
}
self.formatProperty(array, property, isGetter);
});
if (truncated) {
this.append(', ...');
}
this.append(' ]');
};
SinglePrettyPrintRun.prototype.emitSet = function(set) {
if (this.ppNestLevel_ > j$.MAX_PRETTY_PRINT_DEPTH) {
this.append('Set');
return;
}
this.append('Set( ');
var size = Math.min(set.size, j$.MAX_PRETTY_PRINT_ARRAY_LENGTH);
var 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(' )');
};
SinglePrettyPrintRun.prototype.emitMap = function(map) {
if (this.ppNestLevel_ > j$.MAX_PRETTY_PRINT_DEPTH) {
this.append('Map');
return;
}
this.append('Map( ');
var size = Math.min(map.size, j$.MAX_PRETTY_PRINT_ARRAY_LENGTH);
var 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(' )');
};
SinglePrettyPrintRun.prototype.emitObject = function(obj) {
var ctor = obj.constructor,
constructorName;
constructorName =
typeof ctor === 'function' && obj instanceof ctor
? j$.fnNameFor(obj.constructor)
: 'null';
this.append(constructorName);
if (this.ppNestLevel_ > j$.MAX_PRETTY_PRINT_DEPTH) {
return;
}
var self = this;
this.append('({ ');
var first = true;
var truncated = this.iterateObject(obj, function(property, isGetter) {
if (first) {
first = false;
} else {
self.append(', ');
}
self.formatProperty(obj, property, isGetter);
});
if (truncated) {
this.append(', ...');
}
this.append(' })');
};
SinglePrettyPrintRun.prototype.emitTypedArray = function(arr) {
var constructorName = j$.fnNameFor(arr.constructor),
limitedArray = Array.prototype.slice.call(
arr,
0,
j$.MAX_PRETTY_PRINT_ARRAY_LENGTH
),
itemsString = Array.prototype.join.call(limitedArray, ', ');
if (limitedArray.length !== arr.length) {
itemsString += ', ...';
}
this.append(constructorName + ' [ ' + itemsString + ' ]');
};
SinglePrettyPrintRun.prototype.emitDomElement = function(el) {
var tagName = el.tagName.toLowerCase(),
attrs = el.attributes,
i,
len = attrs.length,
out = '<' + tagName,
attr;
for (i = 0; i < len; i++) {
attr = attrs[i];
out += ' ' + attr.name;
if (attr.value !== '') {
out += '="' + attr.value + '"';
}
}
out += '>';
if (el.childElementCount !== 0 || el.textContent !== '') {
out += '...</' + tagName + '>';
}
this.append(out);
};
SinglePrettyPrintRun.prototype.formatProperty = function(
obj,
property,
isGetter
) {
if (typeof property === 'symbol') {
this.append(property.toString());
} else {
this.append(property);
}
this.append(': ');
if (isGetter) {
this.append('<getter>');
} else {
this.format(obj[property]);
}
};
SinglePrettyPrintRun.prototype.append = function(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);
}
var 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 truncate(s, maxlen) {
if (s.length <= maxlen) {
return { value: s, truncated: false };
@@ -351,10 +329,8 @@ getJasmineRequireObj().makePrettyPrinter = function(j$) {
MaxCharsReachedError.prototype = new Error();
function customFormat(value, customObjectFormatters) {
var i, result;
for (i = 0; i < customObjectFormatters.length; i++) {
result = customObjectFormatters[i](value);
for (const formatter of customObjectFormatters) {
const result = formatter(value);
if (result !== undefined) {
return result;
@@ -365,8 +341,11 @@ getJasmineRequireObj().makePrettyPrinter = function(j$) {
return function(customObjectFormatters) {
customObjectFormatters = customObjectFormatters || [];
var pp = function(value) {
var prettyPrinter = new SinglePrettyPrintRun(customObjectFormatters, pp);
const pp = function(value) {
const prettyPrinter = new SinglePrettyPrintRun(
customObjectFormatters,
pp
);
prettyPrinter.format(value);
return prettyPrinter.stringParts.join('');
};

View File

@@ -1,12 +1,12 @@
getJasmineRequireObj().QueueRunner = function(j$) {
var nextid = 1;
let nextid = 1;
function StopExecutionError() {}
StopExecutionError.prototype = new Error();
j$.StopExecutionError = StopExecutionError;
function once(fn, onTwice) {
var called = false;
let called = false;
return function(arg) {
if (called) {
if (onTwice) {
@@ -66,12 +66,11 @@ getJasmineRequireObj().QueueRunner = function(j$) {
}
QueueRunner.prototype.execute = function() {
var self = this;
this.handleFinalError = function(message, source, lineno, colno, error) {
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.
self.onException(error || message);
this.onException(error || message);
};
this.globalErrors.pushListener(this.handleFinalError);
this.run(0);
@@ -92,71 +91,77 @@ getJasmineRequireObj().QueueRunner = function(j$) {
};
QueueRunner.prototype.attempt = function attempt(iterativeIndex) {
var self = this,
completedSynchronously = true,
handleError = function handleError(error) {
// TODO probably shouldn't next() right away here.
// That makes debugging async failures much more confusing.
onException(error);
},
cleanup = once(function cleanup() {
if (timeoutId !== void 0) {
self.clearTimeout(timeoutId);
}
self.globalErrors.popListener(handleError);
}),
next = once(
function next(err) {
cleanup();
let timeoutId;
let timedOut;
let completedSynchronously = true;
if (typeof err !== 'undefined') {
if (!(err instanceof StopExecutionError) && !err.jasmineMessage) {
self.fail(err);
}
self.recordError_(iterativeIndex);
}
function runNext() {
self.run(self.nextFnIx_(iterativeIndex));
}
if (completedSynchronously) {
self.setTimeout(runNext);
} else {
runNext();
}
},
function() {
try {
if (!timedOut) {
self.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,
queueableFn = self.queueableFns[iterativeIndex],
timeoutId,
maybeThenable;
next.fail = function nextFail() {
self.fail.apply(null, arguments);
self.recordError_(iterativeIndex);
next();
const onException = e => {
this.onException(e);
this.recordError_(iterativeIndex);
};
self.globalErrors.pushListener(handleError);
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) {
var timeoutInterval = queueableFn.timeout || j$.DEFAULT_TIMEOUT_INTERVAL;
timeoutId = self.setTimeout(function() {
const timeoutInterval =
queueableFn.timeout || j$.DEFAULT_TIMEOUT_INTERVAL;
timeoutId = this.setTimeout(function() {
timedOut = true;
var error = new Error(
const error = new Error(
'Timeout - Async function did not complete within ' +
timeoutInterval +
'ms ' +
@@ -173,8 +178,10 @@ getJasmineRequireObj().QueueRunner = function(j$) {
}
try {
let maybeThenable;
if (queueableFn.fn.length === 0) {
maybeThenable = queueableFn.fn.call(self.userContext);
maybeThenable = queueableFn.fn.call(this.userContext);
if (maybeThenable && j$.isFunction_(maybeThenable.then)) {
maybeThenable.then(
@@ -185,24 +192,19 @@ getJasmineRequireObj().QueueRunner = function(j$) {
return { completedSynchronously: false };
}
} else {
maybeThenable = queueableFn.fn.call(self.userContext, next);
maybeThenable = queueableFn.fn.call(this.userContext, next);
this.diagnoseConflictingAsync_(queueableFn.fn, maybeThenable);
completedSynchronously = false;
return { completedSynchronously: false };
}
} catch (e) {
onException(e);
self.recordError_(iterativeIndex);
this.recordError_(iterativeIndex);
}
cleanup();
return { completedSynchronously: true };
function onException(e) {
self.onException(e);
self.recordError_(iterativeIndex);
}
function onPromiseRejection(e) {
onException(e);
next();
@@ -210,29 +212,27 @@ getJasmineRequireObj().QueueRunner = function(j$) {
};
QueueRunner.prototype.run = function(recursiveIndex) {
var length = this.queueableFns.length,
self = this,
iterativeIndex;
const length = this.queueableFns.length;
for (
iterativeIndex = recursiveIndex;
let iterativeIndex = recursiveIndex;
iterativeIndex < length;
iterativeIndex = this.nextFnIx_(iterativeIndex)
) {
var result = this.attempt(iterativeIndex);
const result = this.attempt(iterativeIndex);
if (!result.completedSynchronously) {
return;
}
}
this.clearStack(function() {
self.globalErrors.popListener(self.handleFinalError);
this.clearStack(() => {
this.globalErrors.popListener(this.handleFinalError);
if (self.errored_) {
self.onComplete(new StopExecutionError());
if (this.errored_) {
this.onComplete(new StopExecutionError());
} else {
self.onComplete();
this.onComplete();
}
});
};
@@ -253,8 +253,6 @@ getJasmineRequireObj().QueueRunner = function(j$) {
};
QueueRunner.prototype.diagnoseConflictingAsync_ = function(fn, retval) {
var msg;
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
@@ -274,8 +272,6 @@ getJasmineRequireObj().QueueRunner = function(j$) {
'function to not return a promise.'
);
}
this.deprecated(msg, { omitStackTrace: true });
}
};

View File

@@ -1,18 +1,17 @@
getJasmineRequireObj().ReportDispatcher = function(j$) {
function ReportDispatcher(methods, queueRunnerFactory, onLateError) {
var dispatchedMethods = methods || [];
const dispatchedMethods = methods || [];
for (var i = 0; i < dispatchedMethods.length; i++) {
var method = dispatchedMethods[i];
for (const method of dispatchedMethods) {
this[method] = (function(m) {
return function() {
dispatch(m, arguments);
return dispatch(m, arguments);
};
})(method);
}
var reporters = [];
var fallbackReporter = null;
let reporters = [];
let fallbackReporter = null;
this.addReporter = function(reporter) {
reporters.push(reporter);
@@ -32,36 +31,35 @@ getJasmineRequireObj().ReportDispatcher = function(j$) {
if (reporters.length === 0 && fallbackReporter !== null) {
reporters.push(fallbackReporter);
}
var onComplete = args[args.length - 1];
args = j$.util.argsToArray(args).splice(0, args.length - 1);
var fns = [];
for (var i = 0; i < reporters.length; i++) {
var reporter = reporters[i];
const fns = [];
for (const reporter of reporters) {
addFn(fns, reporter, method, args);
}
queueRunnerFactory({
queueableFns: fns,
onComplete: onComplete,
isReporter: true,
onMultipleDone: function() {
onLateError(
new Error(
"An asynchronous reporter callback called its 'done' callback " +
'more than once.'
)
);
}
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) {
var fn = reporter[method];
const fn = reporter[method];
if (!fn) {
return;
}
var thisArgs = j$.util.cloneArgs(args);
const thisArgs = j$.util.cloneArgs(args);
if (fn.length <= 1) {
fns.push({
fn: function() {

View File

@@ -0,0 +1,155 @@
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;
};

230
src/core/Runner.js Normal file
View File

@@ -0,0 +1,230 @@
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;
};

View File

@@ -1,28 +1,11 @@
getJasmineRequireObj().Spec = function(j$) {
/**
* @interface Spec
* @see Configuration#specFilter
* @since 2.0.0
*/
function Spec(attrs) {
this.expectationFactory = attrs.expectationFactory;
this.asyncExpectationFactory = attrs.asyncExpectationFactory;
this.resultCallback = attrs.resultCallback || function() {};
/**
* The unique ID of this spec.
* @name Spec#id
* @readonly
* @type {string}
* @since 2.0.0
*/
this.id = attrs.id;
/**
* The description passed to the {@link it} that created this spec.
* @name Spec#description
* @readonly
* @type {string}
* @since 2.0.0
*/
this.filename = attrs.filename;
this.parentSuiteId = attrs.parentSuiteId;
this.description = attrs.description || '';
this.queueableFn = attrs.queueableFn;
this.beforeAndAfterFns =
@@ -43,10 +26,7 @@ getJasmineRequireObj().Spec = function(j$) {
function() {
return '';
};
this.expectationResultFactory =
attrs.expectationResultFactory || function() {};
this.onLateError = attrs.onLateError || function() {};
this.queueRunnerFactory = attrs.queueRunnerFactory || function() {};
this.catchingExceptions =
attrs.catchingExceptions ||
function() {
@@ -59,41 +39,25 @@ getJasmineRequireObj().Spec = function(j$) {
this.exclude();
}
/**
* @typedef SpecResult
* @property {Int} 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.reset();
}
Spec.prototype.addExpectationResult = function(passed, data, isError) {
var expectationResult = this.expectationResultFactory(data);
const expectationResult = j$.buildExpectationResult(data);
if (passed) {
this.result.passedExpectations.push(expectationResult);
} else {
this.result.failedExpectations.push(expectationResult);
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();
@@ -114,56 +78,57 @@ getJasmineRequireObj().Spec = function(j$) {
return this.asyncExpectationFactory(actual, this);
};
Spec.prototype.execute = function(onComplete, excluded, failSpecWithNoExp) {
var self = this;
var onStart = {
fn: function(done) {
self.timer.start();
self.onStart(self, done);
Spec.prototype.execute = function(
queueRunnerFactory,
onComplete,
excluded,
failSpecWithNoExp
) {
const onStart = {
fn: done => {
this.timer.start();
this.onStart(this, done);
}
};
var complete = {
fn: function(done) {
if (self.autoCleanClosures) {
self.queueableFn.fn = null;
const complete = {
fn: done => {
if (this.autoCleanClosures) {
this.queueableFn.fn = null;
}
self.result.status = self.status(excluded, failSpecWithNoExp);
self.result.duration = self.timer.elapsed();
this.result.status = this.status(excluded, failSpecWithNoExp);
this.result.duration = this.timer.elapsed();
if (self.result.status !== 'failed') {
self.result.debugLogs = null;
if (this.result.status !== 'failed') {
this.result.debugLogs = null;
}
self.resultCallback(self.result, done);
this.resultCallback(this.result, done);
},
type: 'specCleanup'
};
var fns = this.beforeAndAfterFns();
const fns = this.beforeAndAfterFns();
var runnerConfig = {
const runnerConfig = {
isLeaf: true,
queueableFns: [...fns.befores, this.queueableFn, ...fns.afters],
onException: function() {
self.onException.apply(self, arguments);
},
onMultipleDone: function() {
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.
self.onLateError(
this.onLateError(
new Error(
'An asynchronous spec, beforeEach, or afterEach function called its ' +
"'done' callback more than once.\n(in spec: " +
self.getFullName() +
this.getFullName() +
')'
)
);
},
onComplete: function() {
if (self.result.status === 'failed') {
onComplete: () => {
if (this.result.status === 'failed') {
onComplete(new j$.StopExecutionError('spec failed'));
} else {
onComplete();
@@ -180,26 +145,46 @@ getJasmineRequireObj().Spec = function(j$) {
runnerConfig.queueableFns.unshift(onStart);
runnerConfig.queueableFns.push(complete);
this.queueRunnerFactory(runnerConfig);
queueRunnerFactory(runnerConfig);
};
Spec.prototype.reset = function() {
/**
* @typedef SpecResult
* @property {String} id - The unique id of this spec.
* @property {String} description - The description passed to the {@link it} that created this spec.
* @property {String} fullName - The full description including all ancestors of this spec.
* @property {String|null} parentSuiteId - The ID of the suite containing this spec, or null if this spec is not in a describe().
* @property {String} filename - The name of the file the spec was defined in.
* @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(),
parentSuiteId: this.parentSuiteId,
filename: this.filename,
failedExpectations: [],
passedExpectations: [],
deprecationWarnings: [],
pendingReason: this.excludeMessage,
pendingReason: this.excludeMessage || '',
duration: null,
properties: null,
debugLogs: null
};
this.markedPending = this.markedExcluding;
this.reportedDone = false;
};
Spec.prototype.onException = function onException(e) {
Spec.prototype.handleException = function handleException(e) {
if (Spec.isPendingSpecException(e)) {
this.pend(extractCustomPendingMessage(e));
return;
@@ -273,13 +258,6 @@ getJasmineRequireObj().Spec = function(j$) {
return 'passed';
};
/**
* The full description including all ancestors of this spec.
* @name Spec#getFullName
* @function
* @returns {string}
* @since 2.0.0
*/
Spec.prototype.getFullName = function() {
return this.getSpecName(this);
};
@@ -289,7 +267,7 @@ getJasmineRequireObj().Spec = function(j$) {
deprecation = { message: deprecation };
}
this.result.deprecationWarnings.push(
this.expectationResultFactory(deprecation)
j$.buildExpectationResult(deprecation)
);
};
@@ -310,8 +288,8 @@ getJasmineRequireObj().Spec = function(j$) {
});
};
var extractCustomPendingMessage = function(e) {
var fullMessage = e.toString(),
const extractCustomPendingMessage = function(e) {
const fullMessage = e.toString(),
boilerplateStart = fullMessage.indexOf(Spec.pendingSpecExceptionMessage),
boilerplateEnd =
boilerplateStart + Spec.pendingSpecExceptionMessage.length;
@@ -332,6 +310,7 @@ getJasmineRequireObj().Spec = function(j$) {
/**
* @interface Spec
* @see Configuration#specFilter
* @since 2.0.0
*/
Object.defineProperty(Spec.prototype, 'metadata', {
get: function() {
@@ -342,6 +321,7 @@ getJasmineRequireObj().Spec = function(j$) {
* @name Spec#id
* @readonly
* @type {string}
* @since 2.0.0
*/
id: this.id,
@@ -350,6 +330,7 @@ getJasmineRequireObj().Spec = function(j$) {
* @name Spec#description
* @readonly
* @type {string}
* @since 2.0.0
*/
description: this.description,
@@ -358,6 +339,7 @@ getJasmineRequireObj().Spec = function(j$) {
* @name Spec#getFullName
* @function
* @returns {string}
* @since 2.0.0
*/
getFullName: this.getFullName.bind(this)
};

View File

@@ -1,6 +1,6 @@
getJasmineRequireObj().Spy = function(j$) {
var nextOrder = (function() {
var order = 0;
const nextOrder = (function() {
let order = 0;
return function() {
return order++;
@@ -15,9 +15,29 @@ getJasmineRequireObj().Spy = function(j$) {
* @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 || {};
var numArgs = typeof originalFn === 'function' ? originalFn.length : 0,
const numArgs = typeof originalFn === 'function' ? originalFn.length : 0,
wrapper = makeFunc(numArgs, function(context, args, invokeNew) {
return spy(context, args, invokeNew);
}),
@@ -32,27 +52,7 @@ getJasmineRequireObj().Spy = function(j$) {
},
matchersUtil
),
callTracker = new j$.CallTracker(),
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.
*/
var callData = {
object: context,
invocationOrder: nextOrder(),
args: Array.prototype.slice.apply(args)
};
callTracker.track(callData);
var returnValue = strategyDispatcher.exec(context, args, invokeNew);
callData.returnValue = returnValue;
return returnValue;
};
callTracker = new j$.CallTracker();
function makeFunc(length, fn) {
switch (length) {
@@ -99,7 +99,7 @@ getJasmineRequireObj().Spy = function(j$) {
}
}
for (var prop in originalFn) {
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"
@@ -144,15 +144,15 @@ getJasmineRequireObj().Spy = function(j$) {
}
function SpyStrategyDispatcher(strategyArgs, matchersUtil) {
var baseStrategy = new j$.SpyStrategy(strategyArgs);
var argsStrategies = new StrategyDict(function() {
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) {
var strategy = argsStrategies.get(args);
let strategy = argsStrategies.get(args);
if (!strategy) {
if (argsStrategies.any() && !baseStrategy.isConfigured()) {
@@ -187,7 +187,7 @@ getJasmineRequireObj().Spy = function(j$) {
};
StrategyDict.prototype.getOrCreate = function(args) {
var strategy = this.get(args);
let strategy = this.get(args);
if (!strategy) {
strategy = this.strategyFactory();
@@ -201,9 +201,7 @@ getJasmineRequireObj().Spy = function(j$) {
};
StrategyDict.prototype.get = function(args) {
var i;
for (i = 0; i < this.strategies.length; i++) {
for (let i = 0; i < this.strategies.length; i++) {
if (this.matchersUtil.equals(args, this.strategies[i].args)) {
return this.strategies[i].strategy;
}

View File

@@ -4,9 +4,12 @@ getJasmineRequireObj().SpyFactory = function(j$) {
getDefaultStrategyFn,
getMatchersUtil
) {
var self = this;
this.createSpy = function(name, originalFn) {
if (j$.isFunction_(name) && originalFn === undefined) {
originalFn = name;
name = originalFn.name;
}
return j$.Spy(name, getMatchersUtil(), {
originalFn,
customStrategies: getCustomStrategies(),
@@ -15,7 +18,7 @@ getJasmineRequireObj().SpyFactory = function(j$) {
};
this.createSpyObj = function(baseName, methodNames, propertyNames) {
var baseNameIsCollection =
const baseNameIsCollection =
j$.isObject_(baseName) || j$.isArray_(baseName);
if (baseNameIsCollection) {
@@ -24,25 +27,24 @@ getJasmineRequireObj().SpyFactory = function(j$) {
baseName = 'unknown';
}
var obj = {};
var spy, descriptor;
const obj = {};
var methods = normalizeKeyValues(methodNames);
for (var i = 0; i < methods.length; i++) {
spy = obj[methods[i][0]] = self.createSpy(
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]);
}
}
var properties = normalizeKeyValues(propertyNames);
for (var i = 0; i < properties.length; i++) {
descriptor = {
const properties = normalizeKeyValues(propertyNames);
for (let i = 0; i < properties.length; i++) {
const descriptor = {
enumerable: true,
get: self.createSpy(baseName + '.' + properties[i][0] + '.get'),
set: self.createSpy(baseName + '.' + properties[i][0] + '.set')
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]);
@@ -60,13 +62,13 @@ getJasmineRequireObj().SpyFactory = function(j$) {
}
function normalizeKeyValues(object) {
var result = [];
const result = [];
if (j$.isArray_(object)) {
for (var i = 0; i < object.length; i++) {
for (let i = 0; i < object.length; i++) {
result.push([object[i]]);
}
} else if (j$.isObject_(object)) {
for (var key in object) {
for (const key in object) {
if (object.hasOwnProperty(key)) {
result.push([key, object[key]]);
}

View File

@@ -1,15 +1,18 @@
getJasmineRequireObj().SpyRegistry = function(j$) {
var spyOnMsg = j$.formatErrorMsg('<spyOn>', 'spyOn(<object>, <methodName>)');
var spyOnPropertyMsg = j$.formatErrorMsg(
const spyOnMsg = j$.formatErrorMsg(
'<spyOn>',
'spyOn(<object>, <methodName>)'
);
const spyOnPropertyMsg = j$.formatErrorMsg(
'<spyOnProperty>',
'spyOnProperty(<object>, <propName>, [accessType])'
);
function SpyRegistry(options) {
options = options || {};
var global = options.global || j$.getGlobal();
var createSpy = options.createSpy;
var currentSpies =
const global = options.global || j$.getGlobal();
const createSpy = options.createSpy;
const currentSpies =
options.currentSpies ||
function() {
return [];
@@ -20,7 +23,7 @@ getJasmineRequireObj().SpyRegistry = function(j$) {
};
this.spyOn = function(obj, methodName) {
var getErrorMsg = spyOnMsg;
const getErrorMsg = spyOnMsg;
if (j$.util.isUndefined(obj) || obj === null) {
throw new Error(
@@ -48,7 +51,7 @@ getJasmineRequireObj().SpyRegistry = function(j$) {
}
}
var descriptor = Object.getOwnPropertyDescriptor(obj, methodName);
const descriptor = Object.getOwnPropertyDescriptor(obj, methodName);
if (descriptor && !(descriptor.writable || descriptor.set)) {
throw new Error(
@@ -56,9 +59,9 @@ getJasmineRequireObj().SpyRegistry = function(j$) {
);
}
var originalMethod = obj[methodName],
spiedMethod = createSpy(methodName, originalMethod),
restoreStrategy;
const originalMethod = obj[methodName];
const spiedMethod = createSpy(methodName, originalMethod);
let restoreStrategy;
if (
Object.prototype.hasOwnProperty.call(obj, methodName) ||
@@ -85,7 +88,7 @@ getJasmineRequireObj().SpyRegistry = function(j$) {
};
this.spyOnProperty = function(obj, propertyName, accessType) {
var getErrorMsg = spyOnPropertyMsg;
const getErrorMsg = spyOnPropertyMsg;
accessType = accessType || 'get';
@@ -103,7 +106,7 @@ getJasmineRequireObj().SpyRegistry = function(j$) {
throw new Error(getErrorMsg('No property name supplied'));
}
var descriptor = j$.util.getPropertyDescriptor(obj, propertyName);
const descriptor = j$.util.getPropertyDescriptor(obj, propertyName);
if (!descriptor) {
throw new Error(getErrorMsg(propertyName + ' property does not exist'));
@@ -138,9 +141,9 @@ getJasmineRequireObj().SpyRegistry = function(j$) {
}
}
var originalDescriptor = j$.util.clone(descriptor),
spy = createSpy(propertyName, descriptor[accessType]),
restoreStrategy;
const originalDescriptor = j$.util.clone(descriptor);
const spy = createSpy(propertyName, descriptor[accessType]);
let restoreStrategy;
if (Object.prototype.hasOwnProperty.call(obj, propertyName)) {
restoreStrategy = function() {
@@ -170,7 +173,7 @@ getJasmineRequireObj().SpyRegistry = function(j$) {
);
}
var pointer = obj,
let pointer = obj,
propsToSpyOn = [],
properties,
propertiesToSkip = [];
@@ -190,24 +193,24 @@ getJasmineRequireObj().SpyRegistry = function(j$) {
pointer = Object.getPrototypeOf(pointer);
}
for (var i = 0; i < propsToSpyOn.length; i++) {
this.spyOn(obj, propsToSpyOn[i]);
for (const prop of propsToSpyOn) {
this.spyOn(obj, prop);
}
return obj;
};
this.clearSpies = function() {
var spies = currentSpies();
for (var i = spies.length - 1; i >= 0; i--) {
var spyEntry = spies[i];
const spies = currentSpies();
for (let i = spies.length - 1; i >= 0; i--) {
const spyEntry = spies[i];
spyEntry.restoreObjectToOriginalState();
}
};
}
function getProps(obj, includeNonEnumerable) {
var enumerableProperties = Object.keys(obj);
const enumerableProperties = Object.keys(obj);
if (!includeNonEnumerable) {
return enumerableProperties;
@@ -222,10 +225,9 @@ getJasmineRequireObj().SpyRegistry = function(j$) {
}
function getSpyableFunctionProps(obj, propertiesToCheck) {
var props = [],
prop;
for (var i = 0; i < propertiesToCheck.length; i++) {
prop = propertiesToCheck[i];
const props = [];
for (const prop of propertiesToCheck) {
if (
Object.prototype.hasOwnProperty.call(obj, prop) &&
isSpyableProp(obj, prop)
@@ -237,14 +239,15 @@ getJasmineRequireObj().SpyRegistry = function(j$) {
}
function isSpyableProp(obj, prop) {
var value, descriptor;
let value;
try {
value = obj[prop];
} catch (e) {
return false;
}
if (value instanceof Function) {
descriptor = Object.getOwnPropertyDescriptor(obj, prop);
const descriptor = Object.getOwnPropertyDescriptor(obj, prop);
return (descriptor.writable || descriptor.set) && descriptor.configurable;
}
return false;

View File

@@ -5,8 +5,6 @@ getJasmineRequireObj().SpyStrategy = function(j$) {
function SpyStrategy(options) {
options = options || {};
var self = this;
/**
* Get the identifying information for the spy.
* @name SpyStrategy#identity
@@ -19,9 +17,8 @@ getJasmineRequireObj().SpyStrategy = function(j$) {
this.getSpy = options.getSpy || function() {};
this.plan = this._defaultPlan = function() {};
var k,
cs = options.customStrategies || {};
for (k in cs) {
const cs = options.customStrategies || {};
for (const k in cs) {
if (j$.util.has(cs, k) && !this[k]) {
this[k] = createCustomPlan(cs[k]);
}
@@ -35,10 +32,10 @@ getJasmineRequireObj().SpyStrategy = function(j$) {
* @param {*} value The value to return.
*/
this.resolveTo = function(value) {
self.plan = function() {
this.plan = function() {
return Promise.resolve(value);
};
return self.getSpy();
return this.getSpy();
};
/**
@@ -49,16 +46,16 @@ getJasmineRequireObj().SpyStrategy = function(j$) {
* @param {*} value The value to return.
*/
this.rejectWith = function(value) {
self.plan = function() {
this.plan = function() {
return Promise.reject(value);
};
return self.getSpy();
return this.getSpy();
};
}
function createCustomPlan(factory) {
return function() {
var plan = factory.apply(null, arguments);
const plan = factory.apply(null, arguments);
if (!j$.isFunction_(plan)) {
throw new Error('Spy strategy must return a function');
@@ -76,10 +73,10 @@ getJasmineRequireObj().SpyStrategy = function(j$) {
* @function
*/
SpyStrategy.prototype.exec = function(context, args, invokeNew) {
var contextArgs = [context].concat(
const contextArgs = [context].concat(
args ? Array.prototype.slice.call(args) : []
);
var target = this.plan.bind.apply(this.plan, contextArgs);
const target = this.plan.bind.apply(this.plan, contextArgs);
return invokeNew ? new target() : target();
};
@@ -117,7 +114,7 @@ getJasmineRequireObj().SpyStrategy = function(j$) {
* @param {...*} values - Values to be returned on subsequent calls to the spy.
*/
SpyStrategy.prototype.returnValues = function() {
var values = Array.prototype.slice.call(arguments);
const values = Array.prototype.slice.call(arguments);
this.plan = function() {
return values.shift();
};
@@ -132,7 +129,7 @@ getJasmineRequireObj().SpyStrategy = function(j$) {
* @param {Error|Object|String} something Thing to throw
*/
SpyStrategy.prototype.throwError = function(something) {
var error = j$.isString_(something) ? new Error(something) : something;
const error = j$.isString_(something) ? new Error(something) : something;
this.plan = function() {
throw error;
};

View File

@@ -1,22 +1,22 @@
getJasmineRequireObj().StackTrace = function(j$) {
function StackTrace(error) {
var lines = error.stack.split('\n').filter(function(line) {
let lines = error.stack.split('\n').filter(function(line) {
return line !== '';
});
var extractResult = extractMessage(error.message, lines);
const extractResult = extractMessage(error.message, lines);
if (extractResult) {
this.message = extractResult.message;
lines = extractResult.remainder;
}
var parseResult = tryParseFrames(lines);
const parseResult = tryParseFrames(lines);
this.frames = parseResult.frames;
this.style = parseResult.style;
}
var framePatterns = [
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
@@ -46,16 +46,15 @@ getJasmineRequireObj().StackTrace = function(j$) {
// regexes should capture the function name (if any) as group 1
// and the file, line, and column as group 2.
function tryParseFrames(lines) {
var style = null;
var frames = lines.map(function(line) {
var convertedLine = first(framePatterns, function(pattern) {
var overallMatch = line.match(pattern.re),
fileLineColMatch;
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;
}
fileLineColMatch = overallMatch[pattern.fileLineColIx].match(
const fileLineColMatch = overallMatch[pattern.fileLineColIx].match(
/^(.*):(\d+):\d+$/
);
if (!fileLineColMatch) {
@@ -81,10 +80,8 @@ getJasmineRequireObj().StackTrace = function(j$) {
}
function first(items, fn) {
var i, result;
for (i = 0; i < items.length; i++) {
result = fn(items[i]);
for (const item of items) {
const result = fn(item);
if (result) {
return result;
@@ -93,7 +90,7 @@ getJasmineRequireObj().StackTrace = function(j$) {
}
function extractMessage(message, stackLines) {
var len = messagePrefixLength(message, stackLines);
const len = messagePrefixLength(message, stackLines);
if (len > 0) {
return {
@@ -108,10 +105,9 @@ getJasmineRequireObj().StackTrace = function(j$) {
return 0;
}
var messageLines = message.split('\n');
var i;
const messageLines = message.split('\n');
for (i = 1; i < messageLines.length; i++) {
for (let i = 1; i < messageLines.length; i++) {
if (messageLines[i] !== stackLines[i]) {
return 0;
}

View File

@@ -1,48 +1,23 @@
getJasmineRequireObj().Suite = function(j$) {
/**
* @interface Suite
* @see Env#topSuite
* @since 2.0.0
*/
function Suite(attrs) {
this.env = attrs.env;
/**
* The unique ID of this suite.
* @name Suite#id
* @readonly
* @type {string}
* @since 2.0.0
*/
this.id = attrs.id;
this.parentSuite = attrs.parentSuite;
/**
* The description passed to the {@link describe} that created this suite.
* @name Suite#description
* @readonly
* @type {string}
* @since 2.0.0
*/
this.description = attrs.description;
this.reportedParentSuiteId = attrs.reportedParentSuiteId;
this.filename = attrs.filename;
this.expectationFactory = attrs.expectationFactory;
this.asyncExpectationFactory = attrs.asyncExpectationFactory;
this.expectationResultFactory = attrs.expectationResultFactory;
this.throwOnExpectationFailure = !!attrs.throwOnExpectationFailure;
this.autoCleanClosures =
attrs.autoCleanClosures === undefined ? true : !!attrs.autoCleanClosures;
this.onLateError = attrs.onLateError;
this.onLateError = attrs.onLateError || function() {};
this.beforeFns = [];
this.afterFns = [];
this.beforeAllFns = [];
this.afterAllFns = [];
this.timer = attrs.timer || new j$.Timer();
/**
* The suite's children.
* @name Suite#children
* @type {Array.<(Spec|Suite)>}
* @since 2.0.0
*/
this.children = [];
this.reset();
@@ -61,17 +36,10 @@ getJasmineRequireObj().Suite = function(j$) {
return this.asyncExpectationFactory(actual, this);
};
/**
* The full description including all ancestors of this suite.
* @name Suite#getFullName
* @function
* @returns {string}
* @since 2.0.0
*/
Suite.prototype.getFullName = function() {
var fullName = [];
const fullName = [];
for (
var parentSuite = this;
let parentSuite = this;
parentSuite;
parentSuite = parentSuite.parentSuite
) {
@@ -123,8 +91,8 @@ getJasmineRequireObj().Suite = function(j$) {
};
function removeFns(queueableFns) {
for (var i = 0; i < queueableFns.length; i++) {
queueableFns[i].fn = null;
for (const qf of queueableFns) {
qf.fn = null;
}
}
@@ -140,9 +108,11 @@ getJasmineRequireObj().Suite = function(j$) {
Suite.prototype.reset = function() {
/**
* @typedef SuiteResult
* @property {Int} id - The unique id of this suite.
* @property {String} id - The unique id of this suite.
* @property {String} description - The description text passed to the {@link describe} that made this suite.
* @property {String} fullName - The full description including all ancestors of this suite.
* @property {String|null} parentSuiteId - The ID of the suite containing this suite, or null if this is not in another describe().
* @property {String} filename - The name of the file the suite was defined in.
* @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.
@@ -154,6 +124,8 @@ getJasmineRequireObj().Suite = function(j$) {
id: this.id,
description: this.description,
fullName: this.getFullName(),
parentSuiteId: this.reportedParentSuiteId,
filename: this.filename,
failedExpectations: [],
deprecationWarnings: [],
duration: null,
@@ -163,6 +135,7 @@ getJasmineRequireObj().Suite = function(j$) {
this.children.forEach(function(child) {
child.reset();
});
this.reportedDone = false;
};
Suite.prototype.addChild = function(child) {
@@ -204,25 +177,29 @@ getJasmineRequireObj().Suite = function(j$) {
return j$.UserContext.fromExisting(this.sharedUserContext());
};
Suite.prototype.onException = function() {
Suite.prototype.handleException = function() {
if (arguments[0] instanceof j$.errors.ExpectationFailed) {
return;
}
var data = {
const data = {
matcherName: '',
passed: false,
expected: '',
actual: '',
error: arguments[0]
};
var failedExpectation = this.expectationResultFactory(data);
const failedExpectation = j$.buildExpectationResult(data);
if (!this.parentSuite) {
failedExpectation.globalErrorType = 'afterAll';
}
this.result.failedExpectations.push(failedExpectation);
if (this.reportedDone) {
this.onLateError(failedExpectation);
} else {
this.result.failedExpectations.push(failedExpectation);
}
};
Suite.prototype.onMultipleDone = function() {
@@ -249,8 +226,20 @@ getJasmineRequireObj().Suite = function(j$) {
Suite.prototype.addExpectationResult = function() {
if (isFailure(arguments)) {
var data = arguments[1];
this.result.failedExpectations.push(this.expectationResultFactory(data));
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();
}
@@ -262,7 +251,7 @@ getJasmineRequireObj().Suite = function(j$) {
deprecation = { message: deprecation };
}
this.result.deprecationWarnings.push(
this.expectationResultFactory(deprecation)
j$.buildExpectationResult(deprecation)
);
};
@@ -279,6 +268,7 @@ getJasmineRequireObj().Suite = function(j$) {
/**
* @interface Suite
* @see Env#topSuite
* @since 2.0.0
*/
function SuiteMetadata(suite) {
this.suite_ = suite;
@@ -287,6 +277,7 @@ getJasmineRequireObj().Suite = function(j$) {
* @name Suite#id
* @readonly
* @type {string}
* @since 2.0.0
*/
this.id = suite.id;
@@ -303,6 +294,7 @@ getJasmineRequireObj().Suite = function(j$) {
* @name Suite#description
* @readonly
* @type {string}
* @since 2.0.0
*/
this.description = suite.description;
}
@@ -312,6 +304,7 @@ getJasmineRequireObj().Suite = function(j$) {
* @name Suite#getFullName
* @function
* @returns {string}
* @since 2.0.0
*/
SuiteMetadata.prototype.getFullName = function() {
return this.suite_.getFullName();
@@ -321,6 +314,7 @@ getJasmineRequireObj().Suite = function(j$) {
* The suite's children.
* @name Suite#children
* @type {Array.<(Spec|Suite)>}
* @since 2.0.0
*/
Object.defineProperty(SuiteMetadata.prototype, 'children', {
get: function() {

313
src/core/SuiteBuilder.js Normal file
View File

@@ -0,0 +1,313 @@
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, filename) {
ensureIsFunction(definitionFn, 'describe');
const suite = this.suiteFactory_(description, filename);
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, filename) {
ensureIsFunction(definitionFn, 'fdescribe');
const suite = this.suiteFactory_(description, filename);
suite.isFocused = true;
this.focusedRunables.push(suite.id);
this.unfocusAncestor_();
this.addSpecsToSuite_(suite, definitionFn);
return suite;
}
xdescribe(description, definitionFn, filename) {
ensureIsFunction(definitionFn, 'xdescribe');
const suite = this.suiteFactory_(description, filename);
suite.exclude();
this.addSpecsToSuite_(suite, definitionFn);
return suite;
}
it(description, fn, timeout, filename) {
// 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, filename);
}
xit(description, fn, timeout, filename) {
// 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, filename);
spec.exclude('Temporarily disabled with xit');
return spec;
}
fit(description, fn, timeout, filename) {
// 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, filename);
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, filename) {
if (timeout) {
j$.util.validateTimeout(timeout);
}
const spec = this.specFactory_(description, fn, timeout, filename);
if (this.currentDeclarationSuite_.markedExcluding) {
spec.exclude();
}
this.currentDeclarationSuite_.addChild(spec);
return spec;
}
suiteFactory_(description, filename) {
const config = this.env_.configuration();
const parentSuite = this.currentDeclarationSuite_;
const reportedParentSuiteId =
parentSuite === this.topSuite ? null : parentSuite.id;
return new j$.Suite({
id: 'suite' + this.nextSuiteId_++,
description,
filename,
parentSuite,
reportedParentSuiteId,
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, filename) {
this.totalSpecsDefined++;
const config = this.env_.configuration();
const suite = this.currentDeclarationSuite_;
const parentSuiteId = suite === this.topSuite ? null : suite.id;
const spec = new j$.Spec({
id: 'spec' + this.nextSpecId_++,
filename,
parentSuiteId,
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;
};

View File

@@ -1,5 +1,5 @@
getJasmineRequireObj().Timer = function() {
var defaultNow = (function(Date) {
const defaultNow = (function(Date) {
return function() {
return new Date().getTime();
};
@@ -8,8 +8,8 @@ getJasmineRequireObj().Timer = function() {
function Timer(options) {
options = options || {};
var now = options.now || defaultNow,
startTime;
const now = options.now || defaultNow;
let startTime;
this.start = function() {
startTime = now();

View File

@@ -1,25 +1,25 @@
getJasmineRequireObj().TreeProcessor = function() {
function TreeProcessor(attrs) {
var tree = attrs.tree,
runnableIds = attrs.runnableIds,
queueRunnerFactory = attrs.queueRunnerFactory,
nodeStart = attrs.nodeStart || function() {},
nodeComplete = attrs.nodeComplete || function() {},
failSpecWithNoExpectations = !!attrs.failSpecWithNoExpectations,
orderChildren =
attrs.orderChildren ||
function(node) {
return node.children;
},
excludeNode =
attrs.excludeNode ||
function(node) {
return false;
},
stats = { valid: true },
processed = false,
defaultMin = Infinity,
defaultMax = 1 - Infinity;
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);
@@ -27,7 +27,7 @@ getJasmineRequireObj().TreeProcessor = function() {
return stats;
};
this.execute = function(done) {
this.execute = async function() {
if (!processed) {
this.processTree();
}
@@ -36,23 +36,25 @@ getJasmineRequireObj().TreeProcessor = function() {
throw 'invalid order';
}
var childFns = wrapChildren(tree, 0);
const childFns = wrapChildren(tree, 0);
queueRunnerFactory({
queueableFns: childFns,
userContext: tree.sharedUserContext(),
onException: function() {
tree.onException.apply(tree, arguments);
},
onComplete: done,
onMultipleDone: tree.onMultipleDone
? tree.onMultipleDone.bind(tree)
: null
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 (var i = 0; i < runnableIds.length; i++) {
for (let i = 0; i < runnableIds.length; i++) {
if (runnableIds[i] === id) {
return i;
}
@@ -60,14 +62,14 @@ getJasmineRequireObj().TreeProcessor = function() {
}
function processNode(node, parentExcluded) {
var executableIndex = runnableIndex(node.id);
const executableIndex = runnableIndex(node.id);
if (executableIndex !== undefined) {
parentExcluded = false;
}
if (!node.children) {
var excluded = parentExcluded || excludeNode(node);
const excluded = parentExcluded || excludeNode(node);
stats[node.id] = {
excluded: excluded,
willExecute: !excluded && !node.markedPending,
@@ -82,12 +84,12 @@ getJasmineRequireObj().TreeProcessor = function() {
]
};
} else {
var hasExecutableChild = false;
let hasExecutableChild = false;
var orderedChildren = orderChildren(node);
const orderedChildren = orderChildren(node);
for (var i = 0; i < orderedChildren.length; i++) {
var child = orderedChildren[i];
for (let i = 0; i < orderedChildren.length; i++) {
const child = orderedChildren[i];
processNode(child, parentExcluded);
@@ -95,7 +97,7 @@ getJasmineRequireObj().TreeProcessor = function() {
return;
}
var childStats = stats[child.id];
const childStats = stats[child.id];
hasExecutableChild = hasExecutableChild || childStats.willExecute;
}
@@ -127,7 +129,7 @@ getJasmineRequireObj().TreeProcessor = function() {
nodeStats,
executableIndex
) {
var currentSegment = {
let currentSegment = {
index: 0,
owner: node,
nodes: [],
@@ -146,8 +148,8 @@ getJasmineRequireObj().TreeProcessor = function() {
);
}
for (var i = 0; i < orderedChildSegments.length; i++) {
var childSegment = orderedChildSegments[i],
for (let i = 0; i < orderedChildSegments.length; i++) {
const childSegment = orderedChildSegments[i],
maxIndex = childSegment.max,
minIndex = childSegment.min;
@@ -172,15 +174,15 @@ getJasmineRequireObj().TreeProcessor = function() {
}
function orderChildSegments(children) {
var specifiedOrder = [],
const specifiedOrder = [],
unspecifiedOrder = [];
for (var i = 0; i < children.length; i++) {
var child = children[i],
for (let i = 0; i < children.length; i++) {
const child = children[i],
segments = stats[child.id].segments;
for (var j = 0; j < segments.length; j++) {
var seg = segments[j];
for (let j = 0; j < segments.length; j++) {
const seg = segments[j];
if (seg.min === defaultMin) {
unspecifiedOrder.push(seg);
@@ -201,7 +203,7 @@ getJasmineRequireObj().TreeProcessor = function() {
if (node.children) {
return {
fn: function(done) {
var onStart = {
const onStart = {
fn: function(next) {
nodeStart(node, next);
}
@@ -209,7 +211,7 @@ getJasmineRequireObj().TreeProcessor = function() {
queueRunnerFactory({
onComplete: function() {
var args = Array.prototype.slice.call(arguments, [0]);
const args = Array.prototype.slice.call(arguments, [0]);
node.cleanupBeforeAfter();
nodeComplete(node, node.getResult(), function() {
done.apply(undefined, args);
@@ -218,7 +220,7 @@ getJasmineRequireObj().TreeProcessor = function() {
queueableFns: [onStart].concat(wrapChildren(node, segmentNumber)),
userContext: node.sharedUserContext(),
onException: function() {
node.onException.apply(node, arguments);
node.handleException.apply(node, arguments);
},
onMultipleDone: node.onMultipleDone
? node.onMultipleDone.bind(node)
@@ -230,6 +232,7 @@ getJasmineRequireObj().TreeProcessor = function() {
return {
fn: function(done) {
node.execute(
queueRunnerFactory,
done,
stats[node.id].excluded,
failSpecWithNoExpectations
@@ -240,10 +243,10 @@ getJasmineRequireObj().TreeProcessor = function() {
}
function wrapChildren(node, segmentNumber) {
var result = [],
const result = [],
segmentChildren = stats[node.id].segments[segmentNumber].nodes;
for (var i = 0; i < segmentChildren.length; i++) {
for (let i = 0; i < segmentChildren.length; i++) {
result.push(
executeNode(segmentChildren[i].owner, segmentChildren[i].index)
);

View File

@@ -2,9 +2,9 @@ getJasmineRequireObj().UserContext = function(j$) {
function UserContext() {}
UserContext.fromExisting = function(oldContext) {
var context = new UserContext();
const context = new UserContext();
for (var prop in oldContext) {
for (const prop in oldContext) {
if (oldContext.hasOwnProperty(prop)) {
context[prop] = oldContext[prop];
}

View File

@@ -30,12 +30,9 @@ getJasmineRequireObj().Any = function(j$) {
return typeof other == 'boolean';
}
/* jshint -W122 */
/* global Symbol */
if (typeof Symbol != 'undefined' && this.expectedObject == Symbol) {
return typeof other == 'symbol';
}
/* jshint +W122 */
return other instanceof this.expectedObject;
};

View File

@@ -19,8 +19,7 @@ getJasmineRequireObj().ArrayContaining = function(j$) {
return false;
}
for (var i = 0; i < this.sample.length; i++) {
var item = this.sample[i];
for (const item of this.sample) {
if (!matchersUtil.contains(other, item)) {
return false;
}

View File

@@ -19,8 +19,7 @@ getJasmineRequireObj().ArrayWithExactContents = function(j$) {
return false;
}
for (var i = 0; i < this.sample.length; i++) {
var item = this.sample[i];
for (const item of this.sample) {
if (!matchersUtil.contains(other, item)) {
return false;
}

View File

@@ -0,0 +1,17 @@
getJasmineRequireObj().Is = function(j$) {
class Is {
constructor(expected) {
this.expected_ = expected;
}
asymmetricMatch(actual) {
return actual === this.expected_;
}
jasmineToString(pp) {
return `<jasmine.is(${pp(this.expected_)})>`;
}
}
return Is;
};

View File

@@ -16,7 +16,7 @@ getJasmineRequireObj().MapContaining = function(j$) {
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
var hasMatch = false;
let hasMatch = false;
for (const [oKey, oValue] of other) {
if (
matchersUtil.equals(oKey, key) &&

View File

@@ -27,7 +27,7 @@ getJasmineRequireObj().ObjectContaining = function(j$) {
return false;
}
for (var property in this.sample) {
for (const property in this.sample) {
if (
!hasProperty(other, property) ||
!matchersUtil.equals(this.sample[property], other[property])
@@ -47,7 +47,7 @@ getJasmineRequireObj().ObjectContaining = function(j$) {
};
}
var filteredOther = {};
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.

View File

@@ -17,7 +17,7 @@ getJasmineRequireObj().SetContaining = function(j$) {
// 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)
var hasMatch = false;
let hasMatch = false;
for (const oItem of other) {
if (matchersUtil.equals(oItem, item)) {
hasMatch = true;

View File

@@ -1,8 +1,4 @@
getJasmineRequireObj().base = function(j$, jasmineGlobal) {
j$.unimplementedMethod_ = function() {
throw new Error('unimplemented method');
};
/**
* 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.
@@ -43,7 +39,7 @@ getJasmineRequireObj().base = function(j$, jasmineGlobal) {
* @default 5000
* @since 1.3.0
*/
var DEFAULT_TIMEOUT_INTERVAL = 5000;
let DEFAULT_TIMEOUT_INTERVAL = 5000;
Object.defineProperty(j$, 'DEFAULT_TIMEOUT_INTERVAL', {
get: function() {
return DEFAULT_TIMEOUT_INTERVAL;
@@ -67,7 +63,7 @@ getJasmineRequireObj().base = function(j$, jasmineGlobal) {
* @return {Env}
*/
j$.getEnv = function(options) {
var env = (j$.currentEnv_ = j$.currentEnv_ || new j$.Env(options));
const env = (j$.currentEnv_ = j$.currentEnv_ || new j$.Env(options));
//jasmine. singletons in here (setTimeout blah blah).
return env;
};
@@ -208,7 +204,7 @@ getJasmineRequireObj().base = function(j$, jasmineGlobal) {
return func.name;
}
var matches =
const matches =
func.toString().match(/^\s*function\s*(\w+)\s*\(/) ||
func.toString().match(/^\s*\[object\s*(\w+)Constructor\]/);
@@ -216,7 +212,7 @@ getJasmineRequireObj().base = function(j$, jasmineGlobal) {
};
j$.isPending_ = function(promise) {
var sentinel = {};
const sentinel = {};
return Promise.race([promise, Promise.resolve(sentinel)]).then(
function(result) {
return result === sentinel;
@@ -283,6 +279,18 @@ getJasmineRequireObj().base = function(j$, jasmineGlobal) {
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.
@@ -413,4 +421,47 @@ getJasmineRequireObj().base = function(j$, jasmineGlobal) {
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);
};
};

View File

@@ -1,8 +1,7 @@
//TODO: expectation result may make more sense as a presentation of an expectation.
getJasmineRequireObj().buildExpectationResult = function(j$) {
function buildExpectationResult(options) {
var messageFormatter = options.messageFormatter || function() {},
stackFormatter = options.stackFormatter || function() {};
const exceptionFormatter = new j$.ExceptionFormatter();
/**
* @typedef Expectation
@@ -16,7 +15,7 @@ getJasmineRequireObj().buildExpectationResult = function(j$) {
* is reported on the top suite. Valid values are undefined, "afterAll",
* "load", "lateExpectation", and "lateError".
*/
var result = {
const result = {
matcherName: options.matcherName,
message: message(),
stack: options.omitStackTrace ? '' : stack(),
@@ -52,7 +51,7 @@ getJasmineRequireObj().buildExpectationResult = function(j$) {
} else if (options.message) {
return options.message;
} else if (options.error) {
return messageFormatter(options.error);
return exceptionFormatter.message(options.error);
}
return '';
}
@@ -62,7 +61,8 @@ getJasmineRequireObj().buildExpectationResult = function(j$) {
return '';
}
var error = options.error;
let error = options.error;
if (!error) {
if (options.errorForStack) {
error = options.errorForStack;
@@ -78,7 +78,7 @@ getJasmineRequireObj().buildExpectationResult = function(j$) {
}
// Omit the message from the stack trace because it will be
// included elsewhere.
return stackFormatter(error, { omitMessage: true });
return exceptionFormatter.stack(error, { omitMessage: true });
}
}

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