Compare commits

...

348 Commits

Author SHA1 Message Date
Steve Gravrock
7978ad9889 Fix fn names in release notes 2025-08-30 13:38:28 -07:00
Steve Gravrock
af4662ad31 Bump version to 5.10.0 2025-08-30 13:36:12 -07:00
Steve Gravrock
15c38c7728 Add Firefox 140 (current ESR) to supported browsers and demote 128 to best-effort 2025-08-30 13:31:54 -07:00
Steve Gravrock
b597975c7e Fix and enable pending async expectation message spec 2025-08-30 13:02:08 -07:00
Steve Gravrock
09ce3a30b6 Pend environment-specific specs rather than passing 2025-08-30 12:58:04 -07:00
Steve Gravrock
3bcbc2e3af Tweak spec duration margin 2025-08-30 12:37:15 -07:00
Steve Gravrock
fbaba902dc Merge branch 'bonkevin-html-reporter-with-duration'
* Merges #2073 from @bonkevin
* Adds spec duration to HTML reporter
2025-08-30 12:35:54 -07:00
Steve Gravrock
bf2e8e759e Merge branch 'bonkevin-spec-suite-properties-accessors'
* Merges #2072 from @bonkevin
* Adds Env#getSpecProperty
2025-08-30 12:31:40 -07:00
Steve Gravrock
50e566bd67 Move beforeAll failure reporting into TreeRunner 2025-08-30 07:42:29 -07:00
Steve Gravrock
4b7d5e3623 Update and move remaining disabled Runner specs 2025-08-30 07:21:29 -07:00
Steve Gravrock
6449832e7e rm redundant and long-disabled test for toEqual with DOM nodes 2025-08-29 06:57:32 -07:00
Kevin Bon
c6266b24b7 add test 2025-08-27 10:10:45 -04:00
bonkevin
f16b81d4ef feat: html-reporter with spec duration 2025-08-27 09:58:11 -04:00
kbon
7feec406d9 set the right spec property key 2025-08-25 23:12:29 -04:00
kbon
f822ffea21 feat(getSpecProperty) get a user-defined property 2025-08-25 23:00:06 -04:00
Steve Gravrock
db65c3b131 Fold TreeRunner#runQueueWithSkipPolicy into caller 2025-08-25 18:43:54 -07:00
Steve Gravrock
fd37a7eac0 Move/update/remove obsolste disabled TreeProcessor specs 2025-08-25 18:37:31 -07:00
Steve Gravrock
12219e80c1 Move spec execution from Spec to TreeRunner 2025-08-24 14:05:22 -07:00
Steve Gravrock
a980ae6bf2 Extract spec state management out of Spec#execute 2025-08-24 14:05:22 -07:00
Steve Gravrock
56ac8f5505 Refactor TreeRunner specs 2025-08-24 14:05:13 -07:00
Steve Gravrock
3780fe0b35 Convert some TreeRunner internals to promises 2025-08-24 14:04:47 -07:00
Steve Gravrock
164a393932 Move spec begin and end handling from Env/SuiteBuilder to TreeRunner 2025-08-23 09:03:57 -07:00
Steve Gravrock
759a867094 Backfill unit tests for spec autoCleanClosures 2025-08-23 09:03:57 -07:00
Steve Gravrock
f94d0ceda9 Validate queueableFns 2025-08-23 09:03:38 -07:00
Steve Gravrock
8d99f27be8 Throw an Error rather than a string when createSpyObj is called incorectly 2025-08-23 08:19:11 -07:00
Steve Gravrock
63774597f0 Extract tree running out into a separate class 2025-08-18 16:50:04 -07:00
Steve Gravrock
a3e1abfa12 Split the resulting execution tree out from TreeProcessor 2025-08-17 11:44:52 -07:00
Steve Gravrock
b89a870a59 Test TreeProcessor's public interface, not internal state 2025-08-17 11:44:37 -07:00
Steve Gravrock
ea3fc88803 Remove mutual recursion between Runner and TreeProcessor 2025-08-17 11:44:36 -07:00
Steve Gravrock
d5884e33c6 Move suite execution and spec queueRunner building from TreeProcesor to Runner
This:
* Sets the stage for getting suite and spec execution in one place
* Greatly simplifies the interaction between Runner and TreeProcessor
* Focuses TreeProcessor more on building execution trees
2025-08-17 11:43:00 -07:00
Steve Gravrock
138bf9be4b Focused integration tests for Runner and TreeProcessor
These are mostly adaptations of the execution tests from TreeProcessorSpec.js.
They are meant to support refactoring of the interface and responsibility
division between Runner and TreeProcessor. All these scenarios are probably
covered by nearly-end-to-end integration tests, but those are more difficult
to debug.
2025-08-17 09:23:29 -07:00
Steve Gravrock
98d5284c19 Check for and silence suite reentry warnings in Jasmine's own tests 2025-08-15 06:58:08 -07:00
Steve Gravrock
2299c85751 Deprecate spec/suite orders that interleave suites 2025-08-13 19:02:36 -07:00
Steve Gravrock
8e3ec25f6d Move invalid order exception throw into TreeProcessor 2025-08-13 18:30:51 -07:00
Steve Gravrock
b009cd2922 Convert TreeProcessor to a class 2025-08-12 18:23:57 -07:00
Steve Gravrock
8eee6ebb91 Runner: naming improvements, use private members 2025-08-11 23:22:07 -07:00
Steve Gravrock
c15a1aaa6d Rename queueRunnerFactory to runQueue throughout 2025-08-11 23:05:56 -07:00
Steve Gravrock
5b06531cac Prevent GloablErrors from being monkey patched
All current shipped versions of zone.js contain a monkey patch that fails
to pass constructor arguments on to GlobalErrors. That would crash Jasmine
if it was applied early enough to have any effect.

See <https://github.com/angular/angular/issues/63072>.
2025-08-11 18:08:47 -07:00
Steve Gravrock
42cca93926 Minor jsdoc cleanup 2025-08-09 08:35:49 -07:00
Steve Gravrock
395ef85954 Optionally detect late promise rejections and don't report them as errors 2025-08-09 08:35:08 -07:00
Steve Gravrock
5e88fde655 Backfill some unit tests for Runner's interaction with TreeProcessor 2025-07-29 09:59:45 -07:00
Steve Gravrock
bb777e93e5 Bump version to 5.9.0 2025-07-19 08:27:17 -07:00
Steve Gravrock
9d3fb167a2 Document that the filename property of suite and spec results is deprecated
See <https://github.com/jasmine/jasmine/issues/2065>.
2025-07-19 06:54:54 -07:00
Steve Gravrock
3176eaf1d8 Merge branch 'idConflict' of https://github.com/atscott/jasmine
* Avoid generating timers with IDs that conflict with native
* Fixes #2068
* Merges #2069 from @atscott
2025-07-15 16:50:57 -07:00
Andrew Scott
d31a431d1f fix(clock): Avoid generating timers with IDs that conflict with native
This commit attempts to ensure that the timers created by jasmine mock
clock do not conflict with the native timers. This also retains
pre-existing behavior whereby a native scheduled function cannot be
cleared if it was created prior to the mock clock being installed
(unless the mock clock is uninstalled first).

Prior to this commit, attempting to clear a native timer would result in
clearing a mocked scheduled function instead, in some scenarios where
the IDs conflicted.

fixes #2068
2025-07-14 16:55:05 -07:00
Steve Gravrock
84f78c1435 Split GlobalErrors into portable and platform-specific parts 2025-07-12 13:59:19 -07:00
Steve Gravrock
ff476b1982 Unify error dispatching between browser and node 2025-07-12 13:56:58 -07:00
Steve Gravrock
d53d2ff3eb Convert GlobalErrors to an ES6 class 2025-07-12 13:56:50 -07:00
Steve Gravrock
adfbd00c75 Refactor mocking in GlobalErrorsSpec 2025-07-12 13:56:48 -07:00
Steve Gravrock
495e5fcd50 Backfill integration tests for unhandled promise rejections 2025-07-11 21:36:30 -07:00
Steve Gravrock
bc2aa7be25 Start breaking up integration/EnvSpec.js 2025-07-11 07:39:39 -07:00
Steve Gravrock
af04599bb5 Relaxed timeout on flaky test 2025-07-09 06:56:08 -07:00
Steve Gravrock
21db6ec0e3 Removed unnecessary errorWithStack helper 2025-06-22 12:49:26 -07:00
Steve Gravrock
2d07b3e6d7 Removed protections against user code redefining undefined
Jasmine hasn't even run on platforms that allowed redefining undefined
since 2.x.
2025-06-22 12:23:18 -07:00
Steve Gravrock
6891789ed2 Don't test on Node versions before 18.20.5
18.20.5 is the oldest version supported by current selenium-webdriver.
Also, many dev dependencies require at least 18.18.0.
2025-06-14 10:22:03 -07:00
Steve Gravrock
7a3d3c9360 Removed shelljs dev dependency 2025-06-14 09:05:12 -07:00
Steve Gravrock
1b2922e008 Don't hardcode temp dir in buildStandaloneDist 2025-06-14 09:05:12 -07:00
Steve Gravrock
bd8d23f2a7 Removed rimraf dev dependency 2025-06-14 09:05:06 -07:00
Steve Gravrock
de26763868 CI: remove special case for Chrome 2025-06-09 15:05:50 -07:00
Steve Gravrock
f4be08b657 Bump version to 5.8.0 2025-06-06 17:34:09 -07:00
Steve Gravrock
50ef882a1a Merge branch 'gh1886-spy-args-deep-clone' of https://github.com/evanwalsh/jasmine
Merges #2062 from @evanwaslh
Fixes #1886
2025-06-05 06:54:37 -07:00
Steve Gravrock
c1cd5c6291 Use custom object formatters in spy strategy mismatch errors 2025-06-05 05:46:29 -07:00
Steve Gravrock
63ed2b3948 Include function names in pretty printer output
This helps make matcher errors and spy strategy mismatch errors easier
to understand in cases where the difference involves expecting one
function but getting a different one.
2025-06-04 18:37:44 -07:00
Steve Gravrock
0183acc682 Fix diff building when only one side has a custom object formatter
Fixes #2061
2025-06-04 18:04:40 -07:00
Steve Gravrock
e15819c0dd Test aginast Node 24 2025-05-27 17:32:39 -07:00
Evan Walsh
f694194b2b Allow passing a function to saveArgumentsByValue to customize how arguments are saved
For instance, pass `structuredClone` to do a deep clone.

Fixes https://github.com/jasmine/jasmine/issues/1886
2025-05-27 15:43:21 -04:00
Steve Gravrock
94c00886a6 Merge branch 'setimmedate' of https://github.com/atscott/jasmine
Merges #2058 from @atscott
2025-05-03 10:00:41 -07:00
Steve Gravrock
f5915d7963 Bump version to 5.7.1 2025-05-01 19:31:30 -07:00
Steve Gravrock
15587f3ce3 Merge branch 'autotickuninstall' of https://github.com/atscott/jasmine
Merges #2057 from @atscott
2025-05-01 16:46:30 -07:00
Andrew Scott
3ecddc2555 fixup! fix(Clock): Ensure that uninstalling the clock also stops auto tick 2025-05-01 10:25:24 -07:00
Andrew Scott
6a7c0e6368 perf(clock): use setImmediate for autoTick macrotask in Node
When called within an I/O cycle, `setImmediate` is generally faster because it
is designed to execute immediately after the current I/O event completes,
whereas `setTimeout(0)` gets placed in the timers queue and might be subject to delays.

> The main advantage to using setImmediate() over setTimeout() is setImmediate()
> will always be executed before any timers if scheduled within an I/O cycle,
> independently of how many timers are present.

* https://nodejs.org/en/learn/asynchronous-work/event-loop-timers-and-nexttick#setimmediate-vs-settimeout
* https://nodejs.org/en/learn/asynchronous-work/event-loop-timers-and-nexttick#poll
* https://nodejs.org/en/learn/asynchronous-work/understanding-setimmediate
2025-04-30 14:10:39 -07:00
Andrew Scott
84daa0f5dc fix(Clock): Ensure that uninstalling the clock also stops auto tick
The autotick feature mistakenly does not account for the clock being a
singleton and the re-installation of the clock causes the auto ticking
exit conditions to become true again, before it has a chance to break.
2025-04-30 13:38:10 -07:00
Steve Gravrock
c6b3e947e9 Fixed errors in 5.7.0 release notes 2025-04-29 07:28:36 -07:00
Steve Gravrock
0e604de0db Bump version to 5.7.0 2025-04-26 09:29:07 -07:00
Steve Gravrock
e7ca9c5765 Built distribution 2025-04-26 09:28:21 -07:00
Steve Gravrock
cbff6f95cb Fixed autoTick jsdoc 2025-04-26 08:27:12 -07:00
Steve Gravrock
361640f52e Document that SpecResult#filename and SuiteResult#filename are wrong in some common scenarios
See:

* https://github.com/jasmine/jasmine/issues/2016
* https://github.com/jasmine/jasmine/issues/1884
2025-04-26 07:38:28 -07:00
Steve Gravrock
e5d46e8624 Expose spec path as an array of names
This is in addition to the existing concatenated name. It's meant to
support tools like IDE integrations that want to be able to filter a
run to an exact set of suites/specs.
2025-04-12 09:49:35 -07:00
Steve Gravrock
8f6b3c49cc Removed flaky test
It doesn't look like there's a reliable way to test setTimeout throttling
prevention. The underlying behavior is too nondeterministic. This test
failed at a significant rate in browsers where throttling prevention worked,
simply due to setTimeout taking longer than expected (e.g. 130ms for the
entire test vs an expected <= 5oms). When run in Safari, where setTimeout
throttling prevention doesn't work, it would incorrectly pass if run early
enough in the test order. This is presumably because setTimeout throttling
is influenced by the setTimeout calls made by Jasmine itself prior to
running the test.
2025-04-12 08:28:15 -07:00
Steve Gravrock
df6ab05280 Merge pull request #2056 from jasmine/fix-readme-image
Update README header image
2025-04-11 12:11:06 -07:00
Steve Gravrock
9ea027dbff Update README header image
* Work around GitHub's broken rendering of rawgithub.com images
* Add alt text
* Remove unnecessary docs link
2025-04-11 12:10:25 -07:00
Steve Gravrock
7cc7da4abc Merge branch 'degrunt' 2025-04-10 18:36:21 -07:00
Steve Gravrock
8be98e73ca Use published css-url-embed 2025-04-10 18:27:55 -07:00
Steve Gravrock
52aaf63d22 Create the dist dir if it doesn't already exist 2025-04-10 18:27:55 -07:00
Steve Gravrock
a09fdd3284 Removed remaining use of Grunt 2025-04-09 09:11:19 -07:00
Steve Gravrock
89f3e9449d Use ejs to build SpecRunner.html 2025-04-08 21:53:05 -07:00
Steve Gravrock
ba033c520d Use sass directly rather than grunt-sass 2025-04-08 21:27:43 -07:00
Steve Gravrock
fc935e89c6 Removed grunt-contrib-concat 2025-04-08 21:08:45 -07:00
Steve Gravrock
4bd2feda7d Use archiver directly rather than grunt-contrib-compress
Removes two old copies of glob and is another step towards removing Grunt.
2025-04-08 18:48:58 -07:00
Steve Gravrock
bcf699f145 Switch from grunt-css-url-embed to css-url-embed
This eliminates some rickety indirect dependencies and is a big step
towards removing Grunt.
2025-04-08 18:00:55 -07:00
Steve Gravrock
d4f29491c9 Removed old eslint config file 2025-04-08 07:46:50 -07:00
Steve Gravrock
5b1c932f89 Built distribution 2025-04-07 22:07:52 -07:00
Steve Gravrock
7a8d6e44e3 Fixed sass deprecation warning 2025-04-07 21:59:56 -07:00
Steve Gravrock
dac349397e Updated grunt-sass 2025-04-07 21:59:14 -07:00
Steve Gravrock
5ff7e7f9a1 Updated to eslint 9
This isn't officially compatible with the oldest version of Node that
Jasmine supports, but it works. If it stops working, we can always disable
linting in CI builds on older Node versions.
2025-04-07 21:39:58 -07:00
Steve Gravrock
7b2ab822c6 Removed mostly-unmaintained dev dependency 'temp'
temp has seen some recent maintainer activity but there haven't been
any commits since 2021 and PRs have gone un-addressed for years. It's
one of the dev dependencies that depends on very old versions of rimraf.
2025-04-07 21:35:36 -07:00
Steve Gravrock
033260300a Upgrade shelljs 2025-04-07 21:35:36 -07:00
Steve Gravrock
cb66b54f8b Upgraded jsdom 2025-04-07 21:35:36 -07:00
Steve Gravrock
f4a8102a80 Merge pull request #2055 from atscott/throttleTest
Fix throttling test unit conversion bug and disable test in Node
2025-04-07 21:31:54 -07:00
Andrew Scott
8f539f17b2 refactor(Clock): Fix throttling test unit conversion bug
* fixes the throttling timeout test which incorrectly converted
from milliseconds to seconds before making an assertion expecting
milliseconds.
* reduces number of timeouts from 2000 to 100, which is still more than enough
to observe throttling (or lackthereof).
* Omits Node from the test since it does not throttle timeouts
2025-04-07 08:37:26 -07:00
Steve Gravrock
e5c543a0a1 Updated bug report template
* Don't separately ask for code and steps to reproduce. They're usually
  the same.
* Require steps to reproduce, don't just recommend them
2025-03-21 10:46:45 -07:00
Steve Gravrock
7d697faf95 Merge branch 'atscott-autoTick'
* Merges #2042 from @atscott
* Fixes #1932
* Fixes #1725
2025-03-21 09:21:14 -07:00
Steve Gravrock
6f23151a5e Hardened stop-sauce-connect 2025-03-17 17:33:34 -07:00
Steve Gravrock
e53c7ed8d1 Update to Sauce Connect 5 2025-03-16 14:34:36 -07:00
Andrew Scott
dcd44a0edf feat(Clock): Add ability to automatically tick the clock asynchronously
Testing with mock clocks can often turn into a real struggle when
dealing with situations where some work in the test is truly async and
other work is captured by the mock clock. This can happen for many
reasons, but as one example:

An asynchonrous change from a task in the mocked clock may change DOM where
a resize observer then gets triggered. This browser API is truly asynchronous
and would require the user to wait real time for it to fire. If there is
follow-up work after the resize observer fires, it may be captured by the mock
clock again. This would require the tester to write something like the
following:

```
// flush the timer
jasmine.clock().tick();
// wait for resize observer
await new Promise(resolve => setTimeout(resolve));
// flush follow-up work from the resize observer callback
jasmine.clock().tick();
```

When using mock clocks, testers are always forced to write tests with intimate
knowledge of when the mock clock needs to be ticked. Oftentimes, the
purpose of using a mock clock is to speed up the execution time of the
test when there are timeouts involved. It is not often a goal to test
the exact timeout values. This can cause tests to be riddled with
`tick`. It ideal for test code to be written in a way
that is independent of whether a mock clock is installed. For example:

```
document.getElementById('submit');
// https://testing-library.com/docs/dom-testing-library/api-async/#waitfor
await waitFor(() => expect(mockAPI).toHaveBeenCalledTimes(1))
```

When mock clocks are involved, the above may not be possible if there is
some delay involved between the click and the request to the API.
Instead, developers would need to manually tick the clock beyond the
delay to trigger the API call.

This commit attempts to resolve these issues by adding a feature to the
clock which allows it to advance on its own with the passage of time,
just as clocks do without mocks installed. It also allows for some
breathing time so any unmocked micro and macrotasks are given space to
execute as well.

This feature would also address both #1725 and #1932. `asyncTick` can be
accomplished by enabling the auto tick feature and then waiting for a
promise with a timout to be resolved
(`await new Promise(resolve => setTimeout(resolve, 20))`) where
`setTimeout` is captured by the mock clock and flushed while the code is
waiting for the promise to resolve.

resolves #1725
resolves #1932

All credit goes to @stephenfarrar for this.
2025-03-10 15:31:32 -07:00
Steve Gravrock
f0a5ea9d0f Updated docs for expected and actual properties of expectation results 2025-02-17 12:07:32 -08:00
Steve Gravrock
491b513aa3 Debug logs for rare TypedArray comparison failures in Firefox 2025-02-16 20:30:47 -08:00
Steve Gravrock
d5872bba66 Fixed Safari footnotes in release notes 2025-02-08 11:13:28 -08:00
Steve Gravrock
c4f4edda1b Bump version to 5.6.0 2025-02-08 11:10:06 -08:00
Steve Gravrock
cf057b6631 Fixed parse error from jsdoc
"arguments" isn't a legal argument name in strict mode JS. The JS
runtimes that Jasmine runs in allow it, but jsdoc doesn't.
2025-02-08 10:25:41 -08:00
Steve Gravrock
2a7a157713 toHaveNoOtherSpyInteractions message tweaks 2025-01-20 11:31:47 -08:00
Steve Gravrock
7463fe511b Match messages exactly in toHaveNoOtherSpyInteractions specs 2025-01-20 11:31:18 -08:00
Steve Gravrock
1b724daa10 Merge branch 'Eradev-issue-1991'
* Merges #2051 from @Eradev
* Fixes #1991
2025-01-20 11:31:06 -08:00
Eradev
888d3b6250 Modified error message 2025-01-18 17:55:00 -05:00
Eradev
289afbf0a6 Remove jsdoc from unverifiedCount 2025-01-18 17:25:07 -05:00
Steve Gravrock
9b89bee4f5 Demote Safari to best-effort support 2025-01-18 11:31:17 -08:00
Eradev
3f8f488a58 Fix broken tests 2025-01-11 12:41:28 -05:00
Steve Gravrock
592d47e971 Update copyright date 2025-01-11 08:33:37 -08:00
Eradev
4732012f1c toHaveNoOtherSpyInteractions implementation 2025-01-10 21:05:12 -05:00
Steve Gravrock
7683325d68 Improved specs for async matcher error messages 2024-12-31 10:10:40 -08:00
Steve Gravrock
03d665e243 Merge branch 'improve_toBeRejectedWithError' of https://github.com/andiz2/jasmine 2024-12-31 10:07:53 -08:00
Andrei D
a1591da25d improved error msg on toBeRehectedWithError and all other built-in async matchers 2024-12-22 17:43:47 +02:00
Steve Gravrock
1f1e1209d2 Merge branch 'add_toBeNullish' of https://github.com/MattMcCherry/jasmine
* Merges #2045 from @MattMcCherry
2024-12-12 17:30:55 -08:00
Steve Gravrock
a389905a38 Merge branch 'feature/matcher-toHaveClasses' of https://github.com/aYorky/jasmine
* Merges #2046 from @aYorky
2024-12-11 19:19:58 -08:00
Matt McCherry
36dd6b07d1 add integration test for a falsy value 2024-12-10 11:39:44 +00:00
Matt McCherry
2f3689713b add tests for falsy values 2024-12-10 11:31:16 +00:00
Alex Yorkovich
819fab7b58 simplified match condition 2024-12-07 13:14:25 -06:00
Steve Gravrock
e5ca1f37f1 Use discussions for support requests, not issues 2024-12-07 09:32:57 -08:00
Alex Yorkovich
c3650ea7c7 updated release number 2024-12-04 12:34:36 -06:00
Alex Yorkovich
1805337424 Added new toHaveClasses matcher; tests included 2024-12-04 12:20:15 -06:00
Matt McCherry
27bb6ebac1 reset jasmine.js 2024-12-02 10:34:55 +00:00
Matt McCherry
580323c221 run prettier and fix tests 2024-12-02 10:34:55 +00:00
Matt McCherry
d9286c549f add integration test for toBeNullish 2024-12-02 10:34:55 +00:00
Matt McCherry
26dfa6d257 Add .toBeNullish matcher 2024-12-02 10:34:49 +00:00
Steve Gravrock
483d4ab3c3 Bump version to 5.5.0 2024-11-23 10:58:56 -08:00
Steve Gravrock
663dfe5932 Updated release instructions 2024-11-23 10:42:20 -08:00
Steve Gravrock
ce9c752899 Added debug logging to flaky test 2024-11-12 20:25:20 -08:00
Steve Gravrock
d5e7bc9fd6 Optionally enforce uniqueness of spec and suite names
This is off by default for backwards compatibility but can be enabled
by setting the forbidDuplicateNames env config property to true.

Fixes #1633.
2024-11-10 09:54:51 -08:00
Steve Gravrock
744e765d6f Update copyright date 2024-11-09 13:37:46 -08:00
Steve Gravrock
29551ba4f3 Prettier 2024-11-09 13:17:32 -08:00
Steve Gravrock
bd9a3b2305 Include property value mismatches in diffs even when there are missing or extra properties 2024-11-09 11:22:27 -08:00
Steve Gravrock
c8c3325b56 Bump version to 5.4.0 2024-10-12 10:31:35 -07:00
Steve Gravrock
84c7e2b21b Fixed de-duplication of exception messages containing blank lines on Node and Chrome
This is particularly helpful when reporting testing-library errors, which
have messages that contain blank lines and can be hundreds or even thousands
of lines long.
2024-10-07 20:04:07 -07:00
Steve Gravrock
dda25bb29e Removed references to PhantomJS from StackTraceSpec.js 2024-10-07 19:55:34 -07:00
Steve Gravrock
9ccf2ef96b Also deprecate the expected and actual properties of ThrowUnlessFailure 2024-10-05 13:55:49 -07:00
Steve Gravrock
c6fa55bfc8 Clarify support status of old Firefox ESRs 2024-10-05 13:46:49 -07:00
Steve Gravrock
06bcf1c2e1 Fixed broken docs link 2024-10-05 13:38:52 -07:00
Steve Gravrock
40f402d117 Deprecate the expected and actual properties of expectation results 2024-10-04 07:54:20 -07:00
Steve Gravrock
71f6a95ce5 Added Firefox 128 (current ESR) to supported browsers 2024-09-24 06:54:36 -07:00
Steve Gravrock
5cd7d47f72 Bump version to 5.3.0 2024-09-07 13:18:21 -07:00
Steve Gravrock
d0fe5c4712 Require curly braces around loop and conditonal bodies 2024-09-02 11:30:36 -07:00
Steve Gravrock
f602c4911c Merge branch 'dave-unclamp-safari' of https://github.com/dcsaszar/jasmine
* Significantly improves performance in Safari
* Merges #2040 from @dcsaszar
* Fixes #2008
2024-09-02 11:22:56 -07:00
Steve Gravrock
7aaf7eaf30 API reference for reporter capabilities 2024-09-02 10:53:56 -07:00
David Császár
35f16e8125 Add test for unclamping setTimeout in Safari 2024-09-01 10:22:16 +02:00
David Császár
acc777c267 Unclamp setTimeout in Safari
Fixes #2008

wrapping setTimeout in postMessage is a trade-off between
* slowdown due to postMessage (slow on Safari)
* speedup due to no setTimeout clamping (can be severe on Safari)
2024-09-01 10:21:57 +02:00
David Császár
1c74356691 Add unclampedSetTimeout helper 2024-09-01 09:54:55 +02:00
David Császár
bbebea0fa5 Refactor: extract postMessage 2024-09-01 09:31:52 +02:00
Steve Gravrock
66eb27b0af Throw if spying has no effect
This provides a useful diagnostic in cases where assigning to a property
is a no-op, like localStorage in Firefox and Safari 17.

See #2036 and #2007.
2024-08-17 09:07:18 -07:00
Steve Gravrock
7a63c06a65 Merge branch 'webkit-performance' of https://github.com/m-akinc/jasmine
* Merges #2034 from @m-akinc
2024-08-10 07:26:00 -07:00
Mert Akinc
554dfd4923 Update comments as requested 2024-08-05 12:27:11 -05:00
Mert Akinc
36a6e2aa1d Merge branch 'main' into webkit-performance 2024-08-05 12:19:53 -05:00
Steve Gravrock
3c4b73f136 Fixed globbing in own test suite when running on Windows outside of c:\Users
The previous code used path.join() to construct glob input. That should't
work, but it did as long as the working directory was under c:\Users.
2024-08-03 18:06:20 -07:00
Mert Akinc
bc3ed74336 Formatting fix 2024-07-26 17:54:04 -05:00
Mert Akinc
97b6f33cc2 Add test, update rexex pattern and constant name 2024-07-26 17:45:23 -05:00
Mert Akinc
a9889ddb31 Improve performance on Playwright Windows WebKit 2024-07-25 09:54:32 -05:00
Steve Gravrock
cd1b7ce9c7 Bump version to 5.2.0 2024-07-20 08:44:53 -07:00
Steve Gravrock
3653d6e0ef Moved eslint and prettier configs out of package.json 2024-07-18 07:17:31 -07:00
Steve Gravrock
c3387f8dbf Merge branch 'stephanreiter-better-toHaveSize'
* Merges #2033 from @stephanreiter
2024-07-13 14:02:15 -07:00
Steve Gravrock
3d54184c7f Docs: Improved discoverability of asymmetric equality testers 2024-07-03 10:17:57 -07:00
Stephan Ferlin-Reiter
6f23e706d7 Improve the error message of the toHaveSize matcher.
We include the size of the thing that didn't meet the size expectation.
2024-07-02 20:28:16 +00:00
Steve Gravrock
cc69edf92c Fixed stack trace filtering in FF when the developer tools are open 2024-06-22 11:49:49 -07:00
Steve Gravrock
ba7560f65e HTML reporter: show debug logs with white-space: pre 2024-06-22 11:44:11 -07:00
Steve Gravrock
c3960c4a96 Test against Node 22 2024-06-18 18:13:51 -07:00
Steve Gravrock
5c21f94bb1 4.6.1 release notes 2024-05-25 10:24:11 -07:00
Steve Gravrock
8cd7c94490 Added a jsdoc example for withContext()
Fixes jasmine/jasmine.github.io#166
2024-04-16 16:22:25 -07:00
Steve Gravrock
6a6fa7b29a Tests for existing handling of non-Error global errors in Node 2024-03-22 09:15:22 -07:00
Steve Gravrock
4984548cab Clarify argument to spyOnGlobalErrorsAsync's spy 2024-03-22 08:53:19 -07:00
Steve Gravrock
6941bde7e2 Revert "Deprecate the suppressLoadErrors option"
jasmine-npm still needs this to enable the default behavior of crashing
with a stack trace on load errors.

This reverts commit 99e350ac85.
2024-03-21 09:34:26 -07:00
Steve Gravrock
1504f25ced Report the message when a browser error event with a message but no error occurs 2024-03-21 09:15:43 -07:00
Steve Gravrock
99e350ac85 Deprecate the suppressLoadErrors option
This was intended as a 3.0 migration aid for browser users who had
dependencies that triggered errors at load time. However, it was never
documented and never supported by jasmine-brower-runner, karma, or any
other commonly used tool for runing Jasmine in the browser. There is
no evidence of it actually being used. It is, however, starting to
show up in machine-generated "tutorials".
2024-03-04 19:35:31 -08:00
Steve Gravrock
1624b07589 Clarify spyOnGlobalErrorsAsync API docs 2024-03-04 19:35:24 -08:00
Steve Gravrock
d06dce4614 Bump version to 5.1.2 2024-02-08 17:22:46 -08:00
Steve Gravrock
03098e81f8 Fixed throwUnlessAsync
Fixes #2026
2024-02-05 18:49:19 -08:00
Steve Gravrock
726c152f6e Added Safari 17 to supported browsers 2024-02-05 18:44:11 -08:00
Steve Gravrock
409d2e29e5 Fixed formatting of copyright notice in README 2023-10-14 17:38:30 -07:00
Steve Gravrock
01e2bd5050 Updated copyright notices
The Pivotal copyright notice needs to be retained. That's the right
thing to do and the MIT license requires it. However, using it by itself
becomes more obviously incorrect with each passing year since Pivotal
ceased to exist. Moreover, Pivotal hasn't actually been the sole
copyright owner since the first external contribution was merged.
Contributors were not asked to sign a copyright assignment, so they
retain copyright to their contributions.

"Copyright (c) 2008-2019 Pivotal Labs" complies with the terms of the
license and acknowledges Pivotal's outsized role in developing Jasmine.
"Copyright (c) 2008-$YEAR The Jasmine developers" acknowledges all
authors and will remain correct in the future.
2023-10-14 08:55:48 -07:00
Steve Gravrock
96033e38ea Merge branch 'main' of https://github.com/jd-apprentice/jasmine
* Merges #2017 from @jd-apprentice
2023-10-07 10:34:06 -07:00
Steve Gravrock
ed75290ef7 Removed badges from README
The CI status badge mostly just shows whether Saucelabas was flaky
last night. Code Triage was a nice idea but it's attracted at most
one new contributor over 5.5 years.
2023-09-30 10:22:37 -07:00
Steve Gravrock
a14dbf012a Fix Chrome on CI 2023-09-30 08:33:27 -07:00
Jonathan
17c11ba7b9 fix: updated remaining files 2023-09-21 11:48:52 -03:00
Jonathan
2a1daca1ca chore: rename license file 2023-09-19 14:41:21 -03:00
Steve Gravrock
f0db5ce350 Added Firefox 115 (current ESR) to supported browsers 2023-09-07 21:43:24 -07:00
Steve Gravrock
39f9c2e1a0 Don't attach spec helpers to the env 2023-08-26 11:52:26 -07:00
Steve Gravrock
bff612a169 Bump version to 5.1.1 2023-08-24 19:00:26 -07:00
Steve Gravrock
4ba42f3746 Fixed global variable leak when using ParallelReportDispatcher 2023-08-22 19:34:22 -07:00
Steve Gravrock
58bee05c36 Documented usage of eval in DelayedFunctionScheduler 2023-08-22 19:28:20 -07:00
Steve Gravrock
c1871b0f0c Removed unnecessary throw when building stack trace
Since 4.0, all supported JS runtimes populate the stack property of
Error objects when the Error is instantiated, not when it's thrown.
2023-08-19 09:54:03 -07:00
Steve Gravrock
c16974b091 Improved jsdocs for originalFn argument to createSpy
Fixes jasmine.github.io#137.
2023-08-14 18:32:51 -07:00
Steve Gravrock
bfedda9764 Link to 5.0 upgrade guide in README, not 4.0 2023-08-12 17:26:39 -07:00
Steve Gravrock
a67b7276be Fixed jsdocs for throwUnless and throwUnlessAsync 2023-07-22 09:36:16 -07:00
Steve Gravrock
47f3105ef0 Bump version to 5.1.0 2023-07-22 09:12:36 -07:00
Steve Gravrock
aeb56539c9 Built distribution 2023-07-22 09:12:18 -07:00
Steve Gravrock
75d45efa16 Exclude inherited Error properties from stack trace
These are likely to be methods or other things that aren't meaningful in
Jasmine's output.
2023-07-19 19:13:24 -07:00
Steve Gravrock
59d1c5bebb Use "bug report" tag, not "unconfirmed bug" 2023-07-19 18:29:51 -07:00
Steve Gravrock
040983c979 Merge branch 'skip-non-error-cause' of https://github.com/angrycat9000/jasmine
* Merges #2013 from @angrycat9000
* Fixes #2011
2023-07-19 18:28:56 -07:00
angrycat9000
2ddb344bac Skip parsing cause if it is not an Error object 2023-07-19 10:00:50 -04:00
Steve Gravrock
e56bd3918b Added throwUnless and throwUnlessAsync
These are similar to `expect` and `expectAsync` except that they throw
exceptions rather than recording matcher failures as spec/suite failures.
They're intended to support using Jasmine matchers in testing-library's
`waitFor`, and also provide a way to integration-test custom matchers.

These funtions are not equivalent to `expect` and `expectAsync` and should
not be used in situations where you want a matcher failure to reliably fail
the spec. Whether that happens depends on the structure of the surrounding
code. In general, you should only use `throwUnless` when you expect
something (which could be your own code or library code like `waitFor`) to
catch the resulting exception.

Fixes #2003.
Fixes #1980.
2023-07-15 12:08:11 -07:00
Steve Gravrock
59600a1c29 Removed expect/expectAsync indirection through spec/suite 2023-07-15 12:08:11 -07:00
Steve Gravrock
f8e4ea868f CI: Use a globally-unique Sauce tunnel ID
CIRCLE_BUILD_NUM is only unique per-repo, and we have multiple repos
that can concurrently run Sauce builds.
2023-07-01 11:05:04 -07:00
Steve Gravrock
0ff56c53b1 Dogfood remote Selenium grid support 2023-07-01 10:17:05 -07:00
Steve Gravrock
b617d983de Bump version to 5.0.1 2023-06-09 16:24:00 -07:00
Steve Gravrock
8e0f0e8e8c Optionally restore the pre-5.0 behavior of boot() always creating a new instance
This is needed by jasmine-npm (and likely other tools like it) that may
need to create and use multiple envs in sequence.
2023-06-05 19:44:06 -07:00
Steve Gravrock
d745d6b5f0 Bump version to 5.0.0 2023-05-13 15:14:53 -07:00
Steve Gravrock
fc2c2a477d Updated most dev dependencies
Not updating Prettier because newer versions impose significant formatting
changes. In particular, 2.0 changes every function definition from
`function() {` to `function () {` with no way to opt out. I'm not willing
to accept that kind of churn just becuse the Prettier devs changed their
mind about what color the bikeshed should be.

We'll most likely stay on Prettier 1.17 for as long as it remains viable,
then either switch to an autoformatter that offers stability or just
remove it.
2023-05-13 12:03:45 -07:00
Steve Gravrock
ff93277c0f Accessibility: Always provide a non-color indication that a spec is pending 2023-04-29 11:45:21 -07:00
Steve Gravrock
90741b3cee Accessibility: Improved contrast of version number and inactive tab links 2023-04-29 11:45:16 -07:00
Steve Gravrock
d1de59f0ed Updated jasmine dev dependency 2023-04-29 10:53:03 -07:00
Steve Gravrock
73f8e001ad Bump version to 5.0.0-beta-0 2023-04-29 10:10:05 -07:00
Steve Gravrock
390cc45af2 Dropped support for Node 16
Node 16 will reach EOL no later than a few months after Jasmine 5 is
released. Experience with Node 12 and Node 14 has shown that our
dependencies, especially dev dependencies, move on from past-EOL Node
versions fairly quickly. That can make it difficult to continue supporting
them. Since long term support for past EOL Node versions is a non-goal and
many users expect that Node versions will only be dropped in major
releases, it's better to drop it in 5.0.
2023-04-27 19:32:18 -07:00
Steve Gravrock
33118ac6e2 Merge branch 'main' into 5.0 2023-04-27 19:22:30 -07:00
Steve Gravrock
31ff9a300c Added Node 20 to supported environments 2023-04-22 08:12:49 -07:00
Steve Gravrock
5cc739d879 Bump version to 5.0.0-alpha.1 2023-04-08 13:02:21 -07:00
Steve Gravrock
1e7f07259e API docs for parallel support things that jasmine-npm uses 2023-04-08 12:37:40 -07:00
Steve Gravrock
c36a5cfd96 Parallel: Cleaner interface for reporter dispatching
This gets jasmine-npm out of having to deal with QueueRunner, GlobalErrors,
and ReportDispatcher directly.
2023-04-08 11:41:15 -07:00
Steve Gravrock
299fd1f770 Removed unnecessary TODO 2023-03-25 11:21:04 -07:00
Steve Gravrock
656427d328 Parallel: Disallow calls to Env#config from spec and helper files
Such configuration changes only affect one worker, which is almost certainly
not the intent.
2023-03-25 11:16:54 -07:00
Steve Gravrock
621522fdd4 Updated Glob 2023-03-21 22:35:15 -07:00
Steve Gravrock
6e3589bf52 Updated most dev dependencies 2023-03-21 22:33:24 -07:00
Steve Gravrock
df2d9b282e Bump version to 5.0.0-alpha.0 2023-03-18 11:25:32 -07:00
Steve Gravrock
726d35c5c5 Merge remote-tracking branch 'origin/main' into 5.0 2023-03-15 17:53:08 -07:00
Steve Gravrock
f509078020 Bump version to 4.6.0 2023-03-15 17:21:56 -07:00
Steve Gravrock
8308515210 Ignore the number of CPUs reported by Circle CI 2023-03-12 15:31:28 -07:00
Steve Gravrock
ed838b3cbf Dropped support for Node <16.14
To match jasmine-npm.
2023-03-11 21:19:30 -08:00
Steve Gravrock
04fac300e8 Uninstall the global error at the end of env execution
jasmine-npm needs this so that it can do its own error handling during
globalTeardown.
2023-03-11 17:45:31 -08: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
61505f4c59 Fixed post-merge test failures 2023-03-04 14:10:05 -08:00
Steve Gravrock
86eddb05b4 Merge remote-tracking branch 'origin/main' into 5.0 2023-03-04 14:06:43 -08:00
Steve Gravrock
8af5509581 Added a parallel flag to the jasmineStarted reporter event 2023-03-03 20:49:10 -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
cbc03feb52 Merge branch 'main' into 5.0 2023-02-12 13:08:42 -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
af9a4114f4 Parallel: Improved error messages for top-level before/afterAll 2022-11-25 13:13:05 -08:00
Steve Gravrock
75f97961f5 Dropped support for Safari 14 and Firefox 91 2022-11-24 13:25:39 -08:00
Steve Gravrock
25a7168286 Merge remote-tracking branch 'origin/main' into 5.0 2022-11-24 12:50:39 -08:00
Steve Gravrock
494e81f436 Document that stopOnSpecFailure is best-effort in parallel mode 2022-11-24 12:48:40 -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
ed5e902106 Parallel: Don't allow reporters to be added or removed in worker processes 2022-10-22 09:56:52 -07:00
Steve Gravrock
87f9ab29df Fixed the jsdoc types of SuiteResult and SpecResult ids 2022-10-19 17:20:24 -07:00
Steve Gravrock
47c64a86d5 Parallel: fail if randomization is disabled or a seed is specified 2022-10-12 20:08:42 -07:00
Steve Gravrock
bb497beeff Parallel: throw if Env#topSuite is called 2022-10-11 20:16:36 -07:00
Steve Gravrock
e14d9c4be3 Parallel: forbid beforeAll/afterAll at the top level
Either running these once total or running them once per process
would be the wrong choice for a significant chunk of users, so do
neither. Later we'll add a new API for exactly-once setup and teardown
in parallel mode.
2022-10-11 20:10:02 -07:00
Steve Gravrock
89e0b35c53 Parallel: throw an error if fit/fdescribe are used in parallel mode 2022-10-11 19:35:59 -07:00
Steve Gravrock
1e7b68236b Parallel: forbid beforeEach/afterEach at the top level of spec files
Each spec file is only loaded in a single worker, so top level
before/afterEach can't behave consistently.

beforeEach/afterEach are still supported in:
* Helper files
* describe() blocks
* At the top level of spec files in non-parallel mode
2022-10-11 19:25:39 -07:00
Steve Gravrock
394068f863 Depend on -npm 5.0 2022-10-08 15:39:57 -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
dd98a45003 Use lcoal core when running our own tests in parallel 2022-09-18 19:59:21 -07:00
Steve Gravrock
fe6762b470 Merge branch '5.0' into parallel 2022-09-18 16:42:25 -07:00
Steve Gravrock
fa16b74500 Merge branch 'main' into 5.0 2022-09-18 13:39:05 -07:00
Steve Gravrock
4cd190b232 Merge branch 'internal-async' 2022-09-18 13:31:43 -07:00
Steve Gravrock
6ada55ff77 Parallel: Fixed reporting of exceptions thrown by a describe 2022-09-18 12:10:34 -07:00
Steve Gravrock
735ce6f758 Merge remote-tracking branch 'origin/5.0' into parallel 2022-09-18 09:43:31 -07:00
Steve Gravrock
430324885b Merge branch 'main' into 5.0 2022-09-18 09:41:45 -07:00
Steve Gravrock
7c2e8ce7ca Merge branch 'main' into parallel 2022-09-17 13:26:37 -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
871111424d Use one worker per CPU when running own specs in parallel 2022-09-17 12:20:05 -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
213144413f Test parallel operation in CI 2022-09-17 11:44:24 -07:00
Steve Gravrock
2272f9aead Parallel: run our own specs in parallel 2022-09-17 11:35:03 -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
4c8d57e14c Dropped support for Node 14 2022-08-27 10:47:40 -07:00
Steve Gravrock
543689e206 Depend on -npm from github 2022-08-27 10:46:25 -07:00
Steve Gravrock
ee524831f4 Merge branch 'main' into parallel 2022-08-27 10:30:21 -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
0690500a0d Breaking change: Made Env#execute async
Errors related to invalid spec order are now reported via promise
rejection rather than synchronous throw.
2022-08-21 16:40:03 -07:00
Steve Gravrock
0bfbda720d Breaking change: Env#execute no longer takes a callback
Use the returned promise instead.
2022-08-21 16:35:12 -07:00
Steve Gravrock
4fcdbd39fb Breaking change: use addEventListener rather than setting window.onerror
* Generally simplifies error handling in browsers
* Makes Jasmine's own integration tests easier to debug
* Stack traces will be provided for more global errors
* ... but less error information will be provided in some browsers if the
  error comes from a file:// URL (use `npx serve` or similar instead)
* Jasmine will no longer override existing onerror handlers in browsers
* Setting window.onerror will no longer override Jasmine's global error
  handling (use jasmine.spyOnGlobalErrors instead)
2022-08-21 16:17:18 -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
2e80ec0c22 Rm dead code for QueueRunner deprecations 2022-08-11 19:51:08 -07:00
Steve Gravrock
588283cfe5 Breaking change: support for -npm reporter handling in parallel mode
* The `boot` function exported by the core module returns the same object
  every time it's called.
* Removed node_boot.js. Use the exported `boot` function instead
* JasmineStartedInfo does not have totalSpecsDefined or order in parallel mode
* JasmineDoneInfo does not have order in parallel mode
* Added incompleteCode and numWorkers to JasmineDoneInfo
2022-08-10 18:23:38 -07:00
Steve Gravrock
3a43871901 Reset the env state between parallel batches 2022-08-06 10:55:02 -07:00
Steve Gravrock
fcbab02b2d Droped support for Node 12 2022-08-06 10:55:02 -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
Steve Gravrock
68eaa64c31 Bump version to 4.1.1 2022-05-09 17:49:42 -07:00
Steve Gravrock
81f6eb45ea Cleaned up specs for symbol property filtering
* Removed redundant spec
* Test the behavior of jasmineUnderTest, not the host jasmine
2022-05-09 17:11:36 -07:00
Steve Gravrock
841b212c66 Merge branch 'patch-1' of https://github.com/suke/jasmine
* Merges #1963 from @suke
* Excludes non-enumerable symbol properties from equality comparison
2022-05-09 16:51:47 -07:00
Steve Gravrock
9a27407d35 Folded util.objectDifference into MatchersUtil
This was always an implementation detail of objectKeysAreDifferentFormatter,
and didn't really do what its name suggested.

* #1966
2022-05-07 14:03:26 -07:00
Steve Gravrock
468e9577cd Include symbol properties in matcher diffs
* #1966
2022-05-07 13:26:15 -07:00
Steve Gravrock
9d80377fe3 Fixed exception when comparing arrays with Symbol keys
* Fixes #1966
2022-05-07 10:42:29 -07:00
Steve Gravrock
270344bd38 Include symbol keys when pretty-printing objects
* Fixes #1966
2022-05-07 10:05:18 -07:00
suke
dfa94c70c1 toEqual does not compare symbols that cannot be enumerated 2022-04-27 10:51:03 +09:00
Steve Gravrock
694375e4ea Added Node 18 to CI matrix 2022-04-23 12:39:44 -07:00
Steve Gravrock
1166d10e43 Use const/let in specs, not var 2022-04-16 13:41:44 -07:00
Steve Gravrock
482dc883eb Check for unused vars and params in specs 2022-04-16 10:58:25 -07:00
Steve Gravrock
364cf35474 Fixed shared example for describe-like fns 2022-04-16 10:26:07 -07:00
Steve Gravrock
2e8732f30f Added supported envs to 4.1.0 release notes 2022-04-11 21:08:07 -07:00
289 changed files with 22083 additions and 13360 deletions

View File

@@ -4,21 +4,21 @@
version: 2.1
executors:
node16:
node24:
docker:
- image: cimg/node:16.14.0 # Latest 16.x
- image: cimg/node:24.0.0
working_directory: ~/workspace
node14:
node22:
docker:
- image: cimg/node:14.17.4 # Latest 14.x
- image: cimg/node:22.0.0
working_directory: ~/workspace
node12_latest:
node20:
docker:
- image: cimg/node:12.22.10 # Latest 12.x
- image: cimg/node:20.0.0
working_directory: ~/workspace
node12_17:
node18:
docker:
- image: cimg/node:12.17.0 # Oldest version supported by Jasmine
- image: cimg/node:18.20.5
working_directory: ~/workspace
jobs:
@@ -55,20 +55,34 @@ jobs:
name: Run tests
command: npm test
test_parallel: &test_parallel
parameters:
executor:
type: executor
executor: << parameters.executor >>
steps:
- attach_workspace:
at: .
- run:
name: Run tests in parallel
command: npm run test:parallel
test_browsers: &test_browsers
executor: node14
executor: node18
steps:
- attach_workspace:
at: .
- run:
name: Install Sauce Connect
command: |
cd /tmp
curl https://saucelabs.com/downloads/sc-4.7.1-linux.tar.gz | tar zxf -
chmod +x sc-4.7.1-linux/bin/sc
tmpdir=$(mktemp -d)
cd "$tmpdir"
curl https://saucelabs.com/downloads/sauce-connect/5.2.2/sauce-connect-5.2.2_linux.x86_64.tar.gz | tar zxf -
chmod +x sc
mkdir ~/workspace/bin
cp sc-4.7.1-linux/bin/sc ~/workspace/bin
~/workspace/bin/sc --version
cp sc ~/workspace/bin
echo "Sauce Connect version info:"
~/workspace/bin/sc version
- run:
name: Run tests
command: |
@@ -76,13 +90,13 @@ jobs:
# cleanly if we kill it from a different step than it started in.
export PATH=$PATH:$HOME/workspace/bin
export SAUCE_TUNNEL_IDENTIFIER=$CIRCLE_BUILD_NUM
scripts/start-sauce-connect sauce-pidfile
export SAUCE_TUNNEL_NAME=$CIRCLE_WORKFLOW_JOB_ID
scripts/start-sauce-connect
set +o errexit
scripts/run-all-browsers
exitcode=$?
set -o errexit
scripts/stop-sauce-connect $(cat sauce-pidfile)
scripts/stop-sauce-connect
exit $exitcode
workflows:
@@ -91,40 +105,55 @@ workflows:
push:
jobs:
- build:
executor: node16
name: build_node_16
executor: node24
name: build_node_24
- build:
executor: node14
name: build_node_14
executor: node22
name: build_node_22
- build:
executor: node12_latest
name: build_node_12_latest
executor: node20
name: build_node_20
- build:
executor: node12_17
name: build_node_12_17
executor: node18
name: build_node_18
- test_node:
executor: node16
name: test_node_16
executor: node22
name: test_node_22
requires:
- build_node_16
- build_node_22
- test_node:
executor: node14
name: test_node_14
executor: node20
name: test_node_20
requires:
- build_node_14
- build_node_20
- test_node:
executor: node12_latest
name: test_node_12_latest
executor: node18
name: test_node_18
requires:
- build_node_12_latest
- test_node:
executor: node12_17
name: test_node_12_17
- build_node_18
- test_parallel:
executor: node24
name: test_parallel_node_24
requires:
- build_node_12_17
- build_node_24
- test_parallel:
executor: node22
name: test_parallel_node_22
requires:
- build_node_22
- test_parallel:
executor: node20
name: test_parallel_node_20
requires:
- build_node_20
- test_parallel:
executor: node18
name: test_parallel_node_18
requires:
- build_node_18
- test_browsers:
requires:
- build_node_14
- build_node_18
filters:
branches:
ignore: /pull\/.*/ # Don't run on pull requests.

View File

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

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:

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

@@ -0,0 +1,95 @@
name: Bug Report
description: I think I've found a bug in Jasmine
labels: ["bug report"]
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](https://jasmine.github.io/pages/faq.html#async). 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.
## Explain how to reproduce the bug
**Working steps to reproduce are required for all bug reports.** Please help us help you by creating complete but minimal instructions for reproducing the bug.
The steps to reproduce could be:
* A code snippet that reproduces the problem when run by itself in a newly generated empty `jasmine` or `jasmine-browser-runner` project, or in the standalone distribution.
* A set of steps that reproduce the problem when followed exactly as they're written in an empty directory.
* A link to a Git repository or zip/tar file containing a [minimal, reproducible example](https://stackoverflow.com/help/minimal-reproducible-example). This option is required for all bugs that can only be reproduced with third-party libraries, including Angular and Karma.
Please **test your steps** by starting with an empty directory and following them exactly as they're written. Bug reports with steps to reproduce that are unclear, don't work, or include an unreasonable amount of extraneous code will likely be closed.
- type: textarea
id: steps-to-reproduce
attributes:
label: Steps to Reproduce
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: 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 and/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

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

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

3
.prettierrc Normal file
View File

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

View File

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

View File

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

View File

@@ -1,18 +1,10 @@
<a name="README">[<img src="https://rawgithub.com/jasmine/jasmine/main/images/jasmine-horizontal.svg" width="400px" />](http://jasmine.github.io)</a>
[![Build Status](https://circleci.com/gh/jasmine/jasmine.svg?style=shield)](https://circleci.com/gh/jasmine/jasmine)
[![Open Source Helpers](https://www.codetriage.com/jasmine/jasmine/badges/users.svg)](https://www.codetriage.com/jasmine/jasmine)
[![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 name="README"><img src="https://raw.githubusercontent.com/jasmine/jasmine/main/images/jasmine-horizontal.svg" width="400px" alt="Jasmine"></a>
# 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 4.x? Check out the [upgrade guide](https://jasmine.github.io/tutorials/upgrading_to_Jasmine_5.0).
## Contributing
@@ -28,34 +20,30 @@ 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
Jasmine tests itself across popular browsers (Safari, Chrome, Firefox, and
Microsoft Edge) as well as Node.
| Environment | Supported versions |
|-------------------|--------------------|
| Node | 12.17+, 14, 16 |
| Safari | 14-15 |
| Chrome | Evergreen |
| Firefox | Evergreen, 91 |
| Edge | Evergreen |
| Environment | Supported versions |
|-------------------|----------------------------------|
| Node | 18.20.5+*, 20, 22, 24 |
| Safari | 15*, 16*, 17* |
| Chrome | Evergreen |
| Firefox | Evergreen, 102*, 115*, 128*, 140 |
| 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.
\* Supported on a best-effort basis. Support for these versions may be dropped
if it becomes impractical, and bugs affecting only these versions may not be
treated as release blockers.
## 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 +59,6 @@ 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-2019 Pivotal Labs<br>
Copyright (c) 2008-2025 The Jasmine developers<br>
This software is licensed under the [MIT License](https://github.com/jasmine/jasmine/blob/main/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,46 +34,34 @@ 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
1. Build the standalone distribution with `grunt buildStandaloneDist`
1. Build the standalone distribution with `npm run buildStandaloneDist`
1. This will generate `dist/jasmine-standalone-<version>.zip`, which you will upload later (see "Finally" below).
### 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`
1. `npm login` to save your credentials locally
2. `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 `jasmine` NPM package
### Release the binding libraries
See <https://github.com/jasmine/jasmine-npm/blob/main/RELEASE.md>.
#### NPM
### Publish the GitHub release
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`.)
### 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.

56
eslint.config.mjs Normal file
View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,5 +1,6 @@
/*
Copyright (c) 2008-2022 Pivotal Labs
Copyright (c) 2008-2019 Pivotal Labs
Copyright (c) 2008-2025 The Jasmine developers
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
@@ -20,6 +21,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.
*/
/**
This file starts the process of "booting" Jasmine. It initializes Jasmine,
makes its globals available, and creates the env. This file should be loaded
@@ -27,14 +29,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 +48,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,6 @@
/*
Copyright (c) 2008-2022 Pivotal Labs
Copyright (c) 2008-2019 Pivotal Labs
Copyright (c) 2008-2025 The Jasmine developers
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
@@ -20,6 +21,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.
*/
/**
This file finishes 'booting' Jasmine, performing all of the necessary
initialization before executing the loaded environment and all of a project's
@@ -34,7 +36,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 +44,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 +60,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 +75,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 +105,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 +122,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,6 @@
/*
Copyright (c) 2008-2022 Pivotal Labs
Copyright (c) 2008-2019 Pivotal Labs
Copyright (c) 2008-2025 The Jasmine developers
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
@@ -20,6 +21,8 @@ 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 +82,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 +124,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,15 +150,16 @@ 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') {
// eslint-disable-next-line no-console
console.error(noSpecMsg);
} else {
// eslint-disable-next-line no-console
console.warn(noSpecMsg);
}
}
@@ -194,10 +200,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 +215,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 +237,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 +267,7 @@ jasmineRequire.HtmlReporter = function(j$) {
statusBarClassName += ' jasmine-failed ';
}
var seedBar;
let seedBar;
if (order && order.random) {
seedBar = createDom(
'span',
@@ -286,10 +293,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 +308,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 +326,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 +362,7 @@ jasmineRequire.HtmlReporter = function(j$) {
);
}
var results = find('.jasmine-results');
const results = find('.jasmine-results');
results.appendChild(summary);
summaryList(stateBuilder.topResults, summary);
@@ -397,8 +404,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 +414,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 +458,7 @@ jasmineRequire.HtmlReporter = function(j$) {
}
function debugLogTable(debugLogs) {
var tbody = createDom('tbody');
const tbody = createDom('tbody');
debugLogs.forEach(function(entry) {
tbody.appendChild(
@@ -459,7 +466,11 @@ jasmineRequire.HtmlReporter = function(j$) {
'tr',
{},
createDom('td', {}, entry.timestamp.toString()),
createDom('td', {}, entry.message)
createDom(
'td',
{ className: 'jasmine-debug-log-msg' },
entry.message
)
)
);
});
@@ -491,14 +502,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,18 +534,17 @@ 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;
}
if (
resultNode.result.status === 'pending' &&
resultNode.result.pendingReason !== ''
) {
specDescription =
specDescription +
' PENDING WITH MESSAGE: ' +
resultNode.result.pendingReason;
if (resultNode.result.status === 'pending') {
if (resultNode.result.pendingReason !== '') {
specDescription +=
' PENDING WITH MESSAGE: ' + resultNode.result.pendingReason;
} else {
specDescription += ' PENDING';
}
}
specListNode.appendChild(
createDom(
@@ -547,6 +557,11 @@ jasmineRequire.HtmlReporter = function(j$) {
'a',
{ href: specHref(resultNode.result) },
specDescription
),
createDom(
'span',
{ className: 'jasmine-spec-duration' },
'(' + resultNode.result.duration + 'ms)'
)
)
);
@@ -555,7 +570,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 +636,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 +655,7 @@ jasmineRequire.HtmlReporter = function(j$) {
);
};
var randomCheckbox = optionsMenuDom.querySelector(
const randomCheckbox = optionsMenuDom.querySelector(
'#jasmine-random-order'
);
randomCheckbox.checked = config.random;
@@ -646,13 +663,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 +690,7 @@ jasmineRequire.HtmlReporter = function(j$) {
}
function failureDescription(result, suite) {
var wrapper = createDom(
const wrapper = createDom(
'div',
{ className: 'jasmine-description' },
createDom(
@@ -680,7 +699,7 @@ jasmineRequire.HtmlReporter = function(j$) {
result.description
)
);
var suiteLink;
let suiteLink;
while (suite && suite.parent) {
wrapper.insertBefore(createTextNode(' > '), wrapper.firstChild);
@@ -698,7 +717,7 @@ jasmineRequire.HtmlReporter = function(j$) {
}
function suiteHref(suite) {
var els = [];
const els = [];
while (suite && suite.parent) {
els.unshift(suite.result.description);
@@ -714,8 +733,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 +746,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 +778,7 @@ jasmineRequire.HtmlReporter = function(j$) {
}
function clearPrior() {
// return the reporter
var oldReporter = find('');
const oldReporter = find('');
if (oldReporter) {
getContainer().removeChild(oldReporter);
@@ -768,22 +786,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 +811,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 +823,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 +853,7 @@ jasmineRequire.HtmlReporter = function(j$) {
}
function noExpectations(result) {
var allExpectations =
const allExpectations =
result.failedExpectations.length + result.passedExpectations.length;
return (
@@ -851,7 +868,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 +882,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 +930,7 @@ jasmineRequire.QueryString = function() {
};
this.fullStringWithNewParam = function(key, value) {
var paramMap = queryStringToParamMap();
const paramMap = queryStringToParamMap();
paramMap[key] = value;
return toQueryString(paramMap);
};
@@ -925,8 +942,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 +952,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);
}

View File

@@ -55,9 +55,6 @@ body {
position: fixed;
right: 100%;
}
.jasmine_html-reporter .jasmine-version {
color: #aaa;
}
.jasmine_html-reporter .jasmine-banner {
margin-top: 14px;
}
@@ -169,10 +166,11 @@ body {
}
.jasmine_html-reporter .jasmine-bar.jasmine-menu {
background-color: #fff;
color: #aaa;
color: #000;
}
.jasmine_html-reporter .jasmine-bar.jasmine-menu a {
color: #333;
color: blue;
text-decoration: underline;
}
.jasmine_html-reporter .jasmine-bar a {
color: white;
@@ -231,6 +229,9 @@ body {
.jasmine_html-reporter .jasmine-specs li.jasmine-excluded a:before {
content: "• ";
}
.jasmine_html-reporter .jasmine-specs li .jasmine-spec-duration {
margin-left: 1em;
}
.jasmine_html-reporter .jasmine-description + .jasmine-suite {
margin-top: 0;
}
@@ -297,4 +298,7 @@ body {
}
.jasmine_html-reporter .jasmine-debug-log table, .jasmine_html-reporter .jasmine-debug-log th, .jasmine_html-reporter .jasmine-debug-log td {
border: 1px solid #ddd;
}
.jasmine_html-reporter .jasmine-debug-log .jasmine-debug-log-msg {
white-space: pre;
}

File diff suppressed because it is too large Load Diff

View File

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

View File

@@ -1,7 +1,7 @@
{
"name": "jasmine-core",
"license": "MIT",
"version": "4.1.0",
"version": "5.10.0",
"repository": {
"type": "git",
"url": "https://github.com/jasmine/jasmine.git"
@@ -15,9 +15,11 @@
],
"scripts": {
"posttest": "eslint \"src/**/*.js\" \"spec/**/*.js\" && prettier --check \"src/**/*.js\" \"spec/**/*.js\"",
"test": "grunt --stack execSpecsInNode",
"test": "node scripts/runSpecsInNode.js",
"test:parallel": "node scripts/runSpecsInParallel.js",
"cleanup": "prettier --write \"src/**/*.js\" \"spec/**/*.js\"",
"build": "grunt buildDistribution",
"build": "node scripts/buildDistribution.js",
"buildStandaloneDist": "node scripts/buildStandaloneDist.js",
"serve": "node spec/support/localJasmineBrowser.js",
"serve:performance": "node spec/support/localJasmineBrowser.js jasmine-browser-performance.json",
"ci": "node spec/support/ci.js",
@@ -27,82 +29,32 @@
"homepage": "https://jasmine.github.io",
"main": "./lib/jasmine-core.js",
"files": [
"MIT.LICENSE",
"LICENSE",
"README.md",
"images/*.{png,svg}",
"lib/**/*.{js,css}",
"package.json"
],
"devDependencies": {
"eslint": "^7.32.0",
"eslint-plugin-compat": "^4.0.0",
"glob": "^7.2.0",
"grunt": "^1.0.4",
"grunt-cli": "^1.3.2",
"grunt-contrib-compress": "^2.0.0",
"grunt-contrib-concat": "^2.0.0",
"grunt-css-url-embed": "^1.11.1",
"grunt-sass": "^3.0.2",
"jasmine": "github:jasmine/jasmine-npm#main",
"jasmine-browser-runner": "github:jasmine/jasmine-browser#main",
"jsdom": "^19.0.0",
"load-grunt-tasks": "^5.1.0",
"@eslint/eslintrc": "^3.3.1",
"@eslint/js": "^9.24.0",
"archiver": "^7.0.1",
"css-url-embed": "^0.1.0",
"ejs": "^3.1.10",
"eslint": "^9.24.0",
"eslint-plugin-compat": "^6.0.2",
"glob": "^10.2.3",
"globals": "^16.0.0",
"jasmine": "^5.0.0",
"jasmine-browser-runner": "github:jasmine/jasmine-browser-runner",
"jsdom": "^26.0.0",
"prettier": "1.17.1",
"sass": "^1.45.1",
"shelljs": "^0.8.3",
"temp": "^0.9.0"
},
"prettier": {
"singleQuote": true
},
"eslintConfig": {
"extends": [
"plugin:compat/recommended"
],
"env": {
"browser": true,
"node": true,
"es2017": true
},
"parserOptions": {
"ecmaVersion": 2018
},
"rules": {
"quotes": [
"error",
"single",
{
"avoidEscape": true
}
],
"no-unused-vars": [
"error",
{
"args": "none"
}
],
"no-implicit-globals": "error",
"block-spacing": "error",
"func-call-spacing": [
"error",
"never"
],
"key-spacing": "error",
"no-tabs": "error",
"no-trailing-spaces": "error",
"no-whitespace-before-property": "error",
"semi": [
"error",
"always"
],
"space-before-blocks": "error"
}
"sass": "^1.58.3"
},
"browserslist": [
"Safari >= 13",
"Safari >= 15",
"Firefox >= 102",
"last 2 Chrome versions",
"last 2 Firefox versions",
"Firefox >= 68",
"last 2 Edge versions"
]
}

View File

@@ -33,6 +33,18 @@
* Added links to usage instructions to README
## Supported environments
jasmine-core 4.1.0 has been tested in the following environments.
| Environment | Supported versions |
|-------------------|--------------------|
| Node | 12.17+, 14, 16 |
| Safari | 14-15 |
| Chrome | 100 |
| Firefox | 91, 99 |
| Edge | 100 |
------
_Release Notes generated with _[Anchorman](http://github.com/infews/anchorman)_

35
release_notes/4.1.1.md Normal file
View File

@@ -0,0 +1,35 @@
# Jasmine 4.1.1 Release Notes
## Summary
This release fixes several bugs involving equality comparison of properties
with Symbol keys.
## Bug fixes
* Fixes for crash bugs and output problems when comparing objects with Symbol keys
* Include symbol properties in matcher diffs
* Fixed exception when comparing arrays with Symbol keys
* Include symbol properties in matcher diffs
* Include symbol keys when pretty-printing objects
* Fixes [#1966](https://github.com/jasmine/jasmine/issues/1966)
* Exclude non-enumerable symbol properties from equality comparison
* Merges [#1963](https://github.com/jasmine/jasmine/pull/1963) from @suke
## Supported environments
jasmine-core 4.1.1 has been tested in the following environments.
| Environment | Supported versions |
|-------------------|--------------------|
| Node | 12.17+, 14, 16, 18 |
| Safari | 14, 15 |
| Chrome | 101 |
| Firefox | 91, 100 |
| Edge | 101 |
------
_Release Notes generated with _[Anchorman](http://github.com/infews/anchorman)_

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)_

51
release_notes/4.6.1.md Normal file
View File

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

View File

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

View File

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

View File

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

55
release_notes/5.0.0.md Normal file
View File

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

25
release_notes/5.0.1.md Normal file
View File

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

39
release_notes/5.1.0.md Normal file
View File

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

28
release_notes/5.1.1.md Normal file
View File

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

27
release_notes/5.1.2.md Normal file
View File

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

53
release_notes/5.10.0.md Normal file
View File

@@ -0,0 +1,53 @@
# Jasmine Core 5.10.0 Release Notes
## New Features
* Optionally detect late promise rejections and don't report them as errors
This is off by default because it comes with a performance cost. It can be
enabled by setting the `detectLateRejectionHandling` config property to true.
* Add `getSpecProperty` to retrieve data that was set with `setSpecProperty`.
* Merges [#2072](https://github.com/jasmine/jasmine/pull/2072) from @bonkevin
* Show spec duration in the HTML reporter.
* Merges [#2073](https://github.com/jasmine/jasmine/pull/2073) from @bonkevin
* Protect `GlobalErrors` against monkey patching.
All currently shipped versions of zone.js contain a monkey patch that fails
to pass constructor arguments on to `GlobalErrors`. This patch normally has
no effect because zone.js is normally installed after `GlobalErrors` is
instantiated, but it would crash Jasmine if it was applied early enough.
## Deprecations
* Issue a deprecation warning if the suite/spec order passed as a parameter to
`Env#execute` causes a suite to be re-entered.
## Changes to supported environments
* Added Firefox 140 (current ESR) to supported environments
* Demoted Firefox 128 (previous ESR) to best-effort support
## Internal improvements
* Core suite/spec execution flow has been significantly simplified.
## Supported environments
This version has been tested in the following environments.
| Environment | Supported versions |
|-------------------|--------------------------------|
| Node | 18.20.5**, 20, 22, 24 |
| Safari | 15**, 16**, 17** |
| Chrome | 139* |
| Firefox | 102**, 115**, 128**, 140, 142* |
| Edge | 139* |
\* Evergreen browser. Each version of Jasmine is tested against the latest
version available at release time.<br>
\** Supported on a best-effort basis. Support for these versions may be dropped
if it becomes impractical, and bugs affecting only these versions may not be
treated as release blockers.
------
_Release Notes generated with _[Anchorman](http://github.com/infews/anchorman)_

35
release_notes/5.2.0.md Normal file
View File

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

35
release_notes/5.3.0.md Normal file
View File

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

39
release_notes/5.4.0.md Normal file
View File

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

34
release_notes/5.5.0.md Normal file
View File

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

51
release_notes/5.6.0.md Normal file
View File

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

62
release_notes/5.7.0.md Normal file
View File

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

28
release_notes/5.7.1.md Normal file
View File

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

44
release_notes/5.8.0.md Normal file
View File

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

56
release_notes/5.9.0.md Normal file
View File

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

View File

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

View File

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

View File

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

View File

@@ -3,6 +3,7 @@
run_browser() {
browser=$1
version=$2
os="$3"
description="$browser $version"
if [ $version = "latest" ]; then
version=""
@@ -12,7 +13,7 @@ run_browser() {
echo
echo "Running $description"
echo
USE_SAUCE=true JASMINE_BROWSER=$browser SAUCE_BROWSER_VERSION=$version npm run ci
USE_SAUCE=true JASMINE_BROWSER=$browser SAUCE_BROWSER_VERSION=$version SAUCE_OS="$os" npm run ci
if [ $? -eq 0 ]; then
echo "PASS: $description" >> "$passfile"
@@ -23,11 +24,17 @@ run_browser() {
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 91
run_browser firefox 140
run_browser firefox 128
run_browser firefox 115
run_browser firefox 102
run_browser safari 17
run_browser safari 16
run_browser safari 15
run_browser safari 14
run_browser MicrosoftEdge latest
echo

28
scripts/runSpecsInNode.js Normal file
View File

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

View File

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

View File

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

View File

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

View File

@@ -7,10 +7,13 @@ module.exports = {
semi: 'off',
'key-spacing': 'off',
'space-before-blocks': 'off',
'no-unused-vars': 'off',
'no-trailing-spaces': 'off',
'block-spacing': 'off',
// Additionally, check for unused fn args
// TODO: consider doing this in src files as well as specs
'no-unused-vars': ['error', { args: 'after-used' }],
// Since linting is done at the end of the process and doesn't stop us
// from running tests, it makes sense to fail if debugger statements
// or console references are present.

View File

@@ -7,7 +7,7 @@ describe('AsyncExpectation', function() {
describe('#not', function() {
it('converts a pass to a fail', function() {
var addExpectationResult = jasmine.createSpy('addExpectationResult'),
const addExpectationResult = jasmine.createSpy('addExpectationResult'),
actual = Promise.resolve(),
pp = jasmineUnderTest.makePrettyPrinter(),
expectation = jasmineUnderTest.Expectation.asyncFactory({
@@ -28,8 +28,8 @@ describe('AsyncExpectation', function() {
});
it('converts a fail to a pass', function() {
var addExpectationResult = jasmine.createSpy('addExpectationResult'),
actual = Promise.reject(),
const addExpectationResult = jasmine.createSpy('addExpectationResult'),
actual = Promise.reject(new Error('nope')),
expectation = jasmineUnderTest.Expectation.asyncFactory({
matchersUtil: new jasmineUnderTest.MatchersUtil({
pp: function() {}
@@ -51,9 +51,9 @@ describe('AsyncExpectation', function() {
});
it('propagates rejections from the comparison function', function() {
var error = new Error('ExpectationSpec failure');
const error = new Error('ExpectationSpec failure');
var addExpectationResult = jasmine.createSpy('addExpectationResult'),
const addExpectationResult = jasmine.createSpy('addExpectationResult'),
actual = dummyPromise(),
expectation = jasmineUnderTest.Expectation.asyncFactory({
actual: actual,
@@ -74,7 +74,7 @@ describe('AsyncExpectation', function() {
describe('#withContext', function() {
it('prepends the context to the generated failure message', function() {
var matchersUtil = {
const matchersUtil = {
pp: function(val) {
return val.toString();
}
@@ -101,7 +101,7 @@ describe('AsyncExpectation', function() {
});
it('prepends the context to a custom failure message', function() {
var matchersUtil = {
const matchersUtil = {
buildFailureMessage: function() {
return 'failure message';
},
@@ -129,37 +129,37 @@ describe('AsyncExpectation', function() {
});
});
it('prepends the context to a custom failure message from a function', function() {
pending('should actually work, but no custom matchers for async yet');
var matchersUtil = {
buildFailureMessage: function() {
return 'failure message';
}
it('prepends the context to a custom failure message from a matcher', async function() {
const matchersUtil = {
buildFailureMessage() {
return 'failure message';
},
addExpectationResult = jasmine.createSpy('addExpectationResult'),
actual = Promise.reject(),
expectation = jasmineUnderTest.Expectation.asyncFactory({
actual: actual,
addExpectationResult: addExpectationResult,
matchersUtil: matchersUtil
});
pp(v) {
return v.toString();
}
};
const addExpectationResult = jasmine.createSpy('addExpectationResult');
const actual = Promise.reject(new Error('nope'));
const expectation = jasmineUnderTest.Expectation.asyncFactory({
actual: actual,
addExpectationResult: addExpectationResult,
matchersUtil: matchersUtil
});
return expectation
.withContext('Some context')
.toBeResolved()
.then(function() {
expect(addExpectationResult).toHaveBeenCalledWith(
false,
jasmine.objectContaining({
message: 'Some context: msg'
})
);
});
await expectation.withContext('Some context').toBeResolved();
expect(addExpectationResult).toHaveBeenCalledWith(
false,
jasmine.objectContaining({
message:
'Some context: Expected a promise to be resolved but it ' +
'was rejected with Error: nope.'
})
);
});
it('works with #not', function() {
var addExpectationResult = jasmine.createSpy('addExpectationResult'),
const addExpectationResult = jasmine.createSpy('addExpectationResult'),
actual = Promise.resolve(),
pp = jasmineUnderTest.makePrettyPrinter(),
expectation = jasmineUnderTest.Expectation.asyncFactory({
@@ -183,7 +183,7 @@ describe('AsyncExpectation', function() {
});
it('works with #not and a custom message', function() {
var addExpectationResult = jasmine.createSpy('addExpectationResult'),
const addExpectationResult = jasmine.createSpy('addExpectationResult'),
actual = Promise.resolve('a'),
expectation = jasmineUnderTest.Expectation.asyncFactory({
actual: actual,
@@ -210,22 +210,20 @@ describe('AsyncExpectation', function() {
describe('async matchers', function() {
it('makes custom matchers available to this expectation', function() {
var asyncMatchers = {
const asyncMatchers = {
toFoo: function() {},
toBar: function() {}
},
expectation;
expectation = jasmineUnderTest.Expectation.asyncFactory({
customAsyncMatchers: asyncMatchers
});
expectation = jasmineUnderTest.Expectation.asyncFactory({
customAsyncMatchers: asyncMatchers
});
expect(expectation.toFoo).toBeDefined();
expect(expectation.toBar).toBeDefined();
});
it("wraps matchers's compare functions, passing in matcher dependencies", function() {
var fakeCompare = function() {
const fakeCompare = function() {
return Promise.resolve({ pass: true });
},
matcherFactory = jasmine
@@ -238,14 +236,12 @@ describe('AsyncExpectation', function() {
buildFailureMessage: jasmine.createSpy('buildFailureMessage')
},
addExpectationResult = jasmine.createSpy('addExpectationResult'),
expectation;
expectation = jasmineUnderTest.Expectation.asyncFactory({
matchersUtil: matchersUtil,
customAsyncMatchers: matchers,
actual: 'an actual',
addExpectationResult: addExpectationResult
});
expectation = jasmineUnderTest.Expectation.asyncFactory({
matchersUtil: matchersUtil,
customAsyncMatchers: matchers,
actual: 'an actual',
addExpectationResult: addExpectationResult
});
return expectation.toFoo('hello').then(function() {
expect(matcherFactory).toHaveBeenCalledWith(matchersUtil);
@@ -253,7 +249,7 @@ describe('AsyncExpectation', function() {
});
it("wraps matchers's compare functions, passing the actual and expected", function() {
var fakeCompare = jasmine
const fakeCompare = jasmine
.createSpy('fake-compare')
.and.returnValue(Promise.resolve({ pass: true })),
matchers = {
@@ -267,14 +263,12 @@ describe('AsyncExpectation', function() {
buildFailureMessage: jasmine.createSpy('buildFailureMessage')
},
addExpectationResult = jasmine.createSpy('addExpectationResult'),
expectation;
expectation = jasmineUnderTest.Expectation.asyncFactory({
matchersUtil: matchersUtil,
customAsyncMatchers: matchers,
actual: 'an actual',
addExpectationResult: addExpectationResult
});
expectation = jasmineUnderTest.Expectation.asyncFactory({
matchersUtil: matchersUtil,
customAsyncMatchers: matchers,
actual: 'an actual',
addExpectationResult: addExpectationResult
});
return expectation.toFoo('hello').then(function() {
expect(fakeCompare).toHaveBeenCalledWith('an actual', 'hello');
@@ -282,27 +276,21 @@ describe('AsyncExpectation', function() {
});
it('reports a passing result to the spec when the comparison passes', function() {
var matchers = {
toFoo: function() {
return {
compare: function() {
return Promise.resolve({ pass: true });
}
};
}
},
matchersUtil = {
buildFailureMessage: jasmine.createSpy('buildFailureMessage')
},
addExpectationResult = jasmine.createSpy('addExpectationResult'),
errorWithStack = new Error('errorWithStack'),
expectation;
const matchers = {
toFoo: function() {
return {
compare: function() {
return Promise.resolve({ pass: true });
}
};
}
};
const matchersUtil = {
buildFailureMessage: jasmine.createSpy('buildFailureMessage')
};
const addExpectationResult = jasmine.createSpy('addExpectationResult');
spyOn(jasmineUnderTest.util, 'errorWithStack').and.returnValue(
errorWithStack
);
expectation = jasmineUnderTest.Expectation.asyncFactory({
const expectation = jasmineUnderTest.Expectation.asyncFactory({
customAsyncMatchers: matchers,
matchersUtil: matchersUtil,
actual: 'an actual',
@@ -317,13 +305,13 @@ describe('AsyncExpectation', function() {
error: undefined,
expected: 'hello',
actual: 'an actual',
errorForStack: errorWithStack
errorForStack: jasmine.any(Error)
});
});
});
it('reports a failing result to the spec when the comparison fails', function() {
var matchers = {
const matchers = {
toFoo: function() {
return {
compare: function() {
@@ -336,16 +324,10 @@ describe('AsyncExpectation', function() {
buildFailureMessage: function() {
return '';
}
},
addExpectationResult = jasmine.createSpy('addExpectationResult'),
errorWithStack = new Error('errorWithStack'),
expectation;
};
const addExpectationResult = jasmine.createSpy('addExpectationResult');
spyOn(jasmineUnderTest.util, 'errorWithStack').and.returnValue(
errorWithStack
);
expectation = jasmineUnderTest.Expectation.asyncFactory({
const expectation = jasmineUnderTest.Expectation.asyncFactory({
customAsyncMatchers: matchers,
matchersUtil: matchersUtil,
actual: 'an actual',
@@ -360,33 +342,27 @@ describe('AsyncExpectation', function() {
actual: 'an actual',
message: '',
error: undefined,
errorForStack: errorWithStack
errorForStack: jasmine.any(Error)
});
});
});
it('reports a failing result and a custom fail message to the spec when the comparison fails', function() {
var matchers = {
toFoo: function() {
return {
compare: function() {
return Promise.resolve({
pass: false,
message: 'I am a custom message'
});
}
};
}
},
addExpectationResult = jasmine.createSpy('addExpectationResult'),
errorWithStack = new Error('errorWithStack'),
expectation;
const matchers = {
toFoo: function() {
return {
compare: function() {
return Promise.resolve({
pass: false,
message: 'I am a custom message'
});
}
};
}
};
const addExpectationResult = jasmine.createSpy('addExpectationResult');
spyOn(jasmineUnderTest.util, 'errorWithStack').and.returnValue(
errorWithStack
);
expectation = jasmineUnderTest.Expectation.asyncFactory({
const expectation = jasmineUnderTest.Expectation.asyncFactory({
actual: 'an actual',
customAsyncMatchers: matchers,
addExpectationResult: addExpectationResult
@@ -400,35 +376,29 @@ describe('AsyncExpectation', function() {
actual: 'an actual',
message: 'I am a custom message',
error: undefined,
errorForStack: errorWithStack
errorForStack: jasmine.any(Error)
});
});
});
it('reports a failing result with a custom fail message function to the spec when the comparison fails', function() {
var matchers = {
toFoo: function() {
return {
compare: function() {
return Promise.resolve({
pass: false,
message: function() {
return 'I am a custom message';
}
});
}
};
}
},
addExpectationResult = jasmine.createSpy('addExpectationResult'),
errorWithStack = new Error('errorWithStack'),
expectation;
const matchers = {
toFoo: function() {
return {
compare: function() {
return Promise.resolve({
pass: false,
message: function() {
return 'I am a custom message';
}
});
}
};
}
};
const addExpectationResult = jasmine.createSpy('addExpectationResult');
spyOn(jasmineUnderTest.util, 'errorWithStack').and.returnValue(
errorWithStack
);
expectation = jasmineUnderTest.Expectation.asyncFactory({
const expectation = jasmineUnderTest.Expectation.asyncFactory({
customAsyncMatchers: matchers,
actual: 'an actual',
addExpectationResult: addExpectationResult
@@ -442,31 +412,25 @@ describe('AsyncExpectation', function() {
actual: 'an actual',
message: 'I am a custom message',
error: undefined,
errorForStack: errorWithStack
errorForStack: jasmine.any(Error)
});
});
});
it('reports a passing result to the spec when the comparison fails for a negative expectation', function() {
var matchers = {
toFoo: function() {
return {
compare: function() {
return Promise.resolve({ pass: false });
}
};
}
},
addExpectationResult = jasmine.createSpy('addExpectationResult'),
actual = 'an actual',
errorWithStack = new Error('errorWithStack'),
expectation;
const matchers = {
toFoo: function() {
return {
compare: function() {
return Promise.resolve({ pass: false });
}
};
}
};
const addExpectationResult = jasmine.createSpy('addExpectationResult');
const actual = 'an actual';
spyOn(jasmineUnderTest.util, 'errorWithStack').and.returnValue(
errorWithStack
);
expectation = jasmineUnderTest.Expectation.asyncFactory({
const expectation = jasmineUnderTest.Expectation.asyncFactory({
customAsyncMatchers: matchers,
actual: 'an actual',
addExpectationResult: addExpectationResult
@@ -480,13 +444,13 @@ describe('AsyncExpectation', function() {
error: undefined,
expected: 'hello',
actual: actual,
errorForStack: errorWithStack
errorForStack: jasmine.any(Error)
});
});
});
it('reports a failing result to the spec when the comparison passes for a negative expectation', function() {
var matchers = {
const matchers = {
toFoo: function() {
return {
compare: function() {
@@ -499,17 +463,11 @@ describe('AsyncExpectation', function() {
buildFailureMessage: function() {
return 'default message';
}
},
addExpectationResult = jasmine.createSpy('addExpectationResult'),
actual = 'an actual',
errorWithStack = new Error('errorWithStack'),
expectation;
};
const addExpectationResult = jasmine.createSpy('addExpectationResult');
const actual = 'an actual';
spyOn(jasmineUnderTest.util, 'errorWithStack').and.returnValue(
errorWithStack
);
expectation = jasmineUnderTest.Expectation.asyncFactory({
const expectation = jasmineUnderTest.Expectation.asyncFactory({
customAsyncMatchers: matchers,
actual: 'an actual',
matchersUtil: matchersUtil,
@@ -524,34 +482,28 @@ describe('AsyncExpectation', function() {
actual: actual,
message: 'default message',
error: undefined,
errorForStack: errorWithStack
errorForStack: jasmine.any(Error)
});
});
});
it('reports a failing result and a custom fail message to the spec when the comparison passes for a negative expectation', function() {
var matchers = {
toFoo: function() {
return {
compare: function() {
return Promise.resolve({
pass: true,
message: 'I am a custom message'
});
}
};
}
},
addExpectationResult = jasmine.createSpy('addExpectationResult'),
actual = 'an actual',
errorWithStack = new Error('errorWithStack'),
expectation;
const matchers = {
toFoo: function() {
return {
compare: function() {
return Promise.resolve({
pass: true,
message: 'I am a custom message'
});
}
};
}
};
const addExpectationResult = jasmine.createSpy('addExpectationResult');
const actual = 'an actual';
spyOn(jasmineUnderTest.util, 'errorWithStack').and.returnValue(
errorWithStack
);
expectation = jasmineUnderTest.Expectation.asyncFactory({
const expectation = jasmineUnderTest.Expectation.asyncFactory({
customAsyncMatchers: matchers,
actual: 'an actual',
addExpectationResult: addExpectationResult
@@ -565,34 +517,28 @@ describe('AsyncExpectation', function() {
actual: actual,
message: 'I am a custom message',
error: undefined,
errorForStack: errorWithStack
errorForStack: jasmine.any(Error)
});
});
});
it("reports a passing result to the spec when the 'not' comparison passes, given a negativeCompare", function() {
var matchers = {
toFoo: function() {
return {
compare: function() {
return Promise.resolve({ pass: true });
},
negativeCompare: function() {
return Promise.resolve({ pass: true });
}
};
}
},
addExpectationResult = jasmine.createSpy('addExpectationResult'),
actual = 'an actual',
errorWithStack = new Error('errorWithStack'),
expectation;
const matchers = {
toFoo: function() {
return {
compare: function() {
return Promise.resolve({ pass: true });
},
negativeCompare: function() {
return Promise.resolve({ pass: true });
}
};
}
};
const addExpectationResult = jasmine.createSpy('addExpectationResult');
const actual = 'an actual';
spyOn(jasmineUnderTest.util, 'errorWithStack').and.returnValue(
errorWithStack
);
expectation = jasmineUnderTest.Expectation.asyncFactory({
const expectation = jasmineUnderTest.Expectation.asyncFactory({
customAsyncMatchers: matchers,
actual: 'an actual',
addExpectationResult: addExpectationResult
@@ -606,37 +552,31 @@ describe('AsyncExpectation', function() {
actual: actual,
message: '',
error: undefined,
errorForStack: errorWithStack
errorForStack: jasmine.any(Error)
});
});
});
it("reports a failing result and a custom fail message to the spec when the 'not' comparison fails, given a negativeCompare", function() {
var matchers = {
toFoo: function() {
return {
compare: function() {
return Promise.resolve({ pass: true });
},
negativeCompare: function() {
return Promise.resolve({
pass: false,
message: "I'm a custom message"
});
}
};
}
},
addExpectationResult = jasmine.createSpy('addExpectationResult'),
actual = 'an actual',
errorWithStack = new Error('errorWithStack'),
expectation;
const matchers = {
toFoo: function() {
return {
compare: function() {
return Promise.resolve({ pass: true });
},
negativeCompare: function() {
return Promise.resolve({
pass: false,
message: "I'm a custom message"
});
}
};
}
};
const addExpectationResult = jasmine.createSpy('addExpectationResult');
const actual = 'an actual';
spyOn(jasmineUnderTest.util, 'errorWithStack').and.returnValue(
errorWithStack
);
expectation = jasmineUnderTest.Expectation.asyncFactory({
const expectation = jasmineUnderTest.Expectation.asyncFactory({
customAsyncMatchers: matchers,
actual: 'an actual',
addExpectationResult: addExpectationResult
@@ -650,35 +590,29 @@ describe('AsyncExpectation', function() {
actual: actual,
message: "I'm a custom message",
error: undefined,
errorForStack: errorWithStack
errorForStack: jasmine.any(Error)
});
});
});
it('reports errorWithStack when a custom error message is returned', function() {
var customError = new Error('I am a custom error');
var matchers = {
toFoo: function() {
return {
compare: function() {
return Promise.resolve({
pass: false,
message: 'I am a custom message',
error: customError
});
}
};
}
},
addExpectationResult = jasmine.createSpy('addExpectationResult'),
errorWithStack = new Error('errorWithStack'),
expectation;
const customError = new Error('I am a custom error');
const matchers = {
toFoo: function() {
return {
compare: function() {
return Promise.resolve({
pass: false,
message: 'I am a custom message',
error: customError
});
}
};
}
};
const addExpectationResult = jasmine.createSpy('addExpectationResult');
spyOn(jasmineUnderTest.util, 'errorWithStack').and.returnValue(
errorWithStack
);
expectation = jasmineUnderTest.Expectation.asyncFactory({
const expectation = jasmineUnderTest.Expectation.asyncFactory({
actual: 'an actual',
customAsyncMatchers: matchers,
addExpectationResult: addExpectationResult
@@ -692,33 +626,27 @@ describe('AsyncExpectation', function() {
actual: 'an actual',
message: 'I am a custom message',
error: undefined,
errorForStack: errorWithStack
errorForStack: jasmine.any(Error)
});
});
});
it("reports a custom message to the spec when a 'not' comparison fails", function() {
var matchers = {
toFoo: function() {
return {
compare: function() {
return Promise.resolve({
pass: true,
message: 'I am a custom message'
});
}
};
}
},
addExpectationResult = jasmine.createSpy('addExpectationResult'),
errorWithStack = new Error('errorWithStack'),
expectation;
const matchers = {
toFoo: function() {
return {
compare: function() {
return Promise.resolve({
pass: true,
message: 'I am a custom message'
});
}
};
}
};
const addExpectationResult = jasmine.createSpy('addExpectationResult');
spyOn(jasmineUnderTest.util, 'errorWithStack').and.returnValue(
errorWithStack
);
expectation = jasmineUnderTest.Expectation.asyncFactory({
const expectation = jasmineUnderTest.Expectation.asyncFactory({
actual: 'an actual',
customAsyncMatchers: matchers,
addExpectationResult: addExpectationResult
@@ -732,35 +660,29 @@ describe('AsyncExpectation', function() {
actual: 'an actual',
message: 'I am a custom message',
error: undefined,
errorForStack: errorWithStack
errorForStack: jasmine.any(Error)
});
});
});
it("reports a custom message func to the spec when a 'not' comparison fails", function() {
var matchers = {
toFoo: function() {
return {
compare: function() {
return Promise.resolve({
pass: true,
message: function() {
return 'I am a custom message';
}
});
}
};
}
},
addExpectationResult = jasmine.createSpy('addExpectationResult'),
errorWithStack = new Error('errorWithStack'),
expectation;
const matchers = {
toFoo: function() {
return {
compare: function() {
return Promise.resolve({
pass: true,
message: function() {
return 'I am a custom message';
}
});
}
};
}
};
const addExpectationResult = jasmine.createSpy('addExpectationResult');
spyOn(jasmineUnderTest.util, 'errorWithStack').and.returnValue(
errorWithStack
);
expectation = jasmineUnderTest.Expectation.asyncFactory({
let expectation = jasmineUnderTest.Expectation.asyncFactory({
actual: 'an actual',
customAsyncMatchers: matchers,
addExpectationResult: addExpectationResult
@@ -774,13 +696,13 @@ describe('AsyncExpectation', function() {
actual: 'an actual',
message: 'I am a custom message',
error: undefined,
errorForStack: errorWithStack
errorForStack: jasmine.any(Error)
});
});
});
});
function dummyPromise() {
return new Promise(function(resolve, reject) {});
return new Promise(function() {});
}
});

View File

@@ -1,6 +1,6 @@
describe('CallTracker', function() {
it('tracks that it was called when executed', function() {
var callTracker = new jasmineUnderTest.CallTracker();
const callTracker = new jasmineUnderTest.CallTracker();
expect(callTracker.any()).toBe(false);
@@ -10,7 +10,7 @@ describe('CallTracker', function() {
});
it('tracks that number of times that it is executed', function() {
var callTracker = new jasmineUnderTest.CallTracker();
const callTracker = new jasmineUnderTest.CallTracker();
expect(callTracker.count()).toEqual(0);
@@ -20,7 +20,7 @@ describe('CallTracker', function() {
});
it('tracks the params from each execution', function() {
var callTracker = new jasmineUnderTest.CallTracker();
const callTracker = new jasmineUnderTest.CallTracker();
callTracker.track({ object: void 0, args: [] });
callTracker.track({ object: {}, args: [0, 'foo'] });
@@ -31,9 +31,9 @@ describe('CallTracker', function() {
});
it("tracks the 'this' object from each execution", function() {
var callTracker = new jasmineUnderTest.CallTracker();
const callTracker = new jasmineUnderTest.CallTracker();
var this0 = {},
const this0 = {},
this1 = {};
callTracker.track({ object: this0, args: [] });
callTracker.track({ object: this1, args: [] });
@@ -45,13 +45,13 @@ describe('CallTracker', function() {
});
it('returns any empty array when there was no call', function() {
var callTracker = new jasmineUnderTest.CallTracker();
const callTracker = new jasmineUnderTest.CallTracker();
expect(callTracker.argsFor(0)).toEqual([]);
});
it('allows access for the arguments for all calls', function() {
var callTracker = new jasmineUnderTest.CallTracker();
const callTracker = new jasmineUnderTest.CallTracker();
callTracker.track({ object: {}, args: [] });
callTracker.track({ object: {}, args: [0, 'foo'] });
@@ -60,7 +60,7 @@ describe('CallTracker', function() {
});
it('tracks the context and arguments for each call', function() {
var callTracker = new jasmineUnderTest.CallTracker();
const callTracker = new jasmineUnderTest.CallTracker();
callTracker.track({ object: {}, args: [] });
callTracker.track({ object: {}, args: [0, 'foo'] });
@@ -71,7 +71,7 @@ describe('CallTracker', function() {
});
it('simplifies access to the arguments for the last (most recent) call', function() {
var callTracker = new jasmineUnderTest.CallTracker();
const callTracker = new jasmineUnderTest.CallTracker();
callTracker.track();
callTracker.track({ object: {}, args: [0, 'foo'] });
@@ -83,13 +83,13 @@ describe('CallTracker', function() {
});
it("returns a useful falsy value when there isn't a last (most recent) call", function() {
var callTracker = new jasmineUnderTest.CallTracker();
const callTracker = new jasmineUnderTest.CallTracker();
expect(callTracker.mostRecent()).toBeFalsy();
});
it('simplifies access to the arguments for the first (oldest) call', function() {
var callTracker = new jasmineUnderTest.CallTracker();
const callTracker = new jasmineUnderTest.CallTracker();
callTracker.track({ object: {}, args: [0, 'foo'] });
@@ -97,13 +97,13 @@ describe('CallTracker', function() {
});
it("returns a useful falsy value when there isn't a first (oldest) call", function() {
var callTracker = new jasmineUnderTest.CallTracker();
const callTracker = new jasmineUnderTest.CallTracker();
expect(callTracker.first()).toBeFalsy();
});
it('allows the tracking to be reset', function() {
var callTracker = new jasmineUnderTest.CallTracker();
const callTracker = new jasmineUnderTest.CallTracker();
callTracker.track();
callTracker.track({ object: {}, args: [0, 'foo'] });
@@ -117,10 +117,10 @@ describe('CallTracker', function() {
});
it('allows object arguments to be shallow cloned', function() {
var callTracker = new jasmineUnderTest.CallTracker();
const callTracker = new jasmineUnderTest.CallTracker();
callTracker.saveArgumentsByValue();
var objectArg = { foo: 'bar' },
const objectArg = { foo: 'bar' },
arrayArg = ['foo', 'bar'];
callTracker.track({
@@ -134,8 +134,44 @@ describe('CallTracker', function() {
expect(callTracker.mostRecent().args[1]).toEqual(arrayArg);
});
it('allows object arguments to be deep cloned', function() {
const callTracker = new jasmineUnderTest.CallTracker();
callTracker.saveArgumentsByValue(args => JSON.parse(JSON.stringify(args)));
const objectArg = { foo: { bar: { baz: ['qux'] } } },
arrayArg = ['foo', 'bar'];
callTracker.track({
object: {},
args: [objectArg, arrayArg, false, undefined, null, NaN, '', 0, 1.0]
});
objectArg.foo.bar.baz.push('quux');
expect(callTracker.mostRecent().args[0]).not.toBe(objectArg);
expect(callTracker.mostRecent().args[0]).not.toEqual(objectArg);
expect(callTracker.mostRecent().args[0]).toEqual({
foo: { bar: { baz: ['qux'] } }
});
expect(callTracker.mostRecent().args[1]).not.toBe(arrayArg);
expect(callTracker.mostRecent().args[1]).toEqual(arrayArg);
});
it('can take any function to transform arguments when saving by value', function() {
const callTracker = new jasmineUnderTest.CallTracker();
callTracker.saveArgumentsByValue(JSON.stringify);
const objectArg = { foo: { bar: { baz: ['qux'] } } },
arrayArg = ['foo', 'bar'],
args = [objectArg, arrayArg, false, undefined, null, NaN, '', 0, 1.0];
callTracker.track({ object: {}, args });
expect(callTracker.mostRecent().args).toEqual(JSON.stringify(args));
});
it('saves primitive arguments by value', function() {
var callTracker = new jasmineUnderTest.CallTracker(),
const callTracker = new jasmineUnderTest.CallTracker(),
args = [undefined, null, false, '', /\s/, 0, 1.2, NaN];
callTracker.saveArgumentsByValue();

View File

@@ -1,6 +1,6 @@
describe('ClearStack', function() {
it('works in an integrationy way', function(done) {
var clearStack = jasmineUnderTest.getClearStack(
const clearStack = jasmineUnderTest.getClearStack(
jasmineUnderTest.getGlobal()
);
@@ -9,158 +9,257 @@ describe('ClearStack', function() {
});
});
it('uses setImmediate when available', function() {
var setImmediate = jasmine
.createSpy('setImmediate')
.and.callFake(function(fn) {
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
};
});
it('uses MessageChannel to reduce setTimeout clamping', function() {
const fakeChannel = fakeMessageChannel();
spyOn(fakeChannel.port2, 'postMessage');
const queueMicrotask = jasmine.createSpy('queueMicrotask');
const global = {
navigator: {
userAgent:
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit/605.0.8 (KHTML, like Gecko) Version/15.1 Safari/605.0.8'
},
MessageChannel: function() {
return fakeChannel;
},
queueMicrotask
};
const clearStack = jasmineUnderTest.getClearStack(global);
for (let i = 0; i < 9; i++) {
clearStack(function() {});
}
expect(fakeChannel.port2.postMessage).not.toHaveBeenCalled();
clearStack(function() {});
expect(fakeChannel.port2.postMessage).toHaveBeenCalledTimes(1);
});
});
describe("in WebKit (Playwright's build for Windows)", function() {
usesQueueMicrotaskWithSetTimeout(function() {
return {
navigator: {
userAgent:
'Mozilla/5.0 (Windows NT 6.2; Win64; x64) AppleWebKit/605.1.15 (KHTML, like Gecko)'
},
// queueMicrotask should be used even though MessageChannel is present
MessageChannel: fakeMessageChannel
};
});
});
describe('in browsers other than Safari', function() {
usesMessageChannel(function() {
return {
navigator: {
// Chrome's user agent string contains "Safari" so it's a good test case
userAgent:
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.0.0 Safari/537.36'
}
};
});
describe('when MessageChannel is unavailable', function() {
usesQueueMicrotaskWithSetTimeout(function() {
return {
navigator: {
userAgent: 'CERN-LineMode/2.15 libwww/2.17b3',
MessageChannel: undefined
}
};
});
});
});
describe('in Node', function() {
usesQueueMicrotaskWithoutSetTimeout(function() {
return {
process: {
versions: {
node: '3.1415927'
}
}
};
});
});
function usesMessageChannel(makeGlobal) {
it('uses MessageChannel', function() {
const global = {
...makeGlobal(),
MessageChannel: fakeMessageChannel
};
const clearStack = jasmineUnderTest.getClearStack(global);
let called = false;
clearStack(function() {
called = true;
});
expect(called).toBe(true);
});
it('uses setTimeout instead of MessageChannel every 10 calls to make sure we release the CPU', function() {
const fakeChannel = fakeMessageChannel();
spyOn(fakeChannel.port2, 'postMessage');
const setTimeout = jasmine.createSpy('setTimeout');
const global = {
...makeGlobal(),
setTimeout,
MessageChannel: function() {
return fakeChannel;
}
};
const clearStack = jasmineUnderTest.getClearStack(global);
for (let i = 0; i < 9; i++) {
clearStack(function() {});
}
expect(fakeChannel.port2.postMessage).toHaveBeenCalled();
expect(setTimeout).not.toHaveBeenCalled();
clearStack(function() {});
expect(fakeChannel.port2.postMessage).toHaveBeenCalledTimes(9);
expect(setTimeout).toHaveBeenCalledTimes(1);
clearStack(function() {});
expect(fakeChannel.port2.postMessage).toHaveBeenCalledTimes(10);
expect(setTimeout).toHaveBeenCalledTimes(1);
});
it('calls setTimeout when onmessage is called recursively', function() {
const setTimeout = jasmine.createSpy('setTimeout');
const global = {
...makeGlobal(),
setTimeout,
MessageChannel: fakeMessageChannel
};
const clearStack = jasmineUnderTest.getClearStack(global);
const fn = jasmine.createSpy('second clearStack function');
clearStack(function() {
clearStack(fn);
});
expect(fn).not.toHaveBeenCalled();
expect(setTimeout).toHaveBeenCalledWith(fn, 0);
});
}
function usesQueueMicrotaskWithSetTimeout(makeGlobal) {
it('uses queueMicrotask', function() {
const global = {
...makeGlobal(),
queueMicrotask: function(fn) {
fn();
}),
global = { setImmediate: setImmediate },
clearStack = jasmineUnderTest.getClearStack(global),
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(setImmediate).toHaveBeenCalled();
});
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);
it('uses setTimeout instead of setImmediate every 10 calls to make sure we release the CPU', function() {
var setImmediate = jasmine.createSpy('setImmediate'),
setTimeout = jasmine.createSpy('setTimeout'),
global = { setImmediate: setImmediate, setTimeout: setTimeout },
clearStack = jasmineUnderTest.getClearStack(global);
for (let i = 0; i < 9; i++) {
clearStack(function() {});
}
clearStack(function() {});
clearStack(function() {});
clearStack(function() {});
clearStack(function() {});
clearStack(function() {});
clearStack(function() {});
clearStack(function() {});
clearStack(function() {});
clearStack(function() {});
expect(queueMicrotask).toHaveBeenCalled();
expect(setTimeout).not.toHaveBeenCalled();
expect(setImmediate).toHaveBeenCalled();
expect(setTimeout).not.toHaveBeenCalled();
clearStack(function() {});
expect(queueMicrotask).toHaveBeenCalledTimes(9);
expect(setTimeout).toHaveBeenCalledTimes(1);
clearStack(function() {});
expect(setImmediate.calls.count()).toEqual(9);
expect(setTimeout.calls.count()).toEqual(1);
clearStack(function() {});
expect(queueMicrotask).toHaveBeenCalledTimes(10);
expect(setTimeout).toHaveBeenCalledTimes(1);
});
}
clearStack(function() {});
expect(setImmediate.calls.count()).toEqual(10);
expect(setTimeout.calls.count()).toEqual(1);
});
it('uses MessageChannels when available', function() {
var fakeChannel = {
port1: {},
port2: {
postMessage: function() {
fakeChannel.port1.onmessage();
}
function usesQueueMicrotaskWithoutSetTimeout(makeGlobal) {
it('uses queueMicrotask', function() {
const global = {
...makeGlobal(),
queueMicrotask: function(fn) {
fn();
}
},
global = {
MessageChannel: function() {
return fakeChannel;
}
},
clearStack = jasmineUnderTest.getClearStack(global),
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);
});
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);
it('uses setTimeout instead of MessageChannel every 10 calls to make sure we release the CPU', function() {
var fakeChannel = {
port1: {},
port2: {
postMessage: jasmine
.createSpy('postMessage')
.and.callFake(function() {
fakeChannel.port1.onmessage();
})
}
},
setTimeout = jasmine.createSpy('setTimeout'),
global = {
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() {});
clearStack(function() {});
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() {
var 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');
clearStack(function() {
clearStack(fn);
expect(queueMicrotask).toHaveBeenCalledTimes(10);
expect(setTimeout).not.toHaveBeenCalled();
});
}
expect(fn).not.toHaveBeenCalled();
expect(setTimeout).toHaveBeenCalledWith(fn, 0);
});
it('falls back to setTimeout', function() {
var setTimeout = jasmine.createSpy('setTimeout').and.callFake(function(fn) {
fn();
}),
global = { setTimeout: setTimeout },
clearStack = jasmineUnderTest.getClearStack(global),
called = false;
clearStack(function() {
called = true;
});
expect(called).toBe(true);
expect(setTimeout).toHaveBeenCalledWith(jasmine.any(Function), 0);
});
function fakeMessageChannel() {
const channel = {
port1: {},
port2: {
postMessage: function() {
channel.port1.onmessage();
}
}
};
return channel;
}
});

View File

@@ -1,11 +1,11 @@
describe('Clock', function() {
var NODE_JS =
const NODE_JS =
typeof process !== 'undefined' &&
process.versions &&
typeof process.versions.node === 'string';
it('does not replace setTimeout until it is installed', function() {
var fakeSetTimeout = jasmine.createSpy('global setTimeout'),
const fakeSetTimeout = jasmine.createSpy('global setTimeout'),
fakeGlobal = { setTimeout: fakeSetTimeout },
delayedFunctionScheduler = jasmine.createSpyObj(
'delayedFunctionScheduler',
@@ -40,7 +40,7 @@ describe('Clock', function() {
});
it('does not replace clearTimeout until it is installed', function() {
var fakeClearTimeout = jasmine.createSpy('global cleartimeout'),
const fakeClearTimeout = jasmine.createSpy('global cleartimeout'),
fakeGlobal = { clearTimeout: fakeClearTimeout },
delayedFunctionScheduler = jasmine.createSpyObj(
'delayedFunctionScheduler',
@@ -76,7 +76,7 @@ describe('Clock', function() {
});
it('does not replace setInterval until it is installed', function() {
var fakeSetInterval = jasmine.createSpy('global setInterval'),
const fakeSetInterval = jasmine.createSpy('global setInterval'),
fakeGlobal = { setInterval: fakeSetInterval },
delayedFunctionScheduler = jasmine.createSpyObj(
'delayedFunctionScheduler',
@@ -111,7 +111,7 @@ describe('Clock', function() {
});
it('does not replace clearInterval until it is installed', function() {
var fakeClearInterval = jasmine.createSpy('global clearinterval'),
const fakeClearInterval = jasmine.createSpy('global clearinterval'),
fakeGlobal = { clearInterval: fakeClearInterval },
delayedFunctionScheduler = jasmine.createSpyObj(
'delayedFunctionScheduler',
@@ -147,7 +147,7 @@ describe('Clock', function() {
});
it('does not install if the current setTimeout is not the original function on the global', function() {
var originalFakeSetTimeout = function() {},
const originalFakeSetTimeout = function() {},
replacedSetTimeout = function() {},
fakeGlobal = { setTimeout: originalFakeSetTimeout },
delayedFunctionSchedulerFactory = jasmine.createSpy(
@@ -171,7 +171,7 @@ describe('Clock', function() {
});
it('does not install if the current clearTimeout is not the original function on the global', function() {
var originalFakeClearTimeout = function() {},
const originalFakeClearTimeout = function() {},
replacedClearTimeout = function() {},
fakeGlobal = { clearTimeout: originalFakeClearTimeout },
delayedFunctionSchedulerFactory = jasmine.createSpy(
@@ -195,7 +195,7 @@ describe('Clock', function() {
});
it('does not install if the current setInterval is not the original function on the global', function() {
var originalFakeSetInterval = function() {},
const originalFakeSetInterval = function() {},
replacedSetInterval = function() {},
fakeGlobal = { setInterval: originalFakeSetInterval },
delayedFunctionSchedulerFactory = jasmine.createSpy(
@@ -219,7 +219,7 @@ describe('Clock', function() {
});
it('does not install if the current clearInterval is not the original function on the global', function() {
var originalFakeClearInterval = function() {},
const originalFakeClearInterval = function() {},
replacedClearInterval = function() {},
fakeGlobal = { clearInterval: originalFakeClearInterval },
delayedFunctionSchedulerFactory = jasmine.createSpy(
@@ -243,7 +243,7 @@ describe('Clock', function() {
});
it('replaces the global timer functions on uninstall', function() {
var fakeSetTimeout = jasmine.createSpy('global setTimeout'),
const fakeSetTimeout = jasmine.createSpy('global setTimeout'),
fakeClearTimeout = jasmine.createSpy('global clearTimeout'),
fakeSetInterval = jasmine.createSpy('global setInterval'),
fakeClearInterval = jasmine.createSpy('global clearInterval'),
@@ -286,7 +286,7 @@ describe('Clock', function() {
});
it('can be installed for the duration of a passed in function and uninstalled when done', function() {
var fakeSetTimeout = jasmine.createSpy('global setTimeout'),
const fakeSetTimeout = jasmine.createSpy('global setTimeout'),
fakeClearTimeout = jasmine.createSpy('global clearTimeout'),
fakeSetInterval = jasmine.createSpy('global setInterval'),
fakeClearInterval = jasmine.createSpy('global clearInterval'),
@@ -312,8 +312,8 @@ describe('Clock', function() {
return delayedFunctionScheduler;
},
mockDate
),
passedFunctionCalled = false;
);
let passedFunctionCalled = false;
clock.withMock(function() {
fakeGlobal.setTimeout(delayedFn, 0);
@@ -346,7 +346,7 @@ describe('Clock', function() {
});
it('can be installed for the duration of a passed in function and uninstalled if an error is thrown', function() {
var fakeSetTimeout = jasmine.createSpy('global setTimeout'),
const fakeSetTimeout = jasmine.createSpy('global setTimeout'),
fakeClearTimeout = jasmine.createSpy('global clearTimeout'),
fakeSetInterval = jasmine.createSpy('global setInterval'),
fakeClearInterval = jasmine.createSpy('global clearInterval'),
@@ -372,8 +372,8 @@ describe('Clock', function() {
return delayedFunctionScheduler;
},
mockDate
),
passedFunctionCalled = false;
);
let passedFunctionCalled = false;
expect(function() {
clock.withMock(function() {
@@ -409,7 +409,7 @@ describe('Clock', function() {
});
it('schedules the delayed function (via setTimeout) with the fake timer', function() {
var fakeSetTimeout = jasmine.createSpy('setTimeout'),
const fakeSetTimeout = jasmine.createSpy('setTimeout'),
scheduleFunction = jasmine.createSpy('scheduleFunction'),
delayedFunctionScheduler = { scheduleFunction: scheduleFunction },
fakeGlobal = { setTimeout: fakeSetTimeout },
@@ -451,7 +451,7 @@ describe('Clock', function() {
});
it('returns an id for the delayed function', function() {
var fakeSetTimeout = jasmine.createSpy('setTimeout'),
const fakeSetTimeout = jasmine.createSpy('setTimeout'),
scheduleId = 123,
scheduleFunction = jasmine
.createSpy('scheduleFunction')
@@ -470,11 +470,10 @@ describe('Clock', function() {
return delayedFunctionScheduler;
},
mockDate
),
timeout;
);
clock.install();
timeout = clock.setTimeout(delayedFn, 0);
const timeout = clock.setTimeout(delayedFn, 0);
if (!NODE_JS) {
expect(timeout).toEqual(123);
@@ -484,7 +483,7 @@ describe('Clock', function() {
});
it('clears the scheduled function with the scheduler', function() {
var fakeClearTimeout = jasmine.createSpy('clearTimeout'),
const fakeClearTimeout = jasmine.createSpy('clearTimeout'),
delayedFunctionScheduler = jasmine.createSpyObj(
'delayedFunctionScheduler',
['removeFunctionWithId']
@@ -513,7 +512,7 @@ describe('Clock', function() {
});
it('schedules the delayed function with the fake timer', function() {
var fakeSetInterval = jasmine.createSpy('setInterval'),
const fakeSetInterval = jasmine.createSpy('setInterval'),
scheduleFunction = jasmine.createSpy('scheduleFunction'),
delayedFunctionScheduler = { scheduleFunction: scheduleFunction },
fakeGlobal = { setInterval: fakeSetInterval },
@@ -556,7 +555,7 @@ describe('Clock', function() {
});
it('returns an id for the delayed function', function() {
var fakeSetInterval = jasmine.createSpy('setInterval'),
const fakeSetInterval = jasmine.createSpy('setInterval'),
scheduleId = 123,
scheduleFunction = jasmine
.createSpy('scheduleFunction')
@@ -575,11 +574,10 @@ describe('Clock', function() {
return delayedFunctionScheduler;
},
mockDate
),
interval;
);
clock.install();
interval = clock.setInterval(delayedFn, 0);
const interval = clock.setInterval(delayedFn, 0);
if (!NODE_JS) {
expect(interval).toEqual(123);
@@ -589,7 +587,7 @@ describe('Clock', function() {
});
it('clears the scheduled function with the scheduler', function() {
var clearInterval = jasmine.createSpy('clearInterval'),
const clearInterval = jasmine.createSpy('clearInterval'),
delayedFunctionScheduler = jasmine.createSpyObj(
'delayedFunctionScheduler',
['removeFunctionWithId']
@@ -618,7 +616,7 @@ describe('Clock', function() {
});
it('gives you a friendly reminder if the Clock is not installed and you tick', function() {
var clock = new jasmineUnderTest.Clock(
const clock = new jasmineUnderTest.Clock(
{},
jasmine.createSpyObj('delayedFunctionScheduler', ['tick'])
);
@@ -630,7 +628,7 @@ describe('Clock', function() {
describe('Clock (acceptance)', function() {
it('can run setTimeouts/setIntervals synchronously', function() {
var delayedFn1 = jasmine.createSpy('delayedFn1'),
const delayedFn1 = jasmine.createSpy('delayedFn1'),
delayedFn2 = jasmine.createSpy('delayedFn2'),
delayedFn3 = jasmine.createSpy('delayedFn3'),
recurring1 = jasmine.createSpy('recurring1'),
@@ -651,7 +649,7 @@ describe('Clock (acceptance)', function() {
clock.install();
clock.setTimeout(delayedFn1, 0);
var intervalId = clock.setInterval(recurring1, 50);
const intervalId = clock.setInterval(recurring1, 50);
clock.setTimeout(delayedFn2, 100);
clock.setTimeout(delayedFn3, 200);
@@ -689,8 +687,161 @@ describe('Clock (acceptance)', function() {
expect(recurring1.calls.count()).toBe(4);
});
describe('auto tick mode', () => {
let delayedFunctionScheduler;
let mockDate;
let clock;
beforeEach(() => {
delayedFunctionScheduler = new jasmineUnderTest.DelayedFunctionScheduler();
mockDate = {
install: function() {},
tick: function() {},
uninstall: function() {}
};
// window setTimeout to window to make firefox happy
const _setTimeout =
typeof window !== 'undefined' ? setTimeout.bind(window) : setTimeout;
// passing a fake global allows us to preserve the real timing functions for use in tests
const _global = { setTimeout: _setTimeout, setInterval: setInterval };
clock = new jasmineUnderTest.Clock(
_global,
function() {
return delayedFunctionScheduler;
},
mockDate
);
clock.install().autoTick();
});
afterEach(() => {
clock.uninstall();
});
it('flushes microtask queue between macrotasks', async () => {
const log = [];
await new Promise(r => clock.setTimeout(r, 10)).then(() => {
log.push(1);
Promise.resolve().then(() => log.push(2));
Promise.resolve().then(() => log.push(3));
});
await new Promise(r => clock.setTimeout(r, 10)).then(() => {
log.push(4);
Promise.resolve().then(() => log.push(5));
});
expect(log).toEqual([1, 2, 3, 4, 5]);
});
it('can run setTimeouts/setIntervals asynchronously', function() {
const recurring = jasmine.createSpy('recurring'),
fn1 = jasmine.createSpy('fn1'),
fn2 = jasmine.createSpy('fn2'),
fn3 = jasmine.createSpy('fn3');
const intervalId = clock.setInterval(recurring, 50);
// In a microtask, add some timeouts.
Promise.resolve()
.then(function() {
return new Promise(function(resolve) {
clock.setTimeout(resolve, 25);
});
})
.then(function() {
fn1();
return new Promise(function(resolve) {
clock.setTimeout(resolve, 200);
});
})
.then(function() {
fn2();
return new Promise(function(resolve) {
clock.setTimeout(resolve, 100);
});
})
.then(function() {
fn3();
});
expect(recurring).not.toHaveBeenCalled();
expect(fn1).not.toHaveBeenCalled();
expect(fn2).not.toHaveBeenCalled();
expect(fn3).not.toHaveBeenCalled();
return new Promise(resolve => clock.setTimeout(resolve, 50))
.then(function() {
expect(recurring).toHaveBeenCalledTimes(1);
expect(fn1).toHaveBeenCalled();
expect(fn2).not.toHaveBeenCalled();
expect(fn3).not.toHaveBeenCalled();
return new Promise(resolve => clock.setTimeout(resolve, 175));
})
.then(function() {
expect(recurring).toHaveBeenCalledTimes(4);
expect(fn1).toHaveBeenCalled();
expect(fn2).toHaveBeenCalled();
expect(fn3).not.toHaveBeenCalled();
clock.clearInterval(intervalId);
return new Promise(resolve => clock.setTimeout(resolve, 100));
})
.then(function() {
expect(recurring).toHaveBeenCalledTimes(4);
expect(fn1).toHaveBeenCalled();
expect(fn2).toHaveBeenCalled();
expect(fn3).toHaveBeenCalled();
});
});
it('aborts auto ticking when uninstalled, even if installed again synchonrously', async () => {
clock.uninstall();
clock.install();
let resolved = false;
const promise = new Promise(resolve => {
clock.setTimeout(resolve, 1);
}).then(() => {
resolved = true;
});
// wait some real time and verify that the clock did not flush the timer above automatically
await new Promise(resolve => setTimeout(resolve, 2));
expect(resolved).toBe(false);
// enabling auto tick again will flush the timer
clock.autoTick();
await expectAsync(promise).toBeResolved();
});
it('speeds up the execution of the timers in all browsers', async () => {
const startTimeMs = performance.now() / 1000;
await new Promise(resolve => clock.setTimeout(resolve, 5000));
await new Promise(resolve => clock.setTimeout(resolve, 5000));
await new Promise(resolve => clock.setTimeout(resolve, 5000));
await new Promise(resolve => clock.setTimeout(resolve, 5000));
const endTimeMs = performance.now() / 1000;
// Ensure we didn't take 20s to complete the awaits above and, in fact, can do it in a fraction of a second
expect(endTimeMs - startTimeMs).toBeLessThan(100);
});
it('is easy to test async functions with interleaved timers and microtasks', async () => {
async function blackBoxWithLotsOfAsyncStuff() {
await new Promise(r => clock.setTimeout(r, 10));
await Promise.resolve();
await Promise.resolve();
await new Promise(r => clock.setTimeout(r, 20));
await Promise.resolve();
await Promise.resolve();
await Promise.resolve();
return 'done';
}
const result = await blackBoxWithLotsOfAsyncStuff();
expect(result).toBe('done');
});
});
it('can clear a previously set timeout', function() {
var clearedFn = jasmine.createSpy('clearedFn'),
const clearedFn = jasmine.createSpy('clearedFn'),
delayedFunctionScheduler = new jasmineUnderTest.DelayedFunctionScheduler(),
mockDate = {
install: function() {},
@@ -703,12 +854,11 @@ describe('Clock (acceptance)', function() {
return delayedFunctionScheduler;
},
mockDate
),
timeoutId;
);
clock.install();
timeoutId = clock.setTimeout(clearedFn, 100);
const timeoutId = clock.setTimeout(clearedFn, 100);
expect(clearedFn).not.toHaveBeenCalled();
clock.clearTimeout(timeoutId);
@@ -718,7 +868,7 @@ describe('Clock (acceptance)', function() {
});
it("can clear a previously set interval using that interval's handler", function() {
var spy = jasmine.createSpy('spy'),
const spy = jasmine.createSpy('spy'),
delayedFunctionScheduler = new jasmineUnderTest.DelayedFunctionScheduler(),
mockDate = {
install: function() {},
@@ -731,12 +881,11 @@ describe('Clock (acceptance)', function() {
return delayedFunctionScheduler;
},
mockDate
),
intervalId;
);
clock.install();
intervalId = clock.setInterval(function() {
const intervalId = clock.setInterval(function() {
spy();
clock.clearInterval(intervalId);
}, 100);
@@ -746,7 +895,7 @@ describe('Clock (acceptance)', function() {
});
it('correctly schedules functions after the Clock has advanced', function() {
var delayedFn1 = jasmine.createSpy('delayedFn1'),
const delayedFn1 = jasmine.createSpy('delayedFn1'),
delayedFunctionScheduler = new jasmineUnderTest.DelayedFunctionScheduler(),
mockDate = {
install: function() {},
@@ -772,7 +921,7 @@ describe('Clock (acceptance)', function() {
});
it('correctly schedules functions while the Clock is advancing', function() {
var delayedFn1 = jasmine.createSpy('delayedFn1'),
const delayedFn1 = jasmine.createSpy('delayedFn1'),
delayedFn2 = jasmine.createSpy('delayedFn2'),
delayedFunctionScheduler = new jasmineUnderTest.DelayedFunctionScheduler(),
mockDate = {
@@ -803,7 +952,7 @@ describe('Clock (acceptance)', function() {
});
it('correctly calls functions scheduled while the Clock is advancing', function() {
var delayedFn1 = jasmine.createSpy('delayedFn1'),
const delayedFn1 = jasmine.createSpy('delayedFn1'),
delayedFn2 = jasmine.createSpy('delayedFn2'),
delayedFunctionScheduler = new jasmineUnderTest.DelayedFunctionScheduler(),
mockDate = {
@@ -831,7 +980,7 @@ describe('Clock (acceptance)', function() {
});
it('correctly schedules functions scheduled while the Clock is advancing but after the Clock is uninstalled', function() {
var delayedFn1 = jasmine.createSpy('delayedFn1'),
const delayedFn1 = jasmine.createSpy('delayedFn1'),
delayedFn2 = jasmine.createSpy('delayedFn2'),
delayedFunctionScheduler = new jasmineUnderTest.DelayedFunctionScheduler(),
mockDate = {
@@ -865,7 +1014,7 @@ describe('Clock (acceptance)', function() {
});
it('does not mock the Date object by default', function() {
var delayedFunctionScheduler = new jasmineUnderTest.DelayedFunctionScheduler(),
const delayedFunctionScheduler = new jasmineUnderTest.DelayedFunctionScheduler(),
global = { Date: Date },
mockDate = new jasmineUnderTest.MockDate(global),
clock = new jasmineUnderTest.Clock(
@@ -880,7 +1029,7 @@ describe('Clock (acceptance)', function() {
expect(global.Date).toEqual(Date);
var now = new global.Date().getTime();
const now = new global.Date().getTime();
clock.tick(50);
@@ -888,7 +1037,7 @@ describe('Clock (acceptance)', function() {
});
it('mocks the Date object and sets it to current time', function() {
var delayedFunctionScheduler = new jasmineUnderTest.DelayedFunctionScheduler(),
const delayedFunctionScheduler = new jasmineUnderTest.DelayedFunctionScheduler(),
global = { Date: Date },
mockDate = new jasmineUnderTest.MockDate(global),
clock = new jasmineUnderTest.Clock(
@@ -901,13 +1050,13 @@ describe('Clock (acceptance)', function() {
clock.install().mockDate();
var now = new global.Date().getTime();
const now = new global.Date().getTime();
clock.tick(50);
expect(new global.Date().getTime() - now).toEqual(50);
var timeoutDate = 0;
let timeoutDate = 0;
clock.setTimeout(function() {
timeoutDate = new global.Date().getTime();
}, 100);
@@ -918,7 +1067,7 @@ describe('Clock (acceptance)', function() {
});
it('mocks the Date object and sets it to a given time', function() {
var delayedFunctionScheduler = new jasmineUnderTest.DelayedFunctionScheduler(),
const delayedFunctionScheduler = new jasmineUnderTest.DelayedFunctionScheduler(),
global = { Date: Date },
mockDate = new jasmineUnderTest.MockDate(global),
clock = new jasmineUnderTest.Clock(
@@ -932,7 +1081,7 @@ describe('Clock (acceptance)', function() {
clock.install().mockDate(baseTime);
var now = new global.Date().getTime();
const now = new global.Date().getTime();
expect(now).toEqual(baseTime.getTime());
@@ -940,7 +1089,7 @@ describe('Clock (acceptance)', function() {
expect(new global.Date().getTime()).toEqual(baseTime.getTime() + 50);
var timeoutDate = 0;
let timeoutDate = 0;
clock.setTimeout(function() {
timeoutDate = new global.Date().getTime();
}, 100);
@@ -951,7 +1100,7 @@ describe('Clock (acceptance)', function() {
});
it('throws mockDate is called with a non-Date', function() {
var delayedFunctionScheduler = new jasmineUnderTest.DelayedFunctionScheduler(),
const delayedFunctionScheduler = new jasmineUnderTest.DelayedFunctionScheduler(),
global = { Date: Date },
mockDate = new jasmineUnderTest.MockDate(global),
clock = new jasmineUnderTest.Clock(
@@ -960,8 +1109,7 @@ describe('Clock (acceptance)', function() {
return delayedFunctionScheduler;
},
mockDate
),
env = jasmineUnderTest.getEnv();
);
expect(() => clock.mockDate(12345)).toThrowError(
'The argument to jasmine.clock().mockDate(), if specified, should be ' +
@@ -970,7 +1118,7 @@ describe('Clock (acceptance)', function() {
});
it('mocks the Date object and updates the date per delayed function', function() {
var delayedFunctionScheduler = new jasmineUnderTest.DelayedFunctionScheduler(),
const delayedFunctionScheduler = new jasmineUnderTest.DelayedFunctionScheduler(),
global = { Date: Date },
mockDate = new jasmineUnderTest.MockDate(global),
clock = new jasmineUnderTest.Clock(
@@ -984,8 +1132,8 @@ describe('Clock (acceptance)', function() {
clock.install().mockDate(baseTime);
var actualTimes = [];
var pushCurrentTime = function() {
const actualTimes = [];
const pushCurrentTime = function() {
actualTimes.push(global.Date().getTime());
};
delayedFunctionScheduler.scheduleFunction(pushCurrentTime);
@@ -1009,7 +1157,7 @@ describe('Clock (acceptance)', function() {
});
it('correctly clears a scheduled timeout while the Clock is advancing', function() {
var delayedFunctionScheduler = new jasmineUnderTest.DelayedFunctionScheduler(),
const delayedFunctionScheduler = new jasmineUnderTest.DelayedFunctionScheduler(),
global = { Date: Date, setTimeout: undefined },
mockDate = new jasmineUnderTest.MockDate(global),
clock = new jasmineUnderTest.Clock(
@@ -1022,7 +1170,7 @@ describe('Clock (acceptance)', function() {
clock.install();
var timerId2;
let timerId2;
global.setTimeout(function() {
global.clearTimeout(timerId2);
@@ -1034,7 +1182,7 @@ describe('Clock (acceptance)', function() {
});
it('correctly clears a scheduled interval while the Clock is advancing', function() {
var delayedFunctionScheduler = new jasmineUnderTest.DelayedFunctionScheduler(),
const delayedFunctionScheduler = new jasmineUnderTest.DelayedFunctionScheduler(),
global = { Date: Date, setTimeout: undefined },
mockDate = new jasmineUnderTest.MockDate(global),
clock = new jasmineUnderTest.Clock(
@@ -1047,7 +1195,7 @@ describe('Clock (acceptance)', function() {
clock.install();
var timerId2;
let timerId2;
global.setInterval(function() {
global.clearInterval(timerId2);
}, 100);

View File

@@ -1,6 +1,6 @@
describe('DelayedFunctionScheduler', function() {
it('schedules a function for later execution', function() {
var scheduler = new jasmineUnderTest.DelayedFunctionScheduler(),
const scheduler = new jasmineUnderTest.DelayedFunctionScheduler(),
fn = jasmine.createSpy('fn');
scheduler.scheduleFunction(fn, 0);
@@ -13,7 +13,7 @@ describe('DelayedFunctionScheduler', function() {
});
it('schedules a string for later execution', function() {
var scheduler = new jasmineUnderTest.DelayedFunctionScheduler(),
const scheduler = new jasmineUnderTest.DelayedFunctionScheduler(),
strfn = 'horrible = true;';
scheduler.scheduleFunction(strfn, 0);
@@ -24,7 +24,7 @@ describe('DelayedFunctionScheduler', function() {
});
it('#tick defaults to 0', function() {
var scheduler = new jasmineUnderTest.DelayedFunctionScheduler(),
const scheduler = new jasmineUnderTest.DelayedFunctionScheduler(),
fn = jasmine.createSpy('fn');
scheduler.scheduleFunction(fn, 0);
@@ -37,7 +37,7 @@ describe('DelayedFunctionScheduler', function() {
});
it('defaults delay to 0', function() {
var scheduler = new jasmineUnderTest.DelayedFunctionScheduler(),
const scheduler = new jasmineUnderTest.DelayedFunctionScheduler(),
fn = jasmine.createSpy('fn');
scheduler.scheduleFunction(fn);
@@ -50,7 +50,7 @@ describe('DelayedFunctionScheduler', function() {
});
it('optionally passes params to scheduled functions', function() {
var scheduler = new jasmineUnderTest.DelayedFunctionScheduler(),
const scheduler = new jasmineUnderTest.DelayedFunctionScheduler(),
fn = jasmine.createSpy('fn');
scheduler.scheduleFunction(fn, 0, ['foo', 'bar']);
@@ -63,7 +63,7 @@ describe('DelayedFunctionScheduler', function() {
});
it('scheduled fns can optionally reoccur', function() {
var scheduler = new jasmineUnderTest.DelayedFunctionScheduler(),
const scheduler = new jasmineUnderTest.DelayedFunctionScheduler(),
fn = jasmine.createSpy('fn');
scheduler.scheduleFunction(fn, 20, [], true);
@@ -84,22 +84,21 @@ describe('DelayedFunctionScheduler', function() {
});
it('increments scheduled fns ids unless one is passed', function() {
var scheduler = new jasmineUnderTest.DelayedFunctionScheduler();
const scheduler = new jasmineUnderTest.DelayedFunctionScheduler();
expect(scheduler.scheduleFunction(function() {}, 0)).toBe(1);
expect(scheduler.scheduleFunction(function() {}, 0)).toBe(2);
const initial = scheduler.scheduleFunction(function() {}, 0);
expect(scheduler.scheduleFunction(function() {}, 0)).toBe(initial + 1);
expect(scheduler.scheduleFunction(function() {}, 0)).toBe(initial + 2);
expect(scheduler.scheduleFunction(function() {}, 0, [], false, 123)).toBe(
123
);
expect(scheduler.scheduleFunction(function() {}, 0)).toBe(3);
expect(scheduler.scheduleFunction(function() {}, 0)).toBe(initial + 3);
});
it('#removeFunctionWithId removes a previously scheduled function with a given id', function() {
var scheduler = new jasmineUnderTest.DelayedFunctionScheduler(),
const scheduler = new jasmineUnderTest.DelayedFunctionScheduler(),
fn = jasmine.createSpy('fn'),
timeoutKey;
timeoutKey = scheduler.scheduleFunction(fn, 0);
timeoutKey = scheduler.scheduleFunction(fn, 0);
expect(fn).not.toHaveBeenCalled();
@@ -111,15 +110,15 @@ describe('DelayedFunctionScheduler', function() {
});
it('executes recurring functions interleaved with regular functions in the correct order', function() {
var scheduler = new jasmineUnderTest.DelayedFunctionScheduler(),
fn = jasmine.createSpy('fn'),
recurringCallCount = 0,
recurring = jasmine.createSpy('recurring').and.callFake(function() {
recurringCallCount++;
if (recurringCallCount < 5) {
expect(fn).not.toHaveBeenCalled();
}
});
const scheduler = new jasmineUnderTest.DelayedFunctionScheduler();
const fn = jasmine.createSpy('fn');
let recurringCallCount = 0;
const recurring = jasmine.createSpy('recurring').and.callFake(function() {
recurringCallCount++;
if (recurringCallCount < 5) {
expect(fn).not.toHaveBeenCalled();
}
});
scheduler.scheduleFunction(recurring, 10, [], true);
scheduler.scheduleFunction(fn, 50);
@@ -132,7 +131,7 @@ describe('DelayedFunctionScheduler', function() {
});
it('schedules a function for later execution during a tick', function() {
var scheduler = new jasmineUnderTest.DelayedFunctionScheduler(),
const scheduler = new jasmineUnderTest.DelayedFunctionScheduler(),
fn = jasmine.createSpy('fn'),
fnDelay = 10;
@@ -148,10 +147,10 @@ describe('DelayedFunctionScheduler', function() {
});
it('#removeFunctionWithId removes a previously scheduled function with a given id during a tick', function() {
var scheduler = new jasmineUnderTest.DelayedFunctionScheduler(),
const scheduler = new jasmineUnderTest.DelayedFunctionScheduler(),
fn = jasmine.createSpy('fn'),
fnDelay = 10,
timeoutKey;
fnDelay = 10;
let timeoutKey;
scheduler.scheduleFunction(function() {
scheduler.removeFunctionWithId(timeoutKey);
@@ -166,24 +165,24 @@ describe('DelayedFunctionScheduler', function() {
});
it('executes recurring functions interleaved with regular functions and functions scheduled during a tick in the correct order', function() {
var scheduler = new jasmineUnderTest.DelayedFunctionScheduler(),
fn = jasmine.createSpy('fn'),
recurringCallCount = 0,
recurring = jasmine.createSpy('recurring').and.callFake(function() {
recurringCallCount++;
if (recurringCallCount < 5) {
expect(fn).not.toHaveBeenCalled();
}
}),
innerFn = jasmine.createSpy('innerFn').and.callFake(function() {
expect(recurring.calls.count()).toBe(4);
const scheduler = new jasmineUnderTest.DelayedFunctionScheduler();
const fn = jasmine.createSpy('fn');
let recurringCallCount = 0;
const recurring = jasmine.createSpy('recurring').and.callFake(function() {
recurringCallCount++;
if (recurringCallCount < 5) {
expect(fn).not.toHaveBeenCalled();
}),
scheduling = jasmine.createSpy('scheduling').and.callFake(function() {
expect(recurring.calls.count()).toBe(3);
expect(fn).not.toHaveBeenCalled();
scheduler.scheduleFunction(innerFn, 10); // 41ms absolute
});
}
});
const innerFn = jasmine.createSpy('innerFn').and.callFake(function() {
expect(recurring.calls.count()).toBe(4);
expect(fn).not.toHaveBeenCalled();
});
const scheduling = jasmine.createSpy('scheduling').and.callFake(function() {
expect(recurring.calls.count()).toBe(3);
expect(fn).not.toHaveBeenCalled();
scheduler.scheduleFunction(innerFn, 10); // 41ms absolute
});
scheduler.scheduleFunction(recurring, 10, [], true);
scheduler.scheduleFunction(fn, 50);
@@ -199,7 +198,7 @@ describe('DelayedFunctionScheduler', function() {
});
it('executes recurring functions after rescheduling them', function() {
var scheduler = new jasmineUnderTest.DelayedFunctionScheduler(),
const scheduler = new jasmineUnderTest.DelayedFunctionScheduler(),
recurring = function() {
expect(scheduler.scheduleFunction).toHaveBeenCalled();
};
@@ -212,11 +211,11 @@ describe('DelayedFunctionScheduler', function() {
});
it('removes functions during a tick that runs the function', function() {
var scheduler = new jasmineUnderTest.DelayedFunctionScheduler(),
const scheduler = new jasmineUnderTest.DelayedFunctionScheduler(),
spy = jasmine.createSpy('fn'),
spyAndRemove = jasmine.createSpy('fn'),
fnDelay = 10,
timeoutKey;
fnDelay = 10;
let timeoutKey;
spyAndRemove.and.callFake(function() {
scheduler.removeFunctionWithId(timeoutKey);
@@ -233,10 +232,10 @@ describe('DelayedFunctionScheduler', function() {
});
it('removes functions during the first tick that runs the function', function() {
var scheduler = new jasmineUnderTest.DelayedFunctionScheduler(),
const scheduler = new jasmineUnderTest.DelayedFunctionScheduler(),
fn = jasmine.createSpy('fn'),
fnDelay = 10,
timeoutKey;
fnDelay = 10;
let timeoutKey;
timeoutKey = scheduler.scheduleFunction(fn, fnDelay, [], true);
scheduler.scheduleFunction(function() {
@@ -252,7 +251,7 @@ describe('DelayedFunctionScheduler', function() {
});
it("does not remove a function that hasn't been added yet", function() {
var scheduler = new jasmineUnderTest.DelayedFunctionScheduler(),
const scheduler = new jasmineUnderTest.DelayedFunctionScheduler(),
fn = jasmine.createSpy('fn'),
fnDelay = 10;
@@ -266,8 +265,44 @@ describe('DelayedFunctionScheduler', function() {
expect(fn).toHaveBeenCalled();
});
it('runs the next scheduled funtion', function() {
const scheduler = new jasmineUnderTest.DelayedFunctionScheduler();
const fn = jasmine.createSpy('fn');
const tickSpy = jasmine.createSpy('tick');
scheduler.scheduleFunction(fn, 10, [], false, 'foo');
expect(fn).not.toHaveBeenCalled();
scheduler.runNextQueuedFunction(tickSpy);
expect(fn).toHaveBeenCalled();
expect(tickSpy).toHaveBeenCalledWith(10);
});
it('runs the only a single scheduled funtion in a time slot', function() {
const scheduler = new jasmineUnderTest.DelayedFunctionScheduler();
const fn1 = jasmine.createSpy('fn');
const fn2 = jasmine.createSpy('fn2');
const tickSpy = jasmine.createSpy('tick');
scheduler.scheduleFunction(fn1, 10, [], false, 'foo1');
scheduler.scheduleFunction(fn2, 10, [], false, 'foo2');
scheduler.runNextQueuedFunction(tickSpy);
expect(fn1).toHaveBeenCalled();
expect(fn2).not.toHaveBeenCalled();
expect(tickSpy).toHaveBeenCalledWith(10);
tickSpy.calls.reset();
scheduler.runNextQueuedFunction(tickSpy);
expect(fn2).toHaveBeenCalled();
expect(tickSpy).toHaveBeenCalledWith(0);
});
it('updates the mockDate per scheduled time', function() {
var scheduler = new jasmineUnderTest.DelayedFunctionScheduler(),
const scheduler = new jasmineUnderTest.DelayedFunctionScheduler(),
tickDate = jasmine.createSpy('tickDate');
scheduler.scheduleFunction(function() {});
@@ -279,6 +314,28 @@ describe('DelayedFunctionScheduler', function() {
expect(tickDate).toHaveBeenCalledWith(1);
});
it('does not conflict with native timer IDs', function() {
const NODE_JS =
typeof process !== 'undefined' &&
process.versions &&
typeof process.versions.node === 'string';
if (NODE_JS) {
pending('numeric timer ID conflicts only relevant for browsers.');
}
const nativeTimeoutId = setTimeout(function() {}, 100);
const scheduler = new jasmineUnderTest.DelayedFunctionScheduler();
const fn = jasmine.createSpy('fn');
for (let i = 0; i < nativeTimeoutId; i++) {
scheduler.scheduleFunction(fn, 0, [], false);
}
scheduler.removeFunctionWithId(nativeTimeoutId);
scheduler.tick(1);
expect(fn).toHaveBeenCalledTimes(nativeTimeoutId);
});
describe('ticking inside a scheduled function', function() {
let clock;

View File

@@ -6,8 +6,8 @@ describe('Deprecator', function() {
});
it('logs the mesage without context when the runnable is the top suite', function() {
var runnable = { addDeprecationWarning: function() {} };
var deprecator = new jasmineUnderTest.Deprecator(runnable);
const runnable = { addDeprecationWarning: function() {} };
const deprecator = new jasmineUnderTest.Deprecator(runnable);
deprecator.verboseDeprecations(true);
deprecator.addDeprecationWarning(runnable, 'the message', {
@@ -18,14 +18,14 @@ describe('Deprecator', function() {
});
it('logs the message in a descendant suite', function() {
var runnable = {
const runnable = {
addDeprecationWarning: function() {},
getFullName: function() {
return 'the suite';
},
children: []
};
var deprecator = new jasmineUnderTest.Deprecator({});
const deprecator = new jasmineUnderTest.Deprecator({});
deprecator.verboseDeprecations(true);
deprecator.addDeprecationWarning(runnable, 'the message', {
@@ -38,13 +38,13 @@ describe('Deprecator', function() {
});
it('logs and reports the message in a spec', function() {
var runnable = {
const runnable = {
addDeprecationWarning: function() {},
getFullName: function() {
return 'the spec';
}
};
var deprecator = new jasmineUnderTest.Deprecator({});
const deprecator = new jasmineUnderTest.Deprecator({});
deprecator.verboseDeprecations(true);
deprecator.addDeprecationWarning(runnable, 'the message', {
@@ -57,12 +57,12 @@ describe('Deprecator', function() {
});
it('logs and reports the message without runnable info when ignoreRunnable is true', function() {
var topSuite = jasmine.createSpyObj('topSuite', [
const topSuite = jasmine.createSpyObj('topSuite', [
'addDeprecationWarning',
'getFullName'
]);
var deprecator = new jasmineUnderTest.Deprecator(topSuite);
var runnable = jasmine.createSpyObj('spec', [
const deprecator = new jasmineUnderTest.Deprecator(topSuite);
const runnable = jasmine.createSpyObj('spec', [
'addDeprecationWarning',
'getFullName'
]);
@@ -105,12 +105,12 @@ describe('Deprecator', function() {
});
it('emits the deprecation only once when verboseDeprecations is not set', function() {
var deprecator = new jasmineUnderTest.Deprecator({});
var runnable1 = jasmine.createSpyObj('runnable1', [
const deprecator = new jasmineUnderTest.Deprecator({});
const runnable1 = jasmine.createSpyObj('runnable1', [
'addDeprecationWarning',
'getFullName'
]);
var runnable2 = jasmine.createSpyObj('runnable2', [
const runnable2 = jasmine.createSpyObj('runnable2', [
'addDeprecationWarning',
'getFullName'
]);
@@ -124,12 +124,12 @@ describe('Deprecator', function() {
});
it('emits the deprecation only once when verboseDeprecations is false', function() {
var deprecator = new jasmineUnderTest.Deprecator({});
var runnable1 = jasmine.createSpyObj('runnable1', [
const deprecator = new jasmineUnderTest.Deprecator({});
const runnable1 = jasmine.createSpyObj('runnable1', [
'addDeprecationWarning',
'getFullName'
]);
var runnable2 = jasmine.createSpyObj('runnable2', [
const runnable2 = jasmine.createSpyObj('runnable2', [
'addDeprecationWarning',
'getFullName'
]);
@@ -144,12 +144,12 @@ describe('Deprecator', function() {
});
it('emits the deprecation for each call when verboseDeprecations is true', function() {
var deprecator = new jasmineUnderTest.Deprecator({});
var runnable1 = jasmine.createSpyObj('runnable1', [
const deprecator = new jasmineUnderTest.Deprecator({});
const runnable1 = jasmine.createSpyObj('runnable1', [
'addDeprecationWarning',
'getFullName'
]);
var runnable2 = jasmine.createSpyObj('runnable2', [
const runnable2 = jasmine.createSpyObj('runnable2', [
'addDeprecationWarning',
'getFullName'
]);
@@ -164,8 +164,8 @@ describe('Deprecator', function() {
});
it('includes a note about verboseDeprecations', function() {
var deprecator = new jasmineUnderTest.Deprecator({});
var runnable = jasmine.createSpyObj('runnable', [
const deprecator = new jasmineUnderTest.Deprecator({});
const runnable = jasmine.createSpyObj('runnable', [
'addDeprecationWarning',
'getFullName'
]);
@@ -183,8 +183,8 @@ describe('Deprecator', function() {
});
it('omits the note about verboseDeprecations when verboseDeprecations is true', function() {
var deprecator = new jasmineUnderTest.Deprecator({});
var runnable = jasmine.createSpyObj('runnable', [
const deprecator = new jasmineUnderTest.Deprecator({});
const runnable = jasmine.createSpyObj('runnable', [
'addDeprecationWarning',
'getFullName'
]);
@@ -207,13 +207,12 @@ describe('Deprecator', function() {
// to report their own deprecations through Jasmine. See
// <https://github.com/jasmine/jasmine/pull/1498>.
it('passes the error through unchanged', function() {
var deprecator = new jasmineUnderTest.Deprecator({});
var runnable = jasmine.createSpyObj('runnable', [
const deprecator = new jasmineUnderTest.Deprecator({});
const runnable = jasmine.createSpyObj('runnable', [
'addDeprecationWarning',
'getFullName'
]);
var exceptionFormatter = new jasmineUnderTest.ExceptionFormatter();
var deprecation, originalStack;
let deprecation, originalStack;
try {
throw new Error('the deprecation');
@@ -239,12 +238,12 @@ describe('Deprecator', function() {
});
it('reports the deprecation every time, regardless of config.verboseDeprecations', function() {
var deprecator = new jasmineUnderTest.Deprecator({});
var runnable = jasmine.createSpyObj('runnable', [
const deprecator = new jasmineUnderTest.Deprecator({});
const runnable = jasmine.createSpyObj('runnable', [
'addDeprecationWarning',
'getFullName'
]);
var deprecation;
let deprecation;
try {
throw new Error('the deprecation');
@@ -260,12 +259,12 @@ describe('Deprecator', function() {
});
it('omits the note about verboseDeprecations', function() {
var deprecator = new jasmineUnderTest.Deprecator({});
var runnable = jasmine.createSpyObj('runnable', [
const deprecator = new jasmineUnderTest.Deprecator({});
const runnable = jasmine.createSpyObj('runnable', [
'addDeprecationWarning',
'getFullName'
]);
var deprecation;
let deprecation;
try {
throw new Error('the deprecation');
@@ -294,8 +293,8 @@ describe('Deprecator', function() {
}
function testStackTrace(options) {
var deprecator = new jasmineUnderTest.Deprecator({});
var runnable = jasmine.createSpyObj('runnable', [
const deprecator = new jasmineUnderTest.Deprecator({});
const runnable = jasmine.createSpyObj('runnable', [
'addDeprecationWarning',
'getFullName'
]);
@@ -312,8 +311,8 @@ describe('Deprecator', function() {
}
function testNoStackTrace(options) {
var deprecator = new jasmineUnderTest.Deprecator({});
var runnable = jasmine.createSpyObj('runnable', [
const deprecator = new jasmineUnderTest.Deprecator({});
const runnable = jasmine.createSpyObj('runnable', [
'addDeprecationWarning',
'getFullName'
]);

View File

@@ -1,6 +1,6 @@
// TODO: Fix these unit tests!
describe('Env', function() {
var env;
let env;
beforeEach(function() {
env = new jasmineUnderTest.Env();
});
@@ -27,7 +27,6 @@ describe('Env', function() {
describe('#topSuite', function() {
it('returns an object that describes the tree of suites and specs', function() {
var suite;
spyOn(env, 'deprecated');
env.it('a top level spec');
@@ -38,7 +37,7 @@ describe('Env', function() {
});
});
suite = env.topSuite();
const suite = env.topSuite();
expect(suite).not.toBeInstanceOf(jasmineUnderTest.Suite);
expect(suite.description).toEqual('Jasmine__TopLevel__Suite');
expect(suite.getFullName()).toEqual('');
@@ -81,6 +80,19 @@ describe('Env', function() {
);
expect(suite.children[1].children[1].children[0].children).toBeFalsy();
});
it('throws if called in parallel mode', function() {
env.setParallelLoadingState('helpers');
check();
env.setParallelLoadingState('specs');
check();
function check() {
expect(function() {
env.topSuite();
}).toThrowError("'topSuite' is not available in parallel mode");
}
});
});
it('accepts its own current configureation', function() {
@@ -113,7 +125,7 @@ describe('Env', function() {
it('ignores configuration properties that are present but undefined', function() {
spyOn(env, 'deprecated');
var initialConfig = {
const initialConfig = {
random: true,
seed: '123',
failSpecWithNoExpectations: true,
@@ -161,8 +173,8 @@ describe('Env', function() {
it('returns a suite metadata object', function() {
let innerSuite;
let spec;
const suite = env.describe('outer suite', function() {
innerSuite = env.describe('inner suite', function() {
const suite = env[methodName]('outer suite', function() {
innerSuite = env[methodName]('inner suite', function() {
spec = env.it('a spec');
});
});
@@ -251,6 +263,15 @@ describe('Env', function() {
describe('#fdescribe', function() {
behavesLikeDescribe('fdescribe');
it('throws an error in parallel mode', function() {
env.setParallelLoadingState('specs');
expect(function() {
env.fdescribe('a suite', function() {
env.it('a spec');
});
}).toThrowError("'fdescribe' is not available in parallel mode");
});
});
describe('xdescribe', function() {
@@ -315,8 +336,8 @@ describe('Env', function() {
behavesLikeIt('xit');
it('calls spec.exclude with "Temporarily disabled with xit"', function() {
var excludeSpy = jasmine.createSpy();
spyOn(env, 'it_').and.returnValue({
const excludeSpy = jasmine.createSpy();
spyOn(jasmineUnderTest.SuiteBuilder.prototype, 'it_').and.returnValue({
exclude: excludeSpy
});
env.xit('foo', function() {});
@@ -324,10 +345,10 @@ describe('Env', function() {
});
it('calls spec.pend with "Temporarily disabled with xit"', function() {
var pendSpy = jasmine.createSpy();
var realExclude = jasmineUnderTest.Spec.prototype.exclude;
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
});
@@ -372,6 +393,13 @@ describe('Env', function() {
env.fit('huge timeout', function() {}, 2147483648);
}).toThrowError('Timeout value cannot be greater than 2147483647');
});
it('throws an error in parallel mode', function() {
env.setParallelLoadingState('specs');
expect(function() {
env.fit('a spec', function() {});
}).toThrowError("'fit' is not available in parallel mode");
});
});
describe('#beforeEach', function() {
@@ -394,6 +422,28 @@ describe('Env', function() {
env.beforeEach(function() {}, 2147483648);
}).toThrowError('Timeout value cannot be greater than 2147483647');
});
it('throws when called at the top level in a spec file in parallel mode', function() {
env.setParallelLoadingState('specs');
expect(function() {
env.beforeEach(function() {});
}).toThrowError(
'In parallel mode, beforeEach must be in a describe block or in a helper file'
);
});
it('does not throw when called at the top level in a helper file in parallel mode', function() {
env.setParallelLoadingState('helpers');
env.beforeEach(function() {});
});
it('does not throw when called in a describe in a spec file in parallel mode', function() {
env.setParallelLoadingState('specs');
env.describe('a suite', function() {
env.beforeEach(function() {});
env.it('a spec');
});
});
});
describe('#beforeAll', function() {
@@ -416,6 +466,47 @@ describe('Env', function() {
env.beforeAll(function() {}, 2147483648);
}).toThrowError('Timeout value cannot be greater than 2147483647');
});
describe('in parallel mode', function() {
it('throws an error when called at the top level', function() {
env.setParallelLoadingState('helpers');
check();
env.setParallelLoadingState('specs');
check();
function check() {
expect(function() {
env.beforeAll(function() {});
}).toThrowError(
"In parallel mode, 'beforeAll' must be in a describe block. " +
'Use the globalSetup config property for exactly-once setup in' +
' parallel mode.'
);
}
});
it('does not throw an error when called in a describe', function() {
env.setParallelLoadingState('helpers');
check();
env.setParallelLoadingState('specs');
check();
function check() {
let done = false;
env.describe('a suite', function() {
expect(function() {
env.it('a spec');
env.beforeAll(function() {});
}).not.toThrow();
done = true;
});
expect(done).toBeTrue();
}
});
});
});
describe('#afterEach', function() {
@@ -438,6 +529,28 @@ describe('Env', function() {
env.afterEach(function() {}, 2147483648);
}).toThrowError('Timeout value cannot be greater than 2147483647');
});
it('throws when called at the top level in a spec file in parallel mode', function() {
env.setParallelLoadingState('specs');
expect(function() {
env.afterEach(function() {});
}).toThrowError(
'In parallel mode, afterEach must be in a describe block or in a helper file'
);
});
it('does not throw when called at the top level in a helper file in parallel mode', function() {
env.setParallelLoadingState('helpers');
env.afterEach(function() {});
});
it('does not throw when called in a describe in a spec file in parallel mode', function() {
env.setParallelLoadingState('specs');
env.describe('a suite', function() {
env.afterEach(function() {});
env.it('a spec');
});
});
});
describe('#afterAll', function() {
@@ -460,47 +573,97 @@ describe('Env', function() {
env.afterAll(function() {}, 2147483648);
}).toThrowError('Timeout value cannot be greater than 2147483647');
});
describe('in parallel mode', function() {
it('throws an error when called at the top level', function() {
env.setParallelLoadingState('helpers');
check();
env.setParallelLoadingState('specs');
check();
function check() {
expect(function() {
env.afterAll(function() {});
}).toThrowError(
"In parallel mode, 'afterAll' must be in a describe block. " +
'Use the globalTeardown config property for exactly-once ' +
'teardown in parallel mode.'
);
}
});
it('does not throw an error when called in a describe', function() {
env.setParallelLoadingState('helpers');
check();
env.setParallelLoadingState('specs');
check();
function check() {
let done = false;
env.describe('a suite', function() {
expect(function() {
env.it('a spec');
env.afterAll(function() {});
}).not.toThrow();
done = true;
});
expect(done).toBeTrue();
}
});
});
});
describe('when not constructed with suppressLoadErrors: true', function() {
it('installs a global error handler on construction', function() {
var globalErrors = jasmine.createSpyObj('globalErrors', [
const globalErrors = jasmine.createSpyObj('globalErrors', [
'install',
'uninstall',
'pushListener',
'popListener'
'popListener',
'removeOverrideListener'
]);
spyOn(jasmineUnderTest, 'GlobalErrors').and.returnValue(globalErrors);
env.cleanup_();
env = new jasmineUnderTest.Env();
env = new jasmineUnderTest.Env({
GlobalErrors: function() {
return globalErrors;
}
});
expect(globalErrors.install).toHaveBeenCalled();
});
});
describe('when constructed with suppressLoadErrors: true', function() {
it('does not install a global error handler until execute is called', function() {
var globalErrors = jasmine.createSpyObj('globalErrors', [
const globalErrors = jasmine.createSpyObj('globalErrors', [
'install',
'uninstall',
'pushListener',
'popListener'
'popListener',
'removeOverrideListener'
]);
spyOn(jasmineUnderTest, 'GlobalErrors').and.returnValue(globalErrors);
env.cleanup_();
env = new jasmineUnderTest.Env({ suppressLoadErrors: true });
env = new jasmineUnderTest.Env({
suppressLoadErrors: true,
GlobalErrors: function() {
return globalErrors;
}
});
expect(globalErrors.install).not.toHaveBeenCalled();
env.execute();
expect(globalErrors.install).toHaveBeenCalled();
});
});
it('creates an expectationFactory that uses the current custom equality testers and object formatters', function(done) {
it('creates an expectationFactory that uses the current custom equality testers and object formatters', async function() {
function customEqualityTester() {}
function customObjectFormatter() {}
function prettyPrinter() {}
var RealSpec = jasmineUnderTest.Spec,
specInstance,
expectationFactory;
const RealSpec = jasmineUnderTest.Spec;
let specInstance;
let expectationFactory;
spyOn(jasmineUnderTest, 'MatchersUtil');
spyOn(jasmineUnderTest, 'makePrettyPrinter').and.returnValue(prettyPrinter);
spyOn(jasmineUnderTest, 'Spec').and.callFake(function(options) {
@@ -515,25 +678,23 @@ 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() {}
var RealSpec = jasmineUnderTest.Spec,
specInstance,
asyncExpectationFactory;
const RealSpec = jasmineUnderTest.Spec;
let specInstance;
let asyncExpectationFactory;
spyOn(jasmineUnderTest, 'MatchersUtil');
spyOn(jasmineUnderTest, 'makePrettyPrinter').and.returnValue(prettyPrinter);
spyOn(jasmineUnderTest, 'Spec').and.callFake(function(options) {
@@ -548,20 +709,19 @@ 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
});
});
it("does not expose the suite as 'this'", function() {
var suiteThis;
let suiteThis;
spyOn(env, 'deprecated');
env.describe('a suite', function() {
@@ -585,14 +745,96 @@ describe('Env', function() {
return env.execute(); // 2
})
.then(function() {
var id;
expect(
jasmineUnderTest.Suite.prototype.reset
).toHaveBeenCalledOnceWith();
id = jasmineUnderTest.Suite.prototype.reset.calls.thisFor(0).id;
const id = jasmineUnderTest.Suite.prototype.reset.calls.thisFor(0).id;
expect(id).toBeTruthy();
expect(id).toEqual(env.topSuite().id);
});
});
it('should not reset the topSuite if parallelReset was called since the last run', async function() {
await env.execute();
env.parallelReset();
spyOn(jasmineUnderTest.Suite.prototype, 'reset');
await env.execute();
expect(jasmineUnderTest.Suite.prototype.reset).not.toHaveBeenCalled();
});
describe('In parallel mode', function() {
it('rejects if random is set to false', async function() {
env.configure({ random: false });
env.setParallelLoadingState('specs');
await expectAsync(env.execute()).toBeRejectedWithError(
'Randomization cannot be disabled in parallel mode'
);
});
it('rejects if seed is set', async function() {
env.configure({ seed: 1234 });
env.setParallelLoadingState('specs');
await expectAsync(env.execute()).toBeRejectedWithError(
'Random seed cannot be set in parallel mode'
);
});
});
});
describe('#spyOnGlobalErrorsAsync', function() {
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);
});
});
describe('#addReporter', function() {
it('throws when called in parallel mode', function() {
env.setParallelLoadingState('helpers');
expect(function() {
env.addReporter({});
}).toThrowError('Reporters cannot be added via Env in parallel mode');
env.setParallelLoadingState('specs');
expect(function() {
env.addReporter({});
}).toThrowError('Reporters cannot be added via Env in parallel mode');
});
});
describe('#clearReporters', function() {
it('throws when called in parallel mode', function() {
env.setParallelLoadingState('helpers');
expect(function() {
env.clearReporters();
}).toThrowError('Reporters cannot be removed via Env in parallel mode');
env.setParallelLoadingState('specs');
expect(function() {
env.clearReporters();
}).toThrowError('Reporters cannot be removed via Env in parallel mode');
});
});
describe('#configure', function() {
it('throws when called in parallel mode', function() {
env.setParallelLoadingState('helpers');
expect(function() {
env.configure({});
}).toThrowError('Jasmine cannot be configured via Env in parallel mode');
env.setParallelLoadingState('specs');
expect(function() {
env.configure({});
}).toThrowError('Jasmine cannot be configured via Env in parallel mode');
});
});
});

View File

@@ -1,7 +1,7 @@
describe('ExceptionFormatter', function() {
describe('#message', function() {
it('formats Firefox exception messages', function() {
var sampleFirefoxException = {
const sampleFirefoxException = {
fileName: 'foo.js',
lineNumber: '1978',
message: 'you got your foo in my bar',
@@ -16,7 +16,7 @@ describe('ExceptionFormatter', function() {
});
it('formats Webkit exception messages', function() {
var sampleWebkitException = {
const sampleWebkitException = {
sourceURL: 'foo.js',
line: '1978',
message: 'you got your foo in my bar',
@@ -31,7 +31,7 @@ describe('ExceptionFormatter', function() {
});
it('formats V8 exception messages', function() {
var sampleV8 = {
const sampleV8 = {
message: 'you got your foo in my bar',
name: 'A Classic Mistake'
},
@@ -42,29 +42,29 @@ describe('ExceptionFormatter', function() {
});
it('formats unnamed exceptions with message', function() {
var unnamedError = { message: 'This is an unnamed error message.' };
const unnamedError = { message: 'This is an unnamed error message.' };
var exceptionFormatter = new jasmineUnderTest.ExceptionFormatter(),
const exceptionFormatter = new jasmineUnderTest.ExceptionFormatter(),
message = exceptionFormatter.message(unnamedError);
expect(message).toEqual('This is an unnamed error message.');
});
it('formats empty exceptions with toString format', function() {
var EmptyError = function() {};
const EmptyError = function() {};
EmptyError.prototype.toString = function() {
return '[EmptyError]';
};
var emptyError = new EmptyError();
const emptyError = new EmptyError();
var exceptionFormatter = new jasmineUnderTest.ExceptionFormatter(),
const exceptionFormatter = new jasmineUnderTest.ExceptionFormatter(),
message = exceptionFormatter.message(emptyError);
expect(message).toEqual('[EmptyError] thrown');
});
it("formats thrown exceptions that aren't errors", function() {
var thrown = 'crazy error',
const thrown = 'crazy error',
exceptionFormatter = new jasmineUnderTest.ExceptionFormatter(),
message = exceptionFormatter.message(thrown);
@@ -74,12 +74,7 @@ describe('ExceptionFormatter', function() {
describe('#stack', function() {
it('formats stack traces', function() {
var error;
try {
throw new Error('an error');
} catch (e) {
error = e;
}
const error = new Error('an error');
expect(new jasmineUnderTest.ExceptionFormatter().stack(error)).toMatch(
/ExceptionFormatterSpec\.js.*\d+/
@@ -87,7 +82,7 @@ describe('ExceptionFormatter', function() {
});
it('filters Jasmine stack frames from V8-style traces but leaves unmatched lines intact', function() {
var error = {
const error = {
message: 'nope',
stack:
'C:\\__spec__\\core\\UtilSpec.ts:120\n' +
@@ -101,10 +96,10 @@ describe('ExceptionFormatter', function() {
' at fn3 (C:\\__jasmine__\\lib\\jasmine-core\\jasmine.js:7575:25)\n' +
' at fn4 (node:internal/timers:462:21)\n'
};
var subject = new jasmineUnderTest.ExceptionFormatter({
const subject = new jasmineUnderTest.ExceptionFormatter({
jasmineFile: 'C:\\__jasmine__\\lib\\jasmine-core\\jasmine.js'
});
var result = subject.stack(error);
const result = subject.stack(error);
expect(result).toEqual(
'C:\\__spec__\\core\\UtilSpec.ts:120\n' +
" new Error('nope');\n" +
@@ -118,7 +113,7 @@ describe('ExceptionFormatter', function() {
});
it('filters Jasmine stack frames from V8 style traces', function() {
var error = {
const error = {
message: 'nope',
stack:
'Error: nope\n' +
@@ -127,10 +122,10 @@ describe('ExceptionFormatter', function() {
' at fn3 (http://localhost:8888/__jasmine__/jasmine.js:4320:20)\n' +
' at fn4 (http://localhost:8888/__spec__/core/UtilSpec.js:110:19)\n'
};
var subject = new jasmineUnderTest.ExceptionFormatter({
const subject = new jasmineUnderTest.ExceptionFormatter({
jasmineFile: 'http://localhost:8888/__jasmine__/jasmine.js'
});
var result = subject.stack(error);
const result = subject.stack(error);
expect(result).toEqual(
'Error: nope\n' +
' at fn1 (http://localhost:8888/__spec__/core/UtilSpec.js:115:19)\n' +
@@ -140,17 +135,36 @@ describe('ExceptionFormatter', function() {
});
it('filters Jasmine stack frames from Webkit style traces', function() {
var error = {
const error = {
stack:
'http://localhost:8888/__spec__/core/UtilSpec.js:115:28\n' +
'fn1@http://localhost:8888/__jasmine__/jasmine.js:4320:27\n' +
'fn2@http://localhost:8888/__jasmine__/jasmine.js:4320:27\n' +
'http://localhost:8888/__spec__/core/UtilSpec.js:115:28'
};
var subject = new jasmineUnderTest.ExceptionFormatter({
const subject = new jasmineUnderTest.ExceptionFormatter({
jasmineFile: 'http://localhost:8888/__jasmine__/jasmine.js'
});
var result = subject.stack(error);
const result = subject.stack(error);
expect(result).toEqual(
'http://localhost:8888/__spec__/core/UtilSpec.js:115:28\n' +
'<Jasmine>\n' +
'http://localhost:8888/__spec__/core/UtilSpec.js:115:28'
);
});
it('filters Jasmine stack frames with Firefox async annotations', function() {
const error = {
stack:
'http://localhost:8888/__spec__/core/UtilSpec.js:115:28\n' +
'promise callback*fn1@http://localhost:8888/__jasmine__/jasmine.js:4320:27\n' +
'setTimeout handler*fn2@http://localhost:8888/__jasmine__/jasmine.js:4320:27\n' +
'http://localhost:8888/__spec__/core/UtilSpec.js:115:28'
};
const subject = new jasmineUnderTest.ExceptionFormatter({
jasmineFile: 'http://localhost:8888/__jasmine__/jasmine.js'
});
const result = subject.stack(error);
expect(result).toEqual(
'http://localhost:8888/__spec__/core/UtilSpec.js:115:28\n' +
'<Jasmine>\n' +
@@ -159,17 +173,14 @@ describe('ExceptionFormatter', function() {
});
it('filters Jasmine stack frames in this environment', function() {
var error, i;
try {
throw new Error('an error');
} catch (e) {
error = e;
}
var subject = new jasmineUnderTest.ExceptionFormatter({
const error = new Error('an error');
const subject = new jasmineUnderTest.ExceptionFormatter({
jasmineFile: jasmine.util.jasmineFile()
});
var result = subject.stack(error);
var lines = result.split('\n');
const result = subject.stack(error);
jasmine.debugLog('Original stack trace: ' + error.stack);
jasmine.debugLog('Filtered stack trace: ' + result);
const lines = result.split('\n');
if (lines[0].match(/an error/)) {
lines.shift();
@@ -179,28 +190,23 @@ describe('ExceptionFormatter', function() {
expect(lines[1]).toMatch(/<Jasmine>/);
// Node has some number of additional frames below Jasmine.
for (i = 2; i < lines.length; i++) {
for (let i = 2; i < lines.length; i++) {
expect(lines[i]).not.toMatch(/jasmine.js/);
}
});
it('handles multiline error messages in this environment', function() {
var error,
msg = 'an error\nwith two lines';
try {
throw new Error(msg);
} catch (e) {
error = e;
}
const msg = 'an error\nwith two lines';
const error = new Error(msg);
if (error.stack.indexOf(msg) === -1) {
pending("Stack traces don't have messages in this environment");
}
var subject = new jasmineUnderTest.ExceptionFormatter({
const subject = new jasmineUnderTest.ExceptionFormatter({
jasmineFile: jasmine.util.jasmineFile()
});
var result = subject.stack(error);
var lines = result.split('\n');
const result = subject.stack(error);
const lines = result.split('\n');
expect(lines[0]).toMatch(/an error/);
expect(lines[1]).toMatch(/with two lines/);
@@ -212,20 +218,28 @@ describe('ExceptionFormatter', function() {
expect(new jasmineUnderTest.ExceptionFormatter().stack()).toBeNull();
});
it('includes error properties in stack', function() {
var error;
try {
throw new Error('an error');
} catch (e) {
error = e;
}
it("includes the error's own properties in stack", function() {
const error = new Error('an error');
error.someProperty = 'hello there';
var result = new jasmineUnderTest.ExceptionFormatter().stack(error);
const result = new jasmineUnderTest.ExceptionFormatter().stack(error);
expect(result).toMatch(/error properties:.*someProperty.*hello there/);
});
it('does not include inherited error properties', function() {
function CustomError(msg) {
Error.call(this, msg);
}
CustomError.prototype = new Error();
CustomError.prototype.anInheritedProp = 'something';
const error = new CustomError('nope');
const result = new jasmineUnderTest.ExceptionFormatter().stack(error);
expect(result).not.toContain('anInheritedProp');
});
describe('When omitMessage is true', function() {
it('filters the message from V8-style stack traces', function() {
const error = {
@@ -268,12 +282,7 @@ describe('ExceptionFormatter', function() {
});
it('ensures that stack traces do not include the message in this environment', function() {
let error;
try {
throw new Error('an error');
} catch (e) {
error = e;
}
const error = new Error('an error');
const subject = new jasmineUnderTest.ExceptionFormatter({
jasmineFile: jasmine.util.jasmineFile()
});
@@ -281,5 +290,61 @@ describe('ExceptionFormatter', function() {
expect(result).not.toContain('an error');
});
});
describe('when the error has a cause property', function() {
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');
});
it('does not throw if cause is a non Error', function() {
const formatter = new jasmineUnderTest.ExceptionFormatter();
expect(function() {
formatter.stack(
new Error('error', {
cause: function() {}
})
);
}).not.toThrowError();
expect(function() {
formatter.stack(
new Error('error', {
cause: 'another error'
})
);
}).not.toThrowError();
});
});
});
});

View File

@@ -1,5 +1,5 @@
describe('Exceptions:', function() {
var env;
let env;
beforeEach(function() {
env = new jasmineUnderTest.Env();
@@ -9,8 +9,8 @@ describe('Exceptions:', function() {
env.cleanup_();
});
it('should handle exceptions thrown, but continue', function(done) {
var secondTest = jasmine.createSpy('second test');
it('should handle exceptions thrown, but continue', async function() {
const secondTest = jasmine.createSpy('second test');
env.describe('Suite for handles exceptions', function() {
env.it(
'should be a test that fails because it throws an exception',
@@ -24,16 +24,13 @@ describe('Exceptions:', function() {
);
});
var 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) {
var secondDescribe = jasmine
it('should handle exceptions thrown directly in top-level describe blocks and continue', async function() {
const secondDescribe = jasmine
.createSpy('second describe')
.and.callFake(function() {
env.it('has a test', function() {});
@@ -47,11 +44,8 @@ describe('Exceptions:', function() {
});
env.describe("a suite that doesn't throw an exception", secondDescribe);
var expectations = function() {
expect(secondDescribe).toHaveBeenCalled();
done();
};
await env.execute();
env.execute(null, expectations);
expect(secondDescribe).toHaveBeenCalled();
});
});

View File

@@ -1,7 +1,7 @@
describe('ExpectationFilterChain', function() {
describe('#addFilter', function() {
it('returns a new filter chain with the added filter', function() {
var first = jasmine.createSpy('first'),
const first = jasmine.createSpy('first'),
second = jasmine.createSpy('second'),
orig = new jasmineUnderTest.ExpectationFilterChain({
modifyFailureMessage: first
@@ -15,7 +15,7 @@ describe('ExpectationFilterChain', function() {
});
it('does not modify the original filter chain', function() {
var orig = new jasmineUnderTest.ExpectationFilterChain({}),
const orig = new jasmineUnderTest.ExpectationFilterChain({}),
f = jasmine.createSpy('f');
orig.addFilter({ selectComparisonFunc: f });
@@ -28,7 +28,7 @@ describe('ExpectationFilterChain', function() {
describe('#selectComparisonFunc', function() {
describe('When no filters have #selectComparisonFunc', function() {
it('returns undefined', function() {
var chain = new jasmineUnderTest.ExpectationFilterChain();
const chain = new jasmineUnderTest.ExpectationFilterChain();
chain.addFilter({});
expect(chain.selectComparisonFunc()).toBeUndefined();
});
@@ -36,15 +36,13 @@ describe('ExpectationFilterChain', function() {
describe('When some filters have #selectComparisonFunc', function() {
it('calls the first filter that has #selectComparisonFunc', function() {
var first = jasmine.createSpy('first').and.returnValue('first'),
const first = jasmine.createSpy('first').and.returnValue('first'),
second = jasmine.createSpy('second').and.returnValue('second'),
chain = new jasmineUnderTest.ExpectationFilterChain()
.addFilter({ selectComparisonFunc: first })
.addFilter({ selectComparisonFunc: second }),
matcher = {},
result;
result = chain.selectComparisonFunc(matcher);
result = chain.selectComparisonFunc(matcher);
expect(first).toHaveBeenCalledWith(matcher);
expect(second).not.toHaveBeenCalled();
@@ -56,7 +54,7 @@ describe('ExpectationFilterChain', function() {
describe('#buildFailureMessage', function() {
describe('When no filters have #buildFailureMessage', function() {
it('returns undefined', function() {
var chain = new jasmineUnderTest.ExpectationFilterChain();
const chain = new jasmineUnderTest.ExpectationFilterChain();
chain.addFilter({});
expect(chain.buildFailureMessage()).toBeUndefined();
});
@@ -64,7 +62,7 @@ describe('ExpectationFilterChain', function() {
describe('When some filters have #buildFailureMessage', function() {
it('calls the first filter that has #buildFailureMessage', function() {
var first = jasmine.createSpy('first').and.returnValue('first'),
const first = jasmine.createSpy('first').and.returnValue('first'),
second = jasmine.createSpy('second').and.returnValue('second'),
chain = new jasmineUnderTest.ExpectationFilterChain()
.addFilter({ buildFailureMessage: first })
@@ -72,10 +70,9 @@ describe('ExpectationFilterChain', function() {
matcherResult = { pass: false },
matcherName = 'foo',
args = [],
matchersUtil = {},
result;
matchersUtil = {};
result = chain.buildFailureMessage(
const result = chain.buildFailureMessage(
matcherResult,
matcherName,
args,
@@ -97,7 +94,7 @@ describe('ExpectationFilterChain', function() {
describe('#modifyFailureMessage', function() {
describe('When no filters have #modifyFailureMessage', function() {
it('returns the original message', function() {
var chain = new jasmineUnderTest.ExpectationFilterChain();
const chain = new jasmineUnderTest.ExpectationFilterChain();
chain.addFilter({});
expect(chain.modifyFailureMessage('msg')).toEqual('msg');
});
@@ -105,14 +102,12 @@ describe('ExpectationFilterChain', function() {
describe('When some filters have #modifyFailureMessage', function() {
it('calls the first filter that has #modifyFailureMessage', function() {
var first = jasmine.createSpy('first').and.returnValue('first'),
const first = jasmine.createSpy('first').and.returnValue('first'),
second = jasmine.createSpy('second').and.returnValue('second'),
chain = new jasmineUnderTest.ExpectationFilterChain()
.addFilter({ modifyFailureMessage: first })
.addFilter({ modifyFailureMessage: second }),
result;
result = chain.modifyFailureMessage('original');
result = chain.modifyFailureMessage('original');
expect(first).toHaveBeenCalledWith('original');
expect(second).not.toHaveBeenCalled();

View File

@@ -1,130 +0,0 @@
describe('buildExpectationResult', function() {
it('defaults to passed', function() {
var result = jasmineUnderTest.buildExpectationResult({
passed: 'some-value'
});
expect(result.passed).toBe('some-value');
});
it('message defaults to Passed for passing specs', function() {
var result = jasmineUnderTest.buildExpectationResult({
passed: true,
message: 'some-value'
});
expect(result.message).toBe('Passed.');
});
it('message returns the message for failing expectations', function() {
var result = jasmineUnderTest.buildExpectationResult({
passed: false,
message: 'some-value'
});
expect(result.message).toBe('some-value');
});
it('delegates message formatting to the provided formatter if there was an Error', function() {
var fakeError = { message: 'foo' },
messageFormatter = jasmine
.createSpy('exception message formatter')
.and.returnValue(fakeError.message);
var result = jasmineUnderTest.buildExpectationResult({
passed: false,
error: fakeError,
messageFormatter: messageFormatter
});
expect(messageFormatter).toHaveBeenCalledWith(fakeError);
expect(result.message).toEqual('foo');
});
it('delegates stack formatting to the provided formatter if there was an Error', function() {
var fakeError = { stack: 'foo' },
stackFormatter = jasmine
.createSpy('stack formatter')
.and.returnValue(fakeError.stack);
var result = jasmineUnderTest.buildExpectationResult({
passed: false,
error: fakeError,
stackFormatter: stackFormatter
});
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() {
var fakeError = { stack: 'foo' },
stackFormatter = jasmine
.createSpy('stack formatter')
.and.returnValue(fakeError.stack);
var 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() {
var result = jasmineUnderTest.buildExpectationResult({
matcherName: 'some-value'
});
expect(result.matcherName).toBe('some-value');
});
it('expected returns passed expected', function() {
var result = jasmineUnderTest.buildExpectationResult({
expected: 'some-value'
});
expect(result.expected).toBe('some-value');
});
it('actual returns passed actual', function() {
var result = jasmineUnderTest.buildExpectationResult({
actual: 'some-value'
});
expect(result.actual).toBe('some-value');
});
it('handles nodejs assertions', function() {
if (typeof require === 'undefined') {
return;
}
var assert = require('assert');
var error;
var value = 8421;
var expectedValue = 'JasmineExpectationTestValue';
try {
assert.equal(value, expectedValue);
} catch (e) {
error = e;
}
expect(error.code).toEqual('ERR_ASSERTION');
expect(error.actual).toEqual(value);
expect(error.expected).toEqual(expectedValue);
expect(error.operator).toEqual('==');
var result = jasmineUnderTest.buildExpectationResult({
passed: false,
matcherName: '',
expected: '',
actual: '',
error: error
});
expect(result.code).toEqual('ERR_ASSERTION');
expect(result.actual).toEqual(value);
expect(result.expected).toEqual(expectedValue);
expect(result.matcherName).toEqual('assert ==');
});
});

View File

@@ -1,34 +1,31 @@
describe('Expectation', function() {
it('makes custom matchers available to this expectation', function() {
var matchers = {
const matchers = {
toFoo: function() {},
toBar: function() {}
},
expectation;
expectation = jasmineUnderTest.Expectation.factory({
customMatchers: matchers
});
expectation = jasmineUnderTest.Expectation.factory({
customMatchers: matchers
});
expect(expectation.toFoo).toBeDefined();
expect(expectation.toBar).toBeDefined();
});
it('.addCoreMatchers makes matchers available to any expectation', function() {
var coreMatchers = {
toQuux: function() {}
},
expectation;
const coreMatchers = {
toQuux: function() {}
};
jasmineUnderTest.Expectation.addCoreMatchers(coreMatchers);
expectation = jasmineUnderTest.Expectation.factory({});
const expectation = jasmineUnderTest.Expectation.factory({});
expect(expectation.toQuux).toBeDefined();
});
it("wraps matchers's compare functions, passing in matcher dependencies", function() {
var fakeCompare = function() {
const fakeCompare = function() {
return { pass: true };
},
matcherFactory = jasmine
@@ -40,10 +37,9 @@ describe('Expectation', function() {
matchersUtil = {
buildFailureMessage: jasmine.createSpy('buildFailureMessage')
},
addExpectationResult = jasmine.createSpy('addExpectationResult'),
expectation;
addExpectationResult = jasmine.createSpy('addExpectationResult');
expectation = jasmineUnderTest.Expectation.factory({
const expectation = jasmineUnderTest.Expectation.factory({
matchersUtil: matchersUtil,
customMatchers: matchers,
actual: 'an actual',
@@ -56,7 +52,7 @@ describe('Expectation', function() {
});
it("wraps matchers's compare functions, passing the actual and expected", function() {
var fakeCompare = jasmine
const fakeCompare = jasmine
.createSpy('fake-compare')
.and.returnValue({ pass: true }),
matchers = {
@@ -69,10 +65,9 @@ describe('Expectation', function() {
matchersUtil = {
buildFailureMessage: jasmine.createSpy('buildFailureMessage')
},
addExpectationResult = jasmine.createSpy('addExpectationResult'),
expectation;
addExpectationResult = jasmine.createSpy('addExpectationResult');
expectation = jasmineUnderTest.Expectation.factory({
const expectation = jasmineUnderTest.Expectation.factory({
matchersUtil: matchersUtil,
customMatchers: matchers,
actual: 'an actual',
@@ -85,7 +80,7 @@ describe('Expectation', function() {
});
it('reports a passing result to the spec when the comparison passes', function() {
var matchers = {
const matchers = {
toFoo: function() {
return {
compare: function() {
@@ -97,10 +92,9 @@ describe('Expectation', function() {
matchersUtil = {
buildFailureMessage: jasmine.createSpy('buildFailureMessage')
},
addExpectationResult = jasmine.createSpy('addExpectationResult'),
expectation;
addExpectationResult = jasmine.createSpy('addExpectationResult');
expectation = jasmineUnderTest.Expectation.factory({
const expectation = jasmineUnderTest.Expectation.factory({
customMatchers: matchers,
matchersUtil: matchersUtil,
actual: 'an actual',
@@ -121,7 +115,7 @@ describe('Expectation', function() {
});
it('reports a failing result to the spec when the comparison fails', function() {
var matchers = {
const matchers = {
toFoo: function() {
return {
compare: function() {
@@ -135,10 +129,9 @@ describe('Expectation', function() {
return '';
}
},
addExpectationResult = jasmine.createSpy('addExpectationResult'),
expectation;
addExpectationResult = jasmine.createSpy('addExpectationResult');
expectation = jasmineUnderTest.Expectation.factory({
const expectation = jasmineUnderTest.Expectation.factory({
customMatchers: matchers,
matchersUtil: matchersUtil,
actual: 'an actual',
@@ -159,7 +152,7 @@ describe('Expectation', function() {
});
it('reports a failing result and a custom fail message to the spec when the comparison fails', function() {
var matchers = {
const matchers = {
toFoo: function() {
return {
compare: function() {
@@ -171,10 +164,9 @@ describe('Expectation', function() {
};
}
},
addExpectationResult = jasmine.createSpy('addExpectationResult'),
expectation;
addExpectationResult = jasmine.createSpy('addExpectationResult');
expectation = jasmineUnderTest.Expectation.factory({
const expectation = jasmineUnderTest.Expectation.factory({
actual: 'an actual',
customMatchers: matchers,
addExpectationResult: addExpectationResult
@@ -194,7 +186,7 @@ describe('Expectation', function() {
});
it('reports a failing result with a custom fail message function to the spec when the comparison fails', function() {
var matchers = {
const matchers = {
toFoo: function() {
return {
compare: function() {
@@ -208,10 +200,9 @@ describe('Expectation', function() {
};
}
},
addExpectationResult = jasmine.createSpy('addExpectationResult'),
expectation;
addExpectationResult = jasmine.createSpy('addExpectationResult');
expectation = jasmineUnderTest.Expectation.factory({
const expectation = jasmineUnderTest.Expectation.factory({
customMatchers: matchers,
actual: 'an actual',
addExpectationResult: addExpectationResult
@@ -231,7 +222,7 @@ describe('Expectation', function() {
});
it('reports a passing result to the spec when the comparison fails for a negative expectation', function() {
var matchers = {
const matchers = {
toFoo: function() {
return {
compare: function() {
@@ -241,10 +232,9 @@ describe('Expectation', function() {
}
},
addExpectationResult = jasmine.createSpy('addExpectationResult'),
actual = 'an actual',
expectation;
actual = 'an actual';
expectation = jasmineUnderTest.Expectation.factory({
const expectation = jasmineUnderTest.Expectation.factory({
customMatchers: matchers,
actual: 'an actual',
addExpectationResult: addExpectationResult
@@ -264,7 +254,7 @@ describe('Expectation', function() {
});
it('reports a failing result to the spec when the comparison passes for a negative expectation', function() {
var matchers = {
const matchers = {
toFoo: function() {
return {
compare: function() {
@@ -279,10 +269,9 @@ describe('Expectation', function() {
}
},
addExpectationResult = jasmine.createSpy('addExpectationResult'),
actual = 'an actual',
expectation;
actual = 'an actual';
expectation = jasmineUnderTest.Expectation.factory({
const expectation = jasmineUnderTest.Expectation.factory({
customMatchers: matchers,
actual: 'an actual',
matchersUtil: matchersUtil,
@@ -303,7 +292,7 @@ describe('Expectation', function() {
});
it('reports a failing result and a custom fail message to the spec when the comparison passes for a negative expectation', function() {
var matchers = {
const matchers = {
toFoo: function() {
return {
compare: function() {
@@ -316,10 +305,9 @@ describe('Expectation', function() {
}
},
addExpectationResult = jasmine.createSpy('addExpectationResult'),
actual = 'an actual',
expectation;
actual = 'an actual';
expectation = jasmineUnderTest.Expectation.factory({
const expectation = jasmineUnderTest.Expectation.factory({
customMatchers: matchers,
actual: 'an actual',
addExpectationResult: addExpectationResult
@@ -339,7 +327,7 @@ describe('Expectation', function() {
});
it("reports a passing result to the spec when the 'not' comparison passes, given a negativeCompare", function() {
var matchers = {
const matchers = {
toFoo: function() {
return {
compare: function() {
@@ -352,10 +340,9 @@ describe('Expectation', function() {
}
},
addExpectationResult = jasmine.createSpy('addExpectationResult'),
actual = 'an actual',
expectation;
actual = 'an actual';
expectation = jasmineUnderTest.Expectation.factory({
const expectation = jasmineUnderTest.Expectation.factory({
customMatchers: matchers,
actual: 'an actual',
addExpectationResult: addExpectationResult
@@ -375,7 +362,7 @@ describe('Expectation', function() {
});
it("reports a failing result and a custom fail message to the spec when the 'not' comparison fails, given a negativeCompare", function() {
var matchers = {
const matchers = {
toFoo: function() {
return {
compare: function() {
@@ -391,10 +378,9 @@ describe('Expectation', function() {
}
},
addExpectationResult = jasmine.createSpy('addExpectationResult'),
actual = 'an actual',
expectation;
actual = 'an actual';
expectation = jasmineUnderTest.Expectation.factory({
const expectation = jasmineUnderTest.Expectation.factory({
customMatchers: matchers,
actual: 'an actual',
addExpectationResult: addExpectationResult
@@ -414,8 +400,8 @@ describe('Expectation', function() {
});
it('reports a custom error message to the spec', function() {
var customError = new Error('I am a custom error');
var matchers = {
const customError = new Error('I am a custom error');
const matchers = {
toFoo: function() {
return {
compare: function() {
@@ -428,10 +414,9 @@ describe('Expectation', function() {
};
}
},
addExpectationResult = jasmine.createSpy('addExpectationResult'),
expectation;
addExpectationResult = jasmine.createSpy('addExpectationResult');
expectation = jasmineUnderTest.Expectation.factory({
const expectation = jasmineUnderTest.Expectation.factory({
actual: 'an actual',
customMatchers: matchers,
addExpectationResult: addExpectationResult
@@ -451,8 +436,8 @@ describe('Expectation', function() {
});
it("reports a custom message to the spec when a 'not' comparison fails", function() {
var customError = new Error('I am a custom error');
var matchers = {
const customError = new Error('I am a custom error');
const matchers = {
toFoo: function() {
return {
compare: function() {
@@ -465,10 +450,9 @@ describe('Expectation', function() {
};
}
},
addExpectationResult = jasmine.createSpy('addExpectationResult'),
expectation;
addExpectationResult = jasmine.createSpy('addExpectationResult');
expectation = jasmineUnderTest.Expectation.factory({
const expectation = jasmineUnderTest.Expectation.factory({
actual: 'an actual',
customMatchers: matchers,
addExpectationResult: addExpectationResult
@@ -488,8 +472,8 @@ describe('Expectation', function() {
});
it("reports a custom message func to the spec when a 'not' comparison fails", function() {
var customError = new Error('I am a custom error');
var matchers = {
const customError = new Error('I am a custom error');
const matchers = {
toFoo: function() {
return {
compare: function() {
@@ -504,10 +488,9 @@ describe('Expectation', function() {
};
}
},
addExpectationResult = jasmine.createSpy('addExpectationResult'),
expectation;
addExpectationResult = jasmine.createSpy('addExpectationResult');
expectation = jasmineUnderTest.Expectation.factory({
const expectation = jasmineUnderTest.Expectation.factory({
actual: 'an actual',
customMatchers: matchers,
addExpectationResult: addExpectationResult
@@ -528,7 +511,7 @@ describe('Expectation', function() {
describe('#withContext', function() {
it('prepends the context to the generated failure message', function() {
var matchers = {
const matchers = {
toFoo: function() {
return {
compare: function() {
@@ -561,7 +544,7 @@ describe('Expectation', function() {
});
it('prepends the context to a custom failure message', function() {
var matchers = {
const matchers = {
toFoo: function() {
return {
compare: function() {
@@ -588,7 +571,7 @@ describe('Expectation', function() {
});
it('indents a multiline failure message', function() {
var matchers = {
const matchers = {
toFoo: function() {
return {
compare: function() {
@@ -602,19 +585,18 @@ describe('Expectation', function() {
customMatchers: matchers,
actual: 'an actual',
addExpectationResult: addExpectationResult
}),
actualMessage;
});
expectation.withContext('Some context').toFoo('hello');
actualMessage = addExpectationResult.calls.argsFor(0)[1].message;
const actualMessage = addExpectationResult.calls.argsFor(0)[1].message;
expect(actualMessage).toEqual(
'Some context:\n a\n multiline\n message'
);
});
it('prepends the context to a custom failure message from a function', function() {
var matchers = {
const matchers = {
toFoo: function() {
return {
compare: function() {
@@ -646,7 +628,7 @@ describe('Expectation', function() {
});
it('works with #not', function() {
var matchers = {
const matchers = {
toFoo: function() {
return {
compare: function() {
@@ -675,8 +657,8 @@ describe('Expectation', function() {
});
it('works with #not and a custom message', function() {
var customError = new Error('I am a custom error');
var matchers = {
const customError = new Error('I am a custom error');
const matchers = {
toFoo: function() {
return {
compare: function() {

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
describe('JsApiReporter', function() {
it('knows when a full environment is started', function() {
var reporter = new jasmineUnderTest.JsApiReporter({});
const reporter = new jasmineUnderTest.JsApiReporter({});
expect(reporter.started).toBe(false);
expect(reporter.finished).toBe(false);
@@ -12,7 +12,7 @@ describe('JsApiReporter', function() {
});
it('knows when a full environment is done', function() {
var reporter = new jasmineUnderTest.JsApiReporter({});
const reporter = new jasmineUnderTest.JsApiReporter({});
expect(reporter.started).toBe(false);
expect(reporter.finished).toBe(false);
@@ -24,13 +24,13 @@ describe('JsApiReporter', function() {
});
it("defaults to 'loaded' status", function() {
var reporter = new jasmineUnderTest.JsApiReporter({});
const reporter = new jasmineUnderTest.JsApiReporter({});
expect(reporter.status()).toEqual('loaded');
});
it("reports 'started' when Jasmine has started", function() {
var reporter = new jasmineUnderTest.JsApiReporter({});
const reporter = new jasmineUnderTest.JsApiReporter({});
reporter.jasmineStarted();
@@ -38,7 +38,7 @@ describe('JsApiReporter', function() {
});
it("reports 'done' when Jasmine is done", function() {
var reporter = new jasmineUnderTest.JsApiReporter({});
const reporter = new jasmineUnderTest.JsApiReporter({});
reporter.jasmineDone({});
@@ -46,14 +46,14 @@ describe('JsApiReporter', function() {
});
it('tracks a suite', function() {
var reporter = new jasmineUnderTest.JsApiReporter({});
const reporter = new jasmineUnderTest.JsApiReporter({});
reporter.suiteStarted({
id: 123,
description: 'A suite'
});
var suites = reporter.suites();
const suites = reporter.suites();
expect(suites).toEqual({ 123: { id: 123, description: 'A suite' } });
@@ -69,7 +69,7 @@ describe('JsApiReporter', function() {
});
describe('#specResults', function() {
var reporter, specResult1, specResult2;
let reporter, specResult1, specResult2;
beforeEach(function() {
reporter = new jasmineUnderTest.JsApiReporter({});
specResult1 = {
@@ -99,7 +99,7 @@ describe('JsApiReporter', function() {
});
describe('#suiteResults', function() {
var reporter, suiteStarted1, suiteResult1, suiteResult2;
let reporter, suiteStarted1, suiteResult1, suiteResult2;
beforeEach(function() {
reporter = new jasmineUnderTest.JsApiReporter({});
suiteStarted1 = {
@@ -137,7 +137,7 @@ describe('JsApiReporter', function() {
describe('#executionTime', function() {
it('should start the timer when jasmine starts', function() {
var timerSpy = jasmine.createSpyObj('timer', ['start', 'elapsed']),
const timerSpy = jasmine.createSpyObj('timer', ['start', 'elapsed']),
reporter = new jasmineUnderTest.JsApiReporter({
timer: timerSpy
});
@@ -147,7 +147,7 @@ describe('JsApiReporter', function() {
});
it('should return the time it took the specs to run, in ms', function() {
var timerSpy = jasmine.createSpyObj('timer', ['start', 'elapsed']),
const timerSpy = jasmine.createSpyObj('timer', ['start', 'elapsed']),
reporter = new jasmineUnderTest.JsApiReporter({
timer: timerSpy
});
@@ -159,7 +159,7 @@ describe('JsApiReporter', function() {
describe("when the specs haven't finished being run", function() {
it('should return undefined', function() {
var timerSpy = jasmine.createSpyObj('timer', ['start', 'elapsed']),
const timerSpy = jasmine.createSpyObj('timer', ['start', 'elapsed']),
reporter = new jasmineUnderTest.JsApiReporter({
timer: timerSpy
});
@@ -171,7 +171,7 @@ describe('JsApiReporter', function() {
describe('#runDetails', function() {
it('should have details about the run', function() {
var reporter = new jasmineUnderTest.JsApiReporter({});
const reporter = new jasmineUnderTest.JsApiReporter({});
reporter.jasmineDone({ some: { run: 'details' } });
expect(reporter.runDetails).toEqual({ some: { run: 'details' } });
});

View File

@@ -1,6 +1,6 @@
describe('FakeDate', function() {
it('does not fail if no global date is found', function() {
var fakeGlobal = {},
const fakeGlobal = {},
mockDate = new jasmineUnderTest.MockDate(fakeGlobal);
expect(function() {
@@ -11,11 +11,13 @@ describe('FakeDate', function() {
});
it('replaces the global Date when it is installed', function() {
var globalDate = jasmine.createSpy('global Date').and.callFake(function() {
return {
getTime: function() {}
};
}),
const globalDate = jasmine
.createSpy('global Date')
.and.callFake(function() {
return {
getTime: function() {}
};
}),
fakeGlobal = { Date: globalDate },
mockDate = new jasmineUnderTest.MockDate(fakeGlobal);
@@ -26,11 +28,13 @@ describe('FakeDate', function() {
});
it('replaces the global Date on uninstall', function() {
var globalDate = jasmine.createSpy('global Date').and.callFake(function() {
return {
getTime: function() {}
};
}),
const globalDate = jasmine
.createSpy('global Date')
.and.callFake(function() {
return {
getTime: function() {}
};
}),
fakeGlobal = { Date: globalDate },
mockDate = new jasmineUnderTest.MockDate(fakeGlobal);
@@ -41,13 +45,15 @@ describe('FakeDate', function() {
});
it('takes the current time as the base when installing without parameters', function() {
var globalDate = jasmine.createSpy('global Date').and.callFake(function() {
return {
getTime: function() {
return 1000;
}
};
}),
const globalDate = jasmine
.createSpy('global Date')
.and.callFake(function() {
return {
getTime: function() {
return 1000;
}
};
}),
fakeGlobal = { Date: globalDate },
mockDate = new jasmineUnderTest.MockDate(fakeGlobal);
@@ -59,7 +65,7 @@ describe('FakeDate', function() {
});
it('can accept a date as time base when installing', function() {
var fakeGlobal = { Date: Date },
const fakeGlobal = { Date: Date },
mockDate = new jasmineUnderTest.MockDate(fakeGlobal),
baseDate = new Date();
@@ -70,7 +76,7 @@ describe('FakeDate', function() {
});
it('makes real dates', function() {
var fakeGlobal = { Date: Date },
const fakeGlobal = { Date: Date },
mockDate = new jasmineUnderTest.MockDate(fakeGlobal);
mockDate.install();
@@ -79,17 +85,19 @@ describe('FakeDate', function() {
});
it('fakes current time when using Date.now()', function() {
var globalDate = jasmine.createSpy('global Date').and.callFake(function() {
return {
getTime: function() {
return 1000;
}
};
}),
const globalDate = jasmine
.createSpy('global Date')
.and.callFake(function() {
return {
getTime: function() {
return 1000;
}
};
}),
fakeGlobal = { Date: globalDate };
globalDate.now = function() {};
var mockDate = new jasmineUnderTest.MockDate(fakeGlobal);
const mockDate = new jasmineUnderTest.MockDate(fakeGlobal);
mockDate.install();
@@ -97,17 +105,19 @@ describe('FakeDate', function() {
});
it('makes time passes using tick', function() {
var globalDate = jasmine.createSpy('global Date').and.callFake(function() {
return {
getTime: function() {
return 1000;
}
};
}),
const globalDate = jasmine
.createSpy('global Date')
.and.callFake(function() {
return {
getTime: function() {
return 1000;
}
};
}),
fakeGlobal = { Date: globalDate };
globalDate.now = function() {};
var mockDate = new jasmineUnderTest.MockDate(fakeGlobal);
const mockDate = new jasmineUnderTest.MockDate(fakeGlobal);
mockDate.install();
@@ -121,17 +131,19 @@ describe('FakeDate', function() {
});
it('allows to increase 0 milliseconds using tick', function() {
var globalDate = jasmine.createSpy('global Date').and.callFake(function() {
return {
getTime: function() {
return 1000;
}
};
}),
const globalDate = jasmine
.createSpy('global Date')
.and.callFake(function() {
return {
getTime: function() {
return 1000;
}
};
}),
fakeGlobal = { Date: globalDate };
globalDate.now = function() {};
var mockDate = new jasmineUnderTest.MockDate(fakeGlobal);
const mockDate = new jasmineUnderTest.MockDate(fakeGlobal);
mockDate.install();
@@ -143,40 +155,40 @@ describe('FakeDate', function() {
});
it('allows creation of a Date in a different time than the mocked time', function() {
var fakeGlobal = { Date: Date },
const fakeGlobal = { Date: Date },
mockDate = new jasmineUnderTest.MockDate(fakeGlobal);
mockDate.install();
var otherDate = new fakeGlobal.Date(2013, 9, 23, 0, 0, 1, 0);
const otherDate = new fakeGlobal.Date(2013, 9, 23, 0, 0, 1, 0);
expect(otherDate.getTime()).toEqual(
new Date(2013, 9, 23, 0, 0, 1, 0).getTime()
);
});
it("allows creation of a Date that isn't fully specified", function() {
var fakeGlobal = { Date: Date },
const fakeGlobal = { Date: Date },
mockDate = new jasmineUnderTest.MockDate(fakeGlobal);
mockDate.install();
var otherDate = new fakeGlobal.Date(2013, 9, 23);
const otherDate = new fakeGlobal.Date(2013, 9, 23);
expect(otherDate.getTime()).toEqual(new Date(2013, 9, 23).getTime());
});
it('allows creation of a Date with millis', function() {
var fakeGlobal = { Date: Date },
const fakeGlobal = { Date: Date },
mockDate = new jasmineUnderTest.MockDate(fakeGlobal),
now = new Date(2014, 3, 15).getTime();
mockDate.install();
var otherDate = new fakeGlobal.Date(now);
const otherDate = new fakeGlobal.Date(now);
expect(otherDate.getTime()).toEqual(now);
});
it('copies all Date properties to the mocked date', function() {
var fakeGlobal = { Date: Date },
const fakeGlobal = { Date: Date },
mockDate = new jasmineUnderTest.MockDate(fakeGlobal);
mockDate.install();

View File

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

View File

@@ -1,6 +1,6 @@
describe('PrettyPrinter', function() {
it('should wrap strings in single quotes', function() {
var pp = jasmineUnderTest.makePrettyPrinter();
const pp = jasmineUnderTest.makePrettyPrinter();
expect(pp('some string')).toEqual("'some string'");
expect(pp("som' string")).toEqual("'som' string'");
});
@@ -14,7 +14,7 @@ describe('PrettyPrinter', function() {
});
it('should stringify primitives properly', function() {
var pp = jasmineUnderTest.makePrettyPrinter();
const pp = jasmineUnderTest.makePrettyPrinter();
expect(pp(true)).toEqual('true');
expect(pp(false)).toEqual('false');
expect(pp(null)).toEqual('null');
@@ -26,23 +26,23 @@ describe('PrettyPrinter', function() {
describe('stringify sets', function() {
it('should stringify sets properly', function() {
var set = new Set();
const set = new Set();
set.add(1);
set.add(2);
var pp = jasmineUnderTest.makePrettyPrinter();
const pp = jasmineUnderTest.makePrettyPrinter();
expect(pp(set)).toEqual('Set( 1, 2 )');
});
it('should truncate sets with more elements than jasmineUnderTest.MAX_PRETTY_PRINT_ARRAY_LENGTH', function() {
var originalMaxSize = jasmineUnderTest.MAX_PRETTY_PRINT_ARRAY_LENGTH;
const originalMaxSize = jasmineUnderTest.MAX_PRETTY_PRINT_ARRAY_LENGTH;
try {
jasmineUnderTest.MAX_PRETTY_PRINT_ARRAY_LENGTH = 2;
var set = new Set();
const set = new Set();
set.add('a');
set.add('b');
set.add('c');
var pp = jasmineUnderTest.makePrettyPrinter();
const pp = jasmineUnderTest.makePrettyPrinter();
expect(pp(set)).toEqual("Set( 'a', 'b', ... )");
} finally {
jasmineUnderTest.MAX_PRETTY_PRINT_ARRAY_LENGTH = originalMaxSize;
@@ -52,22 +52,22 @@ describe('PrettyPrinter', function() {
describe('stringify maps', function() {
it('should stringify maps properly', function() {
var map = new Map();
const map = new Map();
map.set(1, 2);
var pp = jasmineUnderTest.makePrettyPrinter();
const pp = jasmineUnderTest.makePrettyPrinter();
expect(pp(map)).toEqual('Map( [ 1, 2 ] )');
});
it('should truncate maps with more elements than jasmineUnderTest.MAX_PRETTY_PRINT_ARRAY_LENGTH', function() {
var originalMaxSize = jasmineUnderTest.MAX_PRETTY_PRINT_ARRAY_LENGTH;
const originalMaxSize = jasmineUnderTest.MAX_PRETTY_PRINT_ARRAY_LENGTH;
try {
jasmineUnderTest.MAX_PRETTY_PRINT_ARRAY_LENGTH = 2;
var map = new Map();
const map = new Map();
map.set('a', 1);
map.set('b', 2);
map.set('c', 3);
var pp = jasmineUnderTest.makePrettyPrinter();
const pp = jasmineUnderTest.makePrettyPrinter();
expect(pp(map)).toEqual("Map( [ 'a', 1 ], [ 'b', 2 ], ... )");
} finally {
jasmineUnderTest.MAX_PRETTY_PRINT_ARRAY_LENGTH = originalMaxSize;
@@ -77,17 +77,22 @@ describe('PrettyPrinter', function() {
describe('stringify arrays', function() {
it('should stringify arrays properly', function() {
var pp = jasmineUnderTest.makePrettyPrinter();
const pp = jasmineUnderTest.makePrettyPrinter();
expect(pp([1, 2])).toEqual('[ 1, 2 ]');
expect(pp([1, 'foo', {}, jasmine.undefined, null])).toEqual(
"[ 1, 'foo', Object({ }), undefined, null ]"
);
});
it('includes symbols', function() {
const pp = jasmineUnderTest.makePrettyPrinter();
expect(pp([1, Symbol('foo'), 2])).toEqual('[ 1, Symbol(foo), 2 ]');
});
it('should truncate arrays that are longer than jasmineUnderTest.MAX_PRETTY_PRINT_ARRAY_LENGTH', function() {
var originalMaxLength = jasmineUnderTest.MAX_PRETTY_PRINT_ARRAY_LENGTH;
var array = [1, 2, 3];
var pp = jasmineUnderTest.makePrettyPrinter();
const originalMaxLength = jasmineUnderTest.MAX_PRETTY_PRINT_ARRAY_LENGTH;
const array = [1, 2, 3];
const pp = jasmineUnderTest.makePrettyPrinter();
try {
jasmineUnderTest.MAX_PRETTY_PRINT_ARRAY_LENGTH = 2;
@@ -98,25 +103,25 @@ describe('PrettyPrinter', function() {
});
it('should stringify arrays with properties properly', function() {
var pp = jasmineUnderTest.makePrettyPrinter();
var arr = [1, 2];
const pp = jasmineUnderTest.makePrettyPrinter();
const arr = [1, 2];
arr.foo = 'bar';
arr.baz = {};
expect(pp(arr)).toEqual("[ 1, 2, foo: 'bar', baz: Object({ }) ]");
});
it('should stringify empty arrays with properties properly', function() {
var pp = jasmineUnderTest.makePrettyPrinter();
var empty = [];
const pp = jasmineUnderTest.makePrettyPrinter();
const empty = [];
empty.foo = 'bar';
empty.baz = {};
expect(pp(empty)).toEqual("[ foo: 'bar', baz: Object({ }) ]");
});
it('should stringify long arrays with properties properly', function() {
var pp = jasmineUnderTest.makePrettyPrinter();
var originalMaxLength = jasmineUnderTest.MAX_PRETTY_PRINT_ARRAY_LENGTH;
var long = [1, 2, 3];
const pp = jasmineUnderTest.makePrettyPrinter();
const originalMaxLength = jasmineUnderTest.MAX_PRETTY_PRINT_ARRAY_LENGTH;
const long = [1, 2, 3];
long.foo = 'bar';
long.baz = {};
@@ -131,22 +136,22 @@ describe('PrettyPrinter', function() {
});
it('should indicate circular array references', function() {
var pp = jasmineUnderTest.makePrettyPrinter();
var array1 = [1, 2];
var array2 = [array1];
const pp = jasmineUnderTest.makePrettyPrinter();
const array1 = [1, 2];
const array2 = [array1];
array1.push(array2);
expect(pp(array1)).toEqual('[ 1, 2, [ <circular reference: Array> ] ]');
});
it('should not indicate circular references incorrectly', function() {
var pp = jasmineUnderTest.makePrettyPrinter();
var array = [[1]];
const pp = jasmineUnderTest.makePrettyPrinter();
const array = [[1]];
expect(pp(array)).toEqual('[ [ 1 ] ]');
});
});
it('should stringify objects properly', function() {
var pp = jasmineUnderTest.makePrettyPrinter();
const pp = jasmineUnderTest.makePrettyPrinter();
expect(pp({ foo: 'bar' })).toEqual("Object({ foo: 'bar' })");
expect(
pp({
@@ -159,19 +164,38 @@ describe('PrettyPrinter', function() {
"Object({ foo: 'bar', baz: 3, nullValue: null, undefinedValue: undefined })"
);
expect(pp({ foo: function() {}, bar: [1, 2, 3] })).toEqual(
'Object({ foo: Function, bar: [ 1, 2, 3 ] })'
"Object({ foo: Function 'foo', bar: [ 1, 2, 3 ] })"
);
});
it('includes symbol keys in objects', function() {
const pp = jasmineUnderTest.makePrettyPrinter();
const obj = {};
obj[Symbol('foo')] = 'bar';
expect(pp(obj)).toEqual("Object({ Symbol(foo): 'bar' })");
});
it('stringifies string and symbol keys differently', function() {
const pp = jasmineUnderTest.makePrettyPrinter();
const symObj = {};
const strObj = {};
const k = 'foo';
const v = 'bar';
symObj[Symbol(k)] = v;
strObj[k] = v;
expect(pp(symObj)).not.toEqual(pp(strObj));
});
it('should stringify objects that almost look like DOM nodes', function() {
var pp = jasmineUnderTest.makePrettyPrinter();
const pp = jasmineUnderTest.makePrettyPrinter();
expect(pp({ nodeType: 1 })).toEqual('Object({ nodeType: 1 })');
});
it('should truncate objects with too many keys', function() {
var pp = jasmineUnderTest.makePrettyPrinter();
var originalMaxLength = jasmineUnderTest.MAX_PRETTY_PRINT_ARRAY_LENGTH;
var long = { a: 1, b: 2, c: 3 };
const pp = jasmineUnderTest.makePrettyPrinter();
const originalMaxLength = jasmineUnderTest.MAX_PRETTY_PRINT_ARRAY_LENGTH;
const long = { a: 1, b: 2, c: 3 };
try {
jasmineUnderTest.MAX_PRETTY_PRINT_ARRAY_LENGTH = 2;
@@ -182,7 +206,7 @@ describe('PrettyPrinter', function() {
});
function withMaxChars(maxChars, fn) {
var originalMaxChars = jasmineUnderTest.MAX_PRETTY_PRINT_CHARS;
const originalMaxChars = jasmineUnderTest.MAX_PRETTY_PRINT_CHARS;
try {
jasmineUnderTest.MAX_PRETTY_PRINT_CHARS = maxChars;
@@ -193,8 +217,8 @@ describe('PrettyPrinter', function() {
}
it('should truncate outputs that are too long', function() {
var pp = jasmineUnderTest.makePrettyPrinter();
var big = [{ a: 1, b: 'a long string' }, {}];
const pp = jasmineUnderTest.makePrettyPrinter();
const big = [{ a: 1, b: 'a long string' }, {}];
withMaxChars(34, function() {
expect(pp(big)).toEqual("[ Object({ a: 1, b: 'a long st ...");
@@ -202,7 +226,7 @@ describe('PrettyPrinter', function() {
});
it('should not serialize more objects after hitting MAX_PRETTY_PRINT_CHARS', function() {
var a = {
const a = {
jasmineToString: function() {
return 'object a';
}
@@ -232,25 +256,25 @@ describe('PrettyPrinter', function() {
});
it("should print 'null' as the constructor of an object with its own constructor property", function() {
var pp = jasmineUnderTest.makePrettyPrinter();
const pp = jasmineUnderTest.makePrettyPrinter();
expect(pp({ constructor: function() {} })).toContain('null({');
expect(pp({ constructor: 'foo' })).toContain('null({');
});
it('should not include inherited properties when stringifying an object', function() {
var pp = jasmineUnderTest.makePrettyPrinter();
var SomeClass = function SomeClass() {};
const pp = jasmineUnderTest.makePrettyPrinter();
const SomeClass = function SomeClass() {};
SomeClass.prototype.foo = 'inherited foo';
var instance = new SomeClass();
const instance = new SomeClass();
instance.bar = 'my own bar';
expect(pp(instance)).toEqual("SomeClass({ bar: 'my own bar' })");
});
it('should not recurse objects and arrays more deeply than jasmineUnderTest.MAX_PRETTY_PRINT_DEPTH', function() {
var pp = jasmineUnderTest.makePrettyPrinter();
var originalMaxDepth = jasmineUnderTest.MAX_PRETTY_PRINT_DEPTH;
var nestedObject = { level1: { level2: { level3: { level4: 'leaf' } } } };
var nestedArray = [1, [2, [3, [4, 'leaf']]]];
const pp = jasmineUnderTest.makePrettyPrinter();
const originalMaxDepth = jasmineUnderTest.MAX_PRETTY_PRINT_DEPTH;
const nestedObject = { level1: { level2: { level3: { level4: 'leaf' } } } };
const nestedArray = [1, [2, [3, [4, 'leaf']]]];
try {
jasmineUnderTest.MAX_PRETTY_PRINT_DEPTH = 2;
@@ -276,8 +300,8 @@ describe('PrettyPrinter', function() {
});
it('should stringify immutable circular objects', function() {
var pp = jasmineUnderTest.makePrettyPrinter();
var frozenObject = { foo: { bar: 'baz' } };
const pp = jasmineUnderTest.makePrettyPrinter();
let frozenObject = { foo: { bar: 'baz' } };
frozenObject.circular = frozenObject;
frozenObject = Object.freeze(frozenObject);
expect(pp(frozenObject)).toEqual(
@@ -286,52 +310,52 @@ describe('PrettyPrinter', function() {
});
it('should stringify RegExp objects properly', function() {
var pp = jasmineUnderTest.makePrettyPrinter();
const pp = jasmineUnderTest.makePrettyPrinter();
expect(pp(/x|y|z/)).toEqual('/x|y|z/');
});
it('should indicate circular object references', function() {
var pp = jasmineUnderTest.makePrettyPrinter();
var sampleValue = { foo: 'hello' };
const pp = jasmineUnderTest.makePrettyPrinter();
const sampleValue = { foo: 'hello' };
sampleValue.nested = sampleValue;
expect(pp(sampleValue)).toEqual(
"Object({ foo: 'hello', nested: <circular reference: Object> })"
);
});
it('should indicate getters on objects as such', function() {
var pp = jasmineUnderTest.makePrettyPrinter();
var sampleValue = {
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' })"
);
});
it('should not do HTML escaping of strings', function() {
var pp = jasmineUnderTest.makePrettyPrinter();
const pp = jasmineUnderTest.makePrettyPrinter();
expect(pp('some <b>html string</b> &', false)).toEqual(
"'some <b>html string</b> &'"
);
});
it('should abbreviate the global (usually window) object', function() {
var pp = jasmineUnderTest.makePrettyPrinter();
const pp = jasmineUnderTest.makePrettyPrinter();
expect(pp(jasmine.getGlobal())).toEqual('<global>');
});
it('should stringify Date objects properly', function() {
var pp = jasmineUnderTest.makePrettyPrinter();
var now = new Date();
const pp = jasmineUnderTest.makePrettyPrinter();
const now = new Date();
expect(pp(now)).toEqual('Date(' + now.toString() + ')');
});
describe('with a spy object', function() {
var env, pp;
let env, pp;
beforeEach(function() {
env = new jasmineUnderTest.Env();
@@ -343,11 +367,11 @@ describe('PrettyPrinter', function() {
});
it('should stringify spy objects properly', function() {
var TestObject = {
const TestObject = {
someFunction: function() {}
};
var spyRegistry = new jasmineUnderTest.SpyRegistry({
const spyRegistry = new jasmineUnderTest.SpyRegistry({
currentSpies: function() {
return [];
},
@@ -363,13 +387,13 @@ describe('PrettyPrinter', function() {
});
it('should stringify spyOn toString properly', function() {
var TestObject = {
const TestObject = {
someFunction: function() {}
},
env = new jasmineUnderTest.Env(),
pp = jasmineUnderTest.makePrettyPrinter();
var spyRegistry = new jasmineUnderTest.SpyRegistry({
const spyRegistry = new jasmineUnderTest.SpyRegistry({
currentSpies: function() {
return [];
},
@@ -379,15 +403,15 @@ describe('PrettyPrinter', function() {
});
spyRegistry.spyOn(TestObject, 'toString');
var testSpyObj = env.createSpyObj('TheClassName', ['toString']);
const testSpyObj = env.createSpyObj('TheClassName', ['toString']);
expect(pp(testSpyObj)).toEqual('spy on TheClassName.toString');
});
});
it('should stringify objects that implement jasmineToString', function() {
var pp = jasmineUnderTest.makePrettyPrinter();
var obj = {
const pp = jasmineUnderTest.makePrettyPrinter();
const obj = {
jasmineToString: function() {
return 'strung';
}
@@ -397,8 +421,8 @@ describe('PrettyPrinter', function() {
});
it('should pass itself to jasmineToString', function() {
var pp = jasmineUnderTest.makePrettyPrinter([]);
var obj = {
const pp = jasmineUnderTest.makePrettyPrinter([]);
const obj = {
jasmineToString: jasmine.createSpy('jasmineToString').and.returnValue('')
};
@@ -407,8 +431,8 @@ describe('PrettyPrinter', function() {
});
it('should stringify objects that implement custom toString', function() {
var pp = jasmineUnderTest.makePrettyPrinter();
var obj = {
const pp = jasmineUnderTest.makePrettyPrinter();
const obj = {
toString: function() {
return 'my toString';
}
@@ -418,7 +442,7 @@ describe('PrettyPrinter', function() {
// Simulate object from another global context (e.g. an iframe or Web Worker) that does not actually have a custom
// toString despite obj.toString !== Object.prototype.toString
var objFromOtherContext = {
const objFromOtherContext = {
foo: 'bar',
toString: function() {
return Object.prototype.toString.call(this);
@@ -426,13 +450,13 @@ describe('PrettyPrinter', function() {
};
expect(pp(objFromOtherContext)).toEqual(
"Object({ foo: 'bar', toString: Function })"
"Object({ foo: 'bar', toString: Function 'toString' })"
);
});
it("should stringify objects have have a toString that isn't a function", function() {
var pp = jasmineUnderTest.makePrettyPrinter();
var obj = {
const pp = jasmineUnderTest.makePrettyPrinter();
const obj = {
toString: 'foo'
};
@@ -440,30 +464,41 @@ describe('PrettyPrinter', function() {
});
it('should stringify objects from anonymous constructors with custom toString', function() {
var pp = jasmineUnderTest.makePrettyPrinter();
var MyAnonymousConstructor = (function() {
const pp = jasmineUnderTest.makePrettyPrinter();
const MyAnonymousConstructor = (function() {
return function() {};
})();
MyAnonymousConstructor.toString = function() {
return '';
};
var a = new MyAnonymousConstructor();
const a = new MyAnonymousConstructor();
expect(pp(a)).toEqual('<anonymous>({ })');
});
it('stringifies functions with names', function() {
const pp = jasmineUnderTest.makePrettyPrinter();
expect(pp(foo)).toEqual("Function 'foo'");
function foo() {}
});
it('stringifies functions without names', function() {
const pp = jasmineUnderTest.makePrettyPrinter();
expect(pp(function() {})).toEqual('Function');
});
it('should handle objects with null prototype', function() {
var pp = jasmineUnderTest.makePrettyPrinter();
var obj = Object.create(null);
const pp = jasmineUnderTest.makePrettyPrinter();
const obj = Object.create(null);
obj.foo = 'bar';
expect(pp(obj)).toEqual("null({ foo: 'bar' })");
});
it('should gracefully handle objects with invalid toString implementations', function() {
var pp = jasmineUnderTest.makePrettyPrinter();
var obj = {
const pp = jasmineUnderTest.makePrettyPrinter();
const obj = {
foo: {
toString: function() {
// Invalid: toString returning a number
@@ -499,8 +534,8 @@ describe('PrettyPrinter', function() {
describe('Custom object formatters', function() {
it('should use the first custom object formatter that does not return undefined', function() {
var customObjectFormatters = [
function(obj) {
const customObjectFormatters = [
function() {
return undefined;
},
function(obj) {
@@ -517,8 +552,8 @@ describe('PrettyPrinter', function() {
});
it('should fall back to built in logic if all custom object formatters return undefined', function() {
var customObjectFormatters = [
function(obj) {
const customObjectFormatters = [
function() {
return undefined;
}
],
@@ -531,8 +566,8 @@ describe('PrettyPrinter', function() {
describe('#customFormat_', function() {
it('should use the first custom object formatter that does not return undefined', function() {
var customObjectFormatters = [
function(obj) {
const customObjectFormatters = [
function() {
return undefined;
},
function(obj) {
@@ -549,8 +584,8 @@ describe('PrettyPrinter', function() {
});
it('should return undefined if all custom object formatters return undefined', function() {
var customObjectFormatters = [
function(obj) {
const customObjectFormatters = [
function() {
return undefined;
}
],

View File

@@ -1,6 +1,22 @@
describe('QueueRunner', function() {
it('validates that queueableFns are truthy', function() {
expect(function() {
new jasmineUnderTest.QueueRunner({
queueableFns: [undefined]
});
}).toThrowError('Received a falsy queueableFn');
});
it('validates that queueableFns have fn properties', function() {
expect(function() {
new jasmineUnderTest.QueueRunner({
queueableFns: [{ fn: undefined }]
});
}).toThrowError('Received a queueableFn with no fn');
});
it("runs all the functions it's passed", function() {
var calls = [],
const calls = [],
queueableFn1 = { fn: jasmine.createSpy('fn1') },
queueableFn2 = { fn: jasmine.createSpy('fn2') },
queueRunner = new jasmineUnderTest.QueueRunner({
@@ -19,9 +35,10 @@ describe('QueueRunner', function() {
});
it("calls each function with a consistent 'this'-- an empty object", function() {
var queueableFn1 = { fn: jasmine.createSpy('fn1') },
queueableFn2 = { fn: jasmine.createSpy('fn2') },
queueableFn3 = {
const queueableFn1 = { fn: jasmine.createSpy('fn1') };
const queueableFn2 = { fn: jasmine.createSpy('fn2') };
let asyncContext;
const queueableFn3 = {
fn: function(done) {
asyncContext = this;
done();
@@ -29,12 +46,11 @@ describe('QueueRunner', function() {
},
queueRunner = new jasmineUnderTest.QueueRunner({
queueableFns: [queueableFn1, queueableFn2, queueableFn3]
}),
asyncContext;
});
queueRunner.execute();
var context = queueableFn1.fn.calls.first().object;
const context = queueableFn1.fn.calls.first().object;
expect(context).toEqual(new jasmineUnderTest.UserContext());
expect(queueableFn2.fn.calls.first().object).toBe(context);
expect(asyncContext).toBe(context);
@@ -53,7 +69,7 @@ describe('QueueRunner', function() {
//TODO: it would be nice if spy arity could match the fake, so we could do something like:
//createSpy('asyncfn').and.callFake(function(done) {});
var onComplete = jasmine.createSpy('onComplete'),
const onComplete = jasmine.createSpy('onComplete'),
beforeCallback = jasmine.createSpy('beforeCallback'),
fnCallback = jasmine.createSpy('fnCallback'),
afterCallback = jasmine.createSpy('afterCallback'),
@@ -104,7 +120,7 @@ describe('QueueRunner', function() {
});
it('explicitly fails an async function with a provided fail function and moves to the next function', function() {
var queueableFn1 = {
const queueableFn1 = {
fn: function(done) {
setTimeout(function() {
done.fail('foo');
@@ -131,7 +147,7 @@ describe('QueueRunner', function() {
describe('When next is called with an argument', function() {
it('explicitly fails and moves to the next function', function() {
var err = 'anything except undefined',
const err = 'anything except undefined',
queueableFn1 = {
fn: function(done) {
setTimeout(function() {
@@ -165,7 +181,7 @@ describe('QueueRunner', function() {
// except on a major release and with a deprecation warning in
// advance.
it('explicitly fails and moves to the next function', function(done) {
var err = new Error('foo'),
const err = new Error('foo'),
queueableFn1 = {
fn: function() {
return Promise.resolve(err);
@@ -185,43 +201,20 @@ describe('QueueRunner', function() {
queueRunner.execute();
});
it('does not log a deprecation', function(done) {
var err = new Error('foo'),
queueableFn1 = {
fn: function() {
return Promise.resolve(err);
}
},
deprecated = jasmine.createSpy('deprecated'),
queueRunner = new jasmineUnderTest.QueueRunner({
queueableFns: [queueableFn1],
deprecated: deprecated,
onComplete: function() {
expect(deprecated).not.toHaveBeenCalled();
done();
}
});
queueRunner.execute();
});
});
describe('and the argument is not an Error', function() {
it('does not log a deprecation or report a failure', function(done) {
var queueableFn1 = {
it('does not report a failure', function(done) {
const queueableFn1 = {
fn: function() {
return Promise.resolve('not an error');
}
},
failFn = jasmine.createSpy('fail'),
deprecated = jasmine.createSpy('deprecated'),
queueRunner = new jasmineUnderTest.QueueRunner({
queueableFns: [queueableFn1],
deprecated: deprecated,
fail: failFn,
onComplete: function() {
expect(deprecated).not.toHaveBeenCalled();
expect(failFn).not.toHaveBeenCalled();
done();
}
@@ -234,7 +227,7 @@ describe('QueueRunner', function() {
});
it('does not cause an explicit fail if execution is being stopped', function() {
var err = new jasmineUnderTest.StopExecutionError('foo'),
const err = new jasmineUnderTest.StopExecutionError('foo'),
queueableFn1 = {
fn: function(done) {
setTimeout(function() {
@@ -261,7 +254,7 @@ describe('QueueRunner', function() {
});
it("sets a timeout if requested for asynchronous functions so they don't go on forever", function() {
var timeout = 3,
const timeout = 3,
beforeFn = { fn: function(done) {}, type: 'before', timeout: timeout },
queueableFn = { fn: jasmine.createSpy('fn'), type: 'queueable' },
onComplete = jasmine.createSpy('onComplete'),
@@ -283,22 +276,22 @@ describe('QueueRunner', function() {
});
it('does not call onMultipleDone if an asynchrnous function completes after timing out', function() {
var timeout = 3,
queueableFn = {
fn: function(done) {
queueableFnDone = done;
},
type: 'queueable',
timeout: timeout
const timeout = 3;
let queueableFnDone;
const queueableFn = {
fn: function(done) {
queueableFnDone = done;
},
onComplete = jasmine.createSpy('onComplete'),
onMultipleDone = jasmine.createSpy('onMultipleDone'),
queueRunner = new jasmineUnderTest.QueueRunner({
queueableFns: [queueableFn],
onComplete: onComplete,
onMultipleDone: onMultipleDone
}),
queueableFnDone;
type: 'queueable',
timeout: timeout
};
const onComplete = jasmine.createSpy('onComplete');
const onMultipleDone = jasmine.createSpy('onMultipleDone');
const queueRunner = new jasmineUnderTest.QueueRunner({
queueableFns: [queueableFn],
onComplete: onComplete,
onMultipleDone: onMultipleDone
});
queueRunner.execute();
jasmine.clock().tick(timeout);
@@ -309,7 +302,7 @@ describe('QueueRunner', function() {
});
it('by default does not set a timeout for asynchronous functions', function() {
var beforeFn = { fn: function(done) {} },
const beforeFn = { fn: function(done) {} },
queueableFn = { fn: jasmine.createSpy('fn') },
onComplete = jasmine.createSpy('onComplete'),
onException = jasmine.createSpy('onException'),
@@ -330,7 +323,7 @@ describe('QueueRunner', function() {
});
it('clears the timeout when an async function throws an exception, to prevent additional exception reporting', function() {
var queueableFn = {
const queueableFn = {
fn: function(done) {
throw new Error('error!');
}
@@ -353,7 +346,7 @@ describe('QueueRunner', function() {
});
it('clears the timeout when the done callback is called', function() {
var queueableFn = {
const queueableFn = {
fn: function(done) {
done();
}
@@ -376,7 +369,7 @@ describe('QueueRunner', function() {
});
it('only moves to the next spec the first time you call done', function() {
var queueableFn = {
const queueableFn = {
fn: function(done) {
done();
done();
@@ -396,44 +389,39 @@ describe('QueueRunner', function() {
});
it('does not move to the next spec if done is called after an exception has ended the spec', function() {
var queueableFn = {
const queueableFn = {
fn: function(done) {
setTimeout(done, 1);
throw new Error('error!');
}
},
nextQueueableFn = { fn: jasmine.createSpy('nextFn') },
deprecated = jasmine.createSpy('deprecated'),
queueRunner = new jasmineUnderTest.QueueRunner({
deprecated: deprecated,
queueableFns: [queueableFn, nextQueueableFn]
});
queueRunner.execute();
jasmine.clock().tick(1);
expect(nextQueueableFn.fn.calls.count()).toEqual(1);
// Don't issue a deprecation. The error already tells the user that
// something went wrong.
expect(deprecated).not.toHaveBeenCalled();
});
it('should return a null when you call done', function() {
// Some promises want handlers to return anything but undefined to help catch "forgotten returns".
var doneReturn,
queueableFn = {
fn: function(done) {
doneReturn = done();
}
},
queueRunner = new jasmineUnderTest.QueueRunner({
queueableFns: [queueableFn]
});
let doneReturn;
const queueableFn = {
fn: function(done) {
doneReturn = done();
}
};
const queueRunner = new jasmineUnderTest.QueueRunner({
queueableFns: [queueableFn]
});
queueRunner.execute();
expect(doneReturn).toBe(null);
});
it('continues running functions when an exception is thrown in async code without timing out', function() {
var queueableFn = {
const queueableFn = {
fn: function(done) {
throwAsync();
},
@@ -483,8 +471,33 @@ describe('QueueRunner', function() {
expect(nextQueueableFn.fn).toHaveBeenCalled();
});
it('handles a global error event with a message but no error', function() {
const queueableFn = {
fn: function(done) {
const currentHandler = globalErrors.pushListener.calls.mostRecent()
.args[0];
currentHandler(undefined, { message: 'nope' });
},
timeout: 1
};
const onException = jasmine.createSpy('onException');
const globalErrors = {
pushListener: jasmine.createSpy('pushListener'),
popListener: jasmine.createSpy('popListener')
};
const queueRunner = new jasmineUnderTest.QueueRunner({
queueableFns: [queueableFn],
onException: onException,
globalErrors: globalErrors
});
queueRunner.execute();
expect(onException).toHaveBeenCalledWith('nope');
});
it('handles exceptions thrown while waiting for the stack to clear', function() {
var queueableFn = {
const queueableFn = {
fn: function(done) {
done();
}
@@ -516,6 +529,40 @@ describe('QueueRunner', function() {
clearStack.calls.argsFor(0)[0]();
expect(onException).toHaveBeenCalledWith(error);
});
it('handles a global error event with no error while waiting for the stack to clear', function() {
const queueableFn = {
fn: function(done) {
done();
}
};
const errorListeners = [];
const globalErrors = {
pushListener: function(f) {
errorListeners.push(f);
},
popListener: function() {
errorListeners.pop();
}
};
const clearStack = jasmine.createSpy('clearStack');
const onException = jasmine.createSpy('onException');
const queueRunner = new jasmineUnderTest.QueueRunner({
queueableFns: [queueableFn],
globalErrors: globalErrors,
clearStack: clearStack,
onException: onException
});
queueRunner.execute();
jasmine.clock().tick();
expect(clearStack).toHaveBeenCalled();
expect(errorListeners.length).toEqual(1);
errorListeners[0](undefined, { message: 'nope' });
clearStack.calls.argsFor(0)[0]();
expect(onException).toHaveBeenCalledWith('nope');
});
});
describe('with a function that returns a promise', function() {
@@ -535,7 +582,7 @@ describe('QueueRunner', function() {
});
it('runs the function asynchronously, advancing once the promise is settled', function() {
var onComplete = jasmine.createSpy('onComplete'),
const onComplete = jasmine.createSpy('onComplete'),
fnCallback = jasmine.createSpy('fnCallback'),
p1 = new StubPromise(),
p2 = new StubPromise(),
@@ -576,7 +623,7 @@ describe('QueueRunner', function() {
});
it('handles a rejected promise like an unhandled exception', function() {
var promise = new StubPromise(),
const promise = new StubPromise(),
queueableFn1 = {
fn: function() {
setTimeout(function() {
@@ -604,7 +651,7 @@ describe('QueueRunner', function() {
});
it('issues an error if the function also takes a parameter', function() {
var queueableFn = {
const queueableFn = {
fn: function(done) {
return new StubPromise();
}
@@ -618,17 +665,19 @@ describe('QueueRunner', function() {
queueRunner.execute();
expect(onException).toHaveBeenCalledWith(
'An asynchronous ' +
'before/it/after function took a done callback but also returned a ' +
'promise. ' +
'Either remove the done callback (recommended) or change the function ' +
'to not return a promise.'
new Error(
'An asynchronous ' +
'before/it/after function took a done callback but also returned a ' +
'promise. ' +
'Either remove the done callback (recommended) or change the function ' +
'to not return a promise.'
)
);
});
it('issues a more specific error if the function is `async`', function() {
eval('var fn = async function(done){};');
var onException = jasmine.createSpy('onException'),
async function fn(done) {}
const onException = jasmine.createSpy('onException'),
queueRunner = new jasmineUnderTest.QueueRunner({
queueableFns: [{ fn: fn }],
onException: onException
@@ -637,42 +686,31 @@ describe('QueueRunner', function() {
queueRunner.execute();
expect(onException).toHaveBeenCalledWith(
'An asynchronous ' +
'before/it/after function was defined with the async keyword but ' +
'also took a done callback. Either remove the done callback ' +
'(recommended) or remove the async keyword.'
new Error(
'An asynchronous ' +
'before/it/after function was defined with the async keyword but ' +
'also took a done callback. Either remove the done callback ' +
'(recommended) or remove the async keyword.'
)
);
});
});
it('passes the error instance to exception handlers in HTML browsers', function() {
var error = new Error('fake error'),
it('passes final errors to exception handlers', function() {
const error = new Error('fake error'),
onExceptionCallback = jasmine.createSpy('on exception callback'),
queueRunner = new jasmineUnderTest.QueueRunner({
onException: onExceptionCallback
});
queueRunner.execute();
queueRunner.handleFinalError(error.message, 'fake.js', 1, 1, error);
queueRunner.handleFinalError(error);
expect(onExceptionCallback).toHaveBeenCalledWith(error);
});
it('passes the first argument to exception handlers for compatibility', function() {
var error = new Error('fake error'),
onExceptionCallback = jasmine.createSpy('on exception callback'),
queueRunner = new jasmineUnderTest.QueueRunner({
onException: onExceptionCallback
});
queueRunner.execute();
queueRunner.handleFinalError(error.message);
expect(onExceptionCallback).toHaveBeenCalledWith(error.message);
});
it('calls exception handlers when an exception is thrown in a fn', function() {
var queueableFn = {
const queueableFn = {
type: 'queueable',
fn: function() {
throw new Error('fake error');
@@ -690,7 +728,7 @@ describe('QueueRunner', function() {
});
it('continues running the functions even after an exception is thrown in an async spec', function() {
var queueableFn = {
const queueableFn = {
fn: function(done) {
throw new Error('error');
}
@@ -767,7 +805,7 @@ describe('QueueRunner', function() {
describe('When configured to complete on first error', function() {
it('skips to cleanup functions on the first exception', function() {
var queueableFn = {
const queueableFn = {
fn: function() {
throw new Error('error');
}
@@ -793,7 +831,7 @@ describe('QueueRunner', function() {
});
it('does not skip when a cleanup function throws', function() {
var queueableFn = { fn: function() {} },
const queueableFn = { fn: function() {} },
cleanupFn1 = {
fn: function() {
throw new Error('error');
@@ -823,27 +861,30 @@ describe('QueueRunner', function() {
});
it('skips to cleanup functions once the fn completes after an unhandled exception', function() {
var errorListeners = [],
queueableFn = {
fn: function(done) {
queueableFnDone = done;
const errorListeners = [];
let queueableFnDone;
const queueableFn = {
fn: function(done) {
queueableFnDone = done;
}
};
const nextQueueableFn = { fn: jasmine.createSpy('nextFunction') };
const cleanupFn = {
fn: jasmine.createSpy('cleanup'),
type: 'specCleanup'
};
const queueRunner = new jasmineUnderTest.QueueRunner({
globalErrors: {
pushListener: function(f) {
errorListeners.push(f);
},
popListener: function() {
errorListeners.pop();
}
},
nextQueueableFn = { fn: jasmine.createSpy('nextFunction') },
cleanupFn = { fn: jasmine.createSpy('cleanup'), type: 'specCleanup' },
queueRunner = new jasmineUnderTest.QueueRunner({
globalErrors: {
pushListener: function(f) {
errorListeners.push(f);
},
popListener: function() {
errorListeners.pop();
}
},
queueableFns: [queueableFn, nextQueueableFn, cleanupFn],
SkipPolicy: jasmineUnderTest.CompleteOnFirstErrorSkipPolicy
}),
queueableFnDone;
queueableFns: [queueableFn, nextQueueableFn, cleanupFn],
SkipPolicy: jasmineUnderTest.CompleteOnFirstErrorSkipPolicy
});
queueRunner.execute();
errorListeners[errorListeners.length - 1](new Error('error'));
@@ -854,7 +895,7 @@ describe('QueueRunner', function() {
});
it('skips to cleanup functions when next.fail is called', function() {
var queueableFn = {
const queueableFn = {
fn: function(done) {
done.fail('nope');
}
@@ -873,7 +914,7 @@ describe('QueueRunner', function() {
});
it('skips to cleanup functions when next is called with an Error', function() {
var queueableFn = {
const queueableFn = {
fn: function(done) {
done(new Error('nope'));
}
@@ -897,7 +938,7 @@ describe('QueueRunner', function() {
});
it('calls a provided complete callback when done', function() {
var queueableFn = { fn: jasmine.createSpy('fn') },
const queueableFn = { fn: jasmine.createSpy('fn') },
completeCallback = jasmine.createSpy('completeCallback'),
queueRunner = new jasmineUnderTest.QueueRunner({
queueableFns: [queueableFn],
@@ -919,7 +960,7 @@ describe('QueueRunner', function() {
});
it('calls a provided stack clearing function when done', function() {
var asyncFn = {
const asyncFn = {
fn: function(done) {
done();
}
@@ -948,16 +989,16 @@ describe('QueueRunner', function() {
describe('when user context has not been defined', function() {
beforeEach(function() {
var fn;
const fn = jasmine.createSpy('fn1');
this.fn = fn = jasmine.createSpy('fn1');
this.fn = fn;
this.queueRunner = new jasmineUnderTest.QueueRunner({
queueableFns: [{ fn: fn }]
});
});
it('runs the functions on the scope of a UserContext', function() {
var context;
let context;
this.fn.and.callFake(function() {
context = this;
});
@@ -970,9 +1011,10 @@ describe('QueueRunner', function() {
describe('when user context has been defined', function() {
beforeEach(function() {
var fn, context;
const fn = jasmine.createSpy('fn1');
let context;
this.fn = fn = jasmine.createSpy('fn1');
this.fn = fn;
this.context = context = new jasmineUnderTest.UserContext();
this.queueRunner = new jasmineUnderTest.QueueRunner({
queueableFns: [{ fn: fn }],
@@ -981,7 +1023,7 @@ describe('QueueRunner', function() {
});
it('runs the functions on the scope of a UserContext', function() {
var context;
let context;
this.fn.and.callFake(function() {
context = this;
});

View File

@@ -1,6 +1,6 @@
describe('ReportDispatcher', function() {
it('builds an interface of requested methods', function() {
var dispatcher = new jasmineUnderTest.ReportDispatcher([
const dispatcher = new jasmineUnderTest.ReportDispatcher([
'foo',
'bar',
'baz'
@@ -12,21 +12,20 @@ describe('ReportDispatcher', function() {
});
it('dispatches requested methods to added reporters', function() {
var queueRunnerFactory = jasmine.createSpy('queueRunner'),
const runQueue = jasmine.createSpy('runQueue'),
dispatcher = new jasmineUnderTest.ReportDispatcher(
['foo', 'bar'],
queueRunnerFactory
runQueue
),
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(
expect(runQueue).toHaveBeenCalledWith(
jasmine.objectContaining({
queueableFns: [
{ fn: jasmine.any(Function) },
@@ -36,7 +35,7 @@ describe('ReportDispatcher', function() {
})
);
var fns = queueRunnerFactory.calls.mostRecent().args[0].queueableFns;
let fns = runQueue.calls.mostRecent().args[0].queueableFns;
fns[0].fn();
expect(reporter.foo).toHaveBeenCalledWith(123, 456);
expect(reporter.foo.calls.mostRecent().object).toBe(reporter);
@@ -45,11 +44,11 @@ describe('ReportDispatcher', function() {
expect(anotherReporter.foo).toHaveBeenCalledWith(123, 456);
expect(anotherReporter.foo.calls.mostRecent().object).toBe(anotherReporter);
queueRunnerFactory.calls.reset();
runQueue.calls.reset();
dispatcher.bar('a', 'b', completeCallback);
dispatcher.bar('a', 'b');
expect(queueRunnerFactory).toHaveBeenCalledWith(
expect(runQueue).toHaveBeenCalledWith(
jasmine.objectContaining({
queueableFns: [
{ fn: jasmine.any(Function) },
@@ -59,7 +58,7 @@ describe('ReportDispatcher', function() {
})
);
fns = queueRunnerFactory.calls.mostRecent().args[0].queueableFns;
fns = runQueue.calls.mostRecent().args[0].queueableFns;
fns[0].fn();
expect(reporter.bar).toHaveBeenCalledWith('a', 'b');
@@ -68,17 +67,14 @@ describe('ReportDispatcher', function() {
});
it("does not dispatch to a reporter if the reporter doesn't accept the method", function() {
var queueRunnerFactory = jasmine.createSpy('queueRunner'),
dispatcher = new jasmineUnderTest.ReportDispatcher(
['foo'],
queueRunnerFactory
),
const runQueue = jasmine.createSpy('runQueue'),
dispatcher = new jasmineUnderTest.ReportDispatcher(['foo'], runQueue),
reporter = jasmine.createSpyObj('reporter', ['baz']);
dispatcher.addReporter(reporter);
dispatcher.foo(123, 456);
expect(queueRunnerFactory).toHaveBeenCalledWith(
expect(runQueue).toHaveBeenCalledWith(
jasmine.objectContaining({
queueableFns: []
})
@@ -86,91 +82,88 @@ describe('ReportDispatcher', function() {
});
it("allows providing a fallback reporter in case there's no other reporter", function() {
var queueRunnerFactory = jasmine.createSpy('queueRunner'),
const runQueue = jasmine.createSpy('runQueue'),
dispatcher = new jasmineUnderTest.ReportDispatcher(
['foo', 'bar'],
queueRunnerFactory
runQueue
),
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(
expect(runQueue).toHaveBeenCalledWith(
jasmine.objectContaining({
queueableFns: [{ fn: jasmine.any(Function) }],
isReporter: true
})
);
var fns = queueRunnerFactory.calls.mostRecent().args[0].queueableFns;
const fns = runQueue.calls.mostRecent().args[0].queueableFns;
fns[0].fn();
expect(reporter.foo).toHaveBeenCalledWith(123, 456);
});
it('does not call fallback reporting methods when another reporter is provided', function() {
var queueRunnerFactory = jasmine.createSpy('queueRunner'),
const runQueue = jasmine.createSpy('runQueue'),
dispatcher = new jasmineUnderTest.ReportDispatcher(
['foo', 'bar'],
queueRunnerFactory
runQueue
),
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(
expect(runQueue).toHaveBeenCalledWith(
jasmine.objectContaining({
queueableFns: [{ fn: jasmine.any(Function) }],
isReporter: true
})
);
var fns = queueRunnerFactory.calls.mostRecent().args[0].queueableFns;
const fns = runQueue.calls.mostRecent().args[0].queueableFns;
fns[0].fn();
expect(reporter.foo).toHaveBeenCalledWith(123, 456);
expect(fallbackReporter.foo).not.toHaveBeenCalledWith(123, 456);
});
it('allows registered reporters to be cleared', function() {
var queueRunnerFactory = jasmine.createSpy('queueRunner'),
const runQueue = jasmine.createSpy('runQueue'),
dispatcher = new jasmineUnderTest.ReportDispatcher(
['foo', 'bar'],
queueRunnerFactory
runQueue
),
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);
expect(queueRunnerFactory).toHaveBeenCalledWith(
dispatcher.foo(123);
expect(runQueue).toHaveBeenCalledWith(
jasmine.objectContaining({
queueableFns: [{ fn: jasmine.any(Function) }],
isReporter: true
})
);
var fns = queueRunnerFactory.calls.mostRecent().args[0].queueableFns;
let fns = runQueue.calls.mostRecent().args[0].queueableFns;
fns[0].fn();
expect(reporter1.foo).toHaveBeenCalledWith(123);
dispatcher.clearReporters();
dispatcher.addReporter(reporter2);
dispatcher.bar(456, completeCallback);
dispatcher.bar(456);
expect(queueRunnerFactory).toHaveBeenCalledWith(
expect(runQueue).toHaveBeenCalledWith(
jasmine.objectContaining({
queueableFns: [{ fn: jasmine.any(Function) }],
isReporter: true
})
);
fns = queueRunnerFactory.calls.mostRecent().args[0].queueableFns;
fns = runQueue.calls.mostRecent().args[0].queueableFns;
fns[0].fn();
expect(reporter1.bar).not.toHaveBeenCalled();
expect(reporter2.bar).toHaveBeenCalledWith(456);

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

630
spec/core/RunnerSpec.js Normal file
View File

@@ -0,0 +1,630 @@
describe('Runner', function() {
describe('Integration with TreeProcessor and TreeRunner', function() {
let suiteNumber,
specNumber,
runQueue,
globalErrors,
reportDispatcher,
failSpecWithNoExpectations,
detectLateRejectionHandling;
beforeEach(function() {
suiteNumber = 0;
specNumber = 0;
runQueue = jasmine.createSpy('runQueue');
globalErrors = 'the global errors instance';
reportDispatcher = jasmine.createSpyObj(
'reportDispatcher',
jasmineUnderTest.reporterEvents
);
for (const k of jasmineUnderTest.reporterEvents) {
reportDispatcher[k].and.returnValue(Promise.resolve());
}
// Reasonable defaults, may be overridden in some cases
failSpecWithNoExpectations = false;
detectLateRejectionHandling = false;
spyOn(jasmineUnderTest.TreeRunner.prototype, '_executeSpec');
});
function StubSuite(attrs) {
attrs = attrs || {};
this.id = 'suite' + suiteNumber++;
this.children = attrs.children || [];
this.canBeReentered = function() {
return !attrs.noReenter;
};
this.markedPending = attrs.markedPending || false;
this.sharedUserContext = function() {
return attrs.userContext || {};
};
this.result = {
id: this.id,
failedExpectations: []
};
this.getResult = jasmine.createSpy('getResult');
this.beforeAllFns = attrs.beforeAllFns || [];
this.afterAllFns = attrs.afterAllFns || [];
this.cleanupBeforeAfter = function() {};
this.startTimer = function() {};
this.endTimer = function() {};
}
function StubSpec(attrs) {
attrs = attrs || {};
this.id = 'spec' + specNumber++;
this.markedPending = attrs.markedPending || false;
this.execute = jasmine.createSpy(this.id + '#execute');
this.beforeAndAfterFns = () => ({ befores: [], afters: [] });
this.userContext = () => ({});
this.getFullName = () => '';
this.queueableFn = () => {};
}
function makeRunner(topSuite) {
const defaultOptions = {
getConfig: () => ({
specFilter: () => true,
failSpecWithNoExpectations,
detectLateRejectionHandling
}),
focusedRunables: () => [],
totalSpecsDefined: () => 1,
TreeProcessor: jasmineUnderTest.TreeProcessor,
runableResources: {
initForRunable: () => {},
clearForRunable: () => {}
},
reportDispatcher,
globalErrors,
runQueue
};
return new jasmineUnderTest.Runner({
...defaultOptions,
topSuite
});
}
function arrayNotContaining(item) {
return {
asymmetricMatch(other, matchersUtil) {
if (!jasmine.isArray_(other)) {
return false;
}
for (const x of other) {
if (matchersUtil.equals(x, item)) {
return false;
}
}
return true;
}
};
}
// Precondition: jasmineUnderTest.TreeRunner.prototype._executeSpec is a spy
function verifyAndFinishSpec(spec, queueableFn, shouldBeExcluded) {
const ex = jasmineUnderTest.TreeRunner.prototype._executeSpec;
ex.withArgs(spec, 'onComplete').and.callThrough();
queueableFn.fn('onComplete');
expect(ex).toHaveBeenCalledWith(spec, 'onComplete');
expect(runQueue).toHaveBeenCalledWith(
jasmine.objectContaining({
isLeaf: true,
SkipPolicy: jasmineUnderTest.CompleteOnFirstErrorSkipPolicy,
queueableFns: shouldBeExcluded
? arrayNotContaining(spec.queueableFn)
: jasmine.arrayContaining([spec.queueableFn])
})
);
}
it('runs a single spec', async function() {
const spec = new StubSpec();
const topSuite = new StubSuite({
children: [spec],
userContext: { root: 'context' }
});
detectLateRejectionHandling = true;
const subject = makeRunner(topSuite);
const promise = subject.execute();
await Promise.resolve();
expect(runQueue).toHaveBeenCalledWith({
onComplete: jasmine.any(Function),
onException: jasmine.any(Function),
userContext: { root: 'context' },
queueableFns: [{ fn: jasmine.any(Function) }],
onMultipleDone: null,
SkipPolicy: jasmineUnderTest.SkipAfterBeforeAllErrorPolicy
});
const runQueueArgs = runQueue.calls.mostRecent().args[0];
verifyAndFinishSpec(spec, runQueueArgs.queueableFns[0], false);
runQueueArgs.onComplete();
await promise;
});
it('runs an empty suite', async function() {
const suite = new StubSuite({ userContext: { for: 'suite' } });
const topSuite = new StubSuite({
children: [suite],
userContext: { for: 'topSuite' }
});
suite.parentSuite = topSuite;
const subject = makeRunner(topSuite);
const promise = subject.execute();
await Promise.resolve();
expect(runQueue).toHaveBeenCalledWith({
onComplete: jasmine.any(Function),
onException: jasmine.any(Function),
userContext: { for: 'topSuite' },
queueableFns: [{ fn: jasmine.any(Function) }],
onMultipleDone: null,
SkipPolicy: jasmineUnderTest.SkipAfterBeforeAllErrorPolicy
});
const runQueueArgs = runQueue.calls.mostRecent().args[0];
const nodeDone = jasmine.createSpy('nodeDone');
runQueueArgs.queueableFns[0].fn(nodeDone);
expect(runQueue).toHaveBeenCalledWith({
onComplete: jasmine.any(Function),
onMultipleDone: null,
queueableFns: [{ fn: jasmine.any(Function) }],
userContext: { for: 'suite' },
onException: jasmine.any(Function),
onMultipleDone: null,
SkipPolicy: jasmineUnderTest.SkipAfterBeforeAllErrorPolicy
});
runQueue.calls.mostRecent().args[0].queueableFns[0].fn('foo');
expect(reportDispatcher.suiteStarted).toHaveBeenCalledWith(suite.result);
suite.getResult.and.returnValue({ my: 'result' });
runQueue.calls.mostRecent().args[0].onComplete();
expect(reportDispatcher.suiteDone).toHaveBeenCalledWith({ my: 'result' });
runQueueArgs.onComplete();
await promise;
});
it('runs a non-empty suite', async function() {
const spec1 = new StubSpec();
const spec2 = new StubSpec();
const suite = new StubSuite({ children: [spec1, spec2] });
const topSuite = new StubSuite({ children: [suite] });
const subject = makeRunner(topSuite);
const promise = subject.execute();
await Promise.resolve();
expect(runQueue).toHaveBeenCalledTimes(1);
let queueableFns = runQueue.calls.mostRecent().args[0].queueableFns;
queueableFns[0].fn(function() {});
expect(runQueue).toHaveBeenCalledTimes(2);
queueableFns = runQueue.calls.mostRecent().args[0].queueableFns;
expect(queueableFns.length).toBe(3);
verifyAndFinishSpec(spec1, queueableFns[1], false);
verifyAndFinishSpec(spec2, queueableFns[2], false);
await expectAsync(promise).toBePending();
});
it('"runs" an excluded suite', async function() {
const spec = new StubSpec();
const parent = new StubSuite({ children: [spec] });
const topSuite = new StubSuite({ children: [parent] });
parent.parentSuite = topSuite;
const subject = makeRunner(topSuite);
// Empty list of runable IDs excludes everything
const promise = subject.execute([]);
await Promise.resolve();
expect(runQueue).toHaveBeenCalledTimes(1);
let queueableFns = runQueue.calls.mostRecent().args[0].queueableFns;
queueableFns[0].fn(function() {});
queueableFns = runQueue.calls.mostRecent().args[0].queueableFns;
expect(queueableFns.length).toBe(2);
queueableFns[0].fn();
expect(reportDispatcher.suiteStarted).toHaveBeenCalledWith(parent.result);
verifyAndFinishSpec(spec, queueableFns[1], true);
parent.getResult.and.returnValue(parent.result);
runQueue.calls.argsFor(1)[0].onComplete();
expect(reportDispatcher.suiteDone).toHaveBeenCalledWith(parent.result);
await expectAsync(promise).toBePending();
});
it('handles the failSpecWithNoExpectations option', async function() {
failSpecWithNoExpectations = true;
const spec = new StubSpec();
const parent = new StubSuite({ children: [spec] });
const topSuite = new StubSuite({ children: [parent] });
parent.parentSuite = topSuite;
const subject = makeRunner(topSuite);
const promise = subject.execute();
await Promise.resolve();
expect(runQueue).toHaveBeenCalledTimes(1);
let queueableFns = runQueue.calls.mostRecent().args[0].queueableFns;
queueableFns[0].fn(function() {});
queueableFns = runQueue.calls.mostRecent().args[0].queueableFns;
expect(queueableFns.length).toBe(2);
queueableFns[1].fn('foo');
expect(
jasmineUnderTest.TreeRunner.prototype._executeSpec
).toHaveBeenCalledWith(spec, 'foo');
await expectAsync(promise).toBePending();
});
it('runs beforeAlls and afterAlls for a suite with children', async function() {
const spec = new StubSpec();
const target = new StubSuite({
children: [spec],
beforeAllFns: [
{ fn: 'beforeAll1', timeout: 1 },
{ fn: 'beforeAll2', timeout: 2 }
],
afterAllFns: [
{ fn: 'afterAll1', timeout: 3 },
{ fn: 'afterAll2', timeout: 4 }
]
});
const topSuite = new StubSuite({ children: [target] });
const subject = makeRunner(topSuite);
const promise = subject.execute();
await Promise.resolve();
expect(runQueue).toHaveBeenCalledTimes(1);
const queueableFns = runQueue.calls.mostRecent().args[0].queueableFns;
queueableFns[0].fn(function() {});
expect(runQueue.calls.mostRecent().args[0].queueableFns).toEqual([
{ fn: jasmine.any(Function) },
{ fn: 'beforeAll1', timeout: 1 },
{ fn: 'beforeAll2', timeout: 2 },
{ fn: jasmine.any(Function) },
{ fn: 'afterAll1', timeout: 3 },
{ fn: 'afterAll2', timeout: 4 }
]);
await expectAsync(promise).toBePending();
});
it('does not run beforeAlls or afterAlls for a suite with no children', async function() {
const target = new StubSuite({
beforeAllFns: [{ fn: 'before' }],
afterAllFns: [{ fn: 'after' }]
});
const topSuite = new StubSuite({ children: [target] });
const subject = makeRunner(topSuite);
const promise = subject.execute();
await Promise.resolve();
expect(runQueue).toHaveBeenCalledTimes(1);
const queueableFns = runQueue.calls.mostRecent().args[0].queueableFns;
queueableFns[0].fn(function() {});
expect(runQueue.calls.mostRecent().args[0].queueableFns.length).toEqual(
1
);
await expectAsync(promise).toBePending();
});
it('does not run beforeAlls or afterAlls for a suite with only pending children', async function() {
const spec = new StubSpec({ markedPending: true });
const target = new StubSuite({
children: [spec],
beforeAllFns: [{ fn: 'before' }],
afterAllFns: [{ fn: 'after' }]
});
const topSuite = new StubSuite({ children: [target] });
const subject = makeRunner(topSuite);
const promise = subject.execute();
await Promise.resolve();
expect(runQueue).toHaveBeenCalledTimes(1);
const queueableFns = runQueue.calls.mostRecent().args[0].queueableFns;
queueableFns[0].fn(function() {});
expect(runQueue.calls.mostRecent().args[0].queueableFns.length).toEqual(
2
);
await expectAsync(promise).toBePending();
});
it('runs specs in the order specified', async function() {
const specs = [new StubSpec(), new StubSpec()];
const topSuite = new StubSuite({ children: specs });
const subject = makeRunner(topSuite);
const promise = subject.execute([specs[1].id, specs[0].id]);
await Promise.resolve();
expect(runQueue).toHaveBeenCalledTimes(1);
const queueableFns = runQueue.calls.mostRecent().args[0].queueableFns;
queueableFns[0].fn('done');
expect(
jasmineUnderTest.TreeRunner.prototype._executeSpec
).not.toHaveBeenCalledWith(specs[0], jasmine.anything());
expect(
jasmineUnderTest.TreeRunner.prototype._executeSpec
).toHaveBeenCalledWith(specs[1], 'done');
queueableFns[1].fn('done');
expect(
jasmineUnderTest.TreeRunner.prototype._executeSpec
).toHaveBeenCalledWith(specs[0], 'done');
await expectAsync(promise).toBePending();
});
it('runs specified specs before non-specified specs within a suite', async function() {
const specified = new StubSpec();
const nonSpecified = new StubSpec();
const topSuite = new StubSuite({ children: [nonSpecified, specified] });
const subject = makeRunner(topSuite);
const promise = subject.execute([specified.id]);
await Promise.resolve();
expect(runQueue).toHaveBeenCalledTimes(1);
const queueableFns = runQueue.calls.mostRecent().args[0].queueableFns;
queueableFns[0].fn('done');
expect(
jasmineUnderTest.TreeRunner.prototype._executeSpec
).not.toHaveBeenCalledWith(nonSpecified, jasmine.anything());
expect(
jasmineUnderTest.TreeRunner.prototype._executeSpec
).toHaveBeenCalledWith(specified, 'done');
queueableFns[1].fn('done');
expect(
jasmineUnderTest.TreeRunner.prototype._executeSpec
).toHaveBeenCalledWith(nonSpecified, 'done');
await expectAsync(promise).toBePending();
});
it('runs suites and specs with a specified order', async function() {
const specifiedSpec = new StubSpec();
const nonSpecifiedSpec = new StubSpec();
const specifiedSuite = new StubSuite({ children: [nonSpecifiedSpec] });
const topSuite = new StubSuite({
children: [specifiedSpec, specifiedSuite]
});
const subject = makeRunner(topSuite);
const promise = subject.execute([specifiedSuite.id, specifiedSpec.id]);
await Promise.resolve();
expect(runQueue).toHaveBeenCalledTimes(1);
const queueableFns = runQueue.calls.mostRecent().args[0].queueableFns;
queueableFns[0].fn();
expect(specifiedSpec.execute).not.toHaveBeenCalled();
const nodeQueueableFns = runQueue.calls.mostRecent().args[0].queueableFns;
nodeQueueableFns[1].fn('done');
expect(
jasmineUnderTest.TreeRunner.prototype._executeSpec
).toHaveBeenCalledWith(nonSpecifiedSpec, 'done');
queueableFns[1].fn('done');
expect(
jasmineUnderTest.TreeRunner.prototype._executeSpec
).toHaveBeenCalledWith(specifiedSpec, 'done');
await expectAsync(promise).toBePending();
});
it('runs suites and specs in the order they were declared', async function() {
const spec1 = new StubSpec();
const spec2 = new StubSpec();
const spec3 = new StubSpec();
const parent = new StubSuite({ children: [spec2, spec3] });
const topSuite = new StubSuite({ children: [spec1, parent] });
const subject = makeRunner(topSuite);
const promise = subject.execute();
await Promise.resolve();
expect(runQueue).toHaveBeenCalledTimes(1);
const queueableFns = runQueue.calls.mostRecent().args[0].queueableFns;
expect(queueableFns.length).toBe(2);
queueableFns[0].fn('done');
expect(
jasmineUnderTest.TreeRunner.prototype._executeSpec
).toHaveBeenCalledWith(spec1, 'done');
queueableFns[1].fn();
const childFns = runQueue.calls.mostRecent().args[0].queueableFns;
expect(childFns.length).toBe(3);
childFns[1].fn('done');
expect(
jasmineUnderTest.TreeRunner.prototype._executeSpec
).toHaveBeenCalledWith(spec2, 'done');
childFns[2].fn('done');
expect(
jasmineUnderTest.TreeRunner.prototype._executeSpec
).toHaveBeenCalledWith(spec3, 'done');
await expectAsync(promise).toBePending();
});
it('runs a suite multiple times if the order specified leaves and re-enters it', async function() {
const spec1 = new StubSpec();
const spec2 = new StubSpec();
const spec3 = new StubSpec();
const spec4 = new StubSpec();
const spec5 = new StubSpec();
const reentered = new StubSuite({ children: [spec1, spec2, spec3] });
const topSuite = new StubSuite({ children: [reentered, spec4, spec5] });
const subject = makeRunner(topSuite);
spyOn(jasmineUnderTest.getEnv(), 'deprecated');
const promise = subject.execute([
spec1.id,
spec4.id,
spec2.id,
spec5.id,
spec3.id
]);
await Promise.resolve();
expect(runQueue).toHaveBeenCalledTimes(1);
const queueableFns = runQueue.calls.mostRecent().args[0].queueableFns;
queueableFns[0].fn();
expect(runQueue.calls.mostRecent().args[0].queueableFns.length).toBe(2);
runQueue.calls.mostRecent().args[0].queueableFns[1].fn('done');
expect(
jasmineUnderTest.TreeRunner.prototype._executeSpec
).toHaveBeenCalledWith(spec1, 'done');
queueableFns[1].fn('done');
expect(
jasmineUnderTest.TreeRunner.prototype._executeSpec
).toHaveBeenCalledWith(spec4, 'done');
queueableFns[2].fn();
expect(runQueue.calls.count()).toBe(3);
expect(runQueue.calls.mostRecent().args[0].queueableFns.length).toBe(2);
runQueue.calls.mostRecent().args[0].queueableFns[1].fn('done');
expect(
jasmineUnderTest.TreeRunner.prototype._executeSpec
).toHaveBeenCalledWith(spec2, 'done');
queueableFns[3].fn('done');
expect(
jasmineUnderTest.TreeRunner.prototype._executeSpec
).toHaveBeenCalledWith(spec5, 'done');
queueableFns[4].fn();
expect(runQueue.calls.count()).toBe(4);
expect(runQueue.calls.mostRecent().args[0].queueableFns.length).toBe(2);
runQueue.calls.mostRecent().args[0].queueableFns[1].fn('done');
expect(
jasmineUnderTest.TreeRunner.prototype._executeSpec
).toHaveBeenCalledWith(spec3, 'done');
await expectAsync(promise).toBePending();
});
it('runs a parent of a suite with multiple segments correctly', async function() {
const spec1 = new StubSpec();
const spec2 = new StubSpec();
const spec3 = new StubSpec();
const spec4 = new StubSpec();
const spec5 = new StubSpec();
const parent = new StubSuite({ children: [spec1, spec2, spec3] });
const grandparent = new StubSuite({ children: [parent] });
const topSuite = new StubSuite({ children: [grandparent, spec4, spec5] });
const subject = makeRunner(topSuite);
spyOn(jasmineUnderTest.getEnv(), 'deprecated');
const promise = subject.execute([
spec1.id,
spec4.id,
spec2.id,
spec5.id,
spec3.id
]);
await Promise.resolve();
expect(runQueue).toHaveBeenCalledTimes(1);
const queueableFns = runQueue.calls.mostRecent().args[0].queueableFns;
expect(queueableFns.length).toBe(5);
queueableFns[0].fn();
expect(runQueue.calls.count()).toBe(2);
expect(runQueue.calls.mostRecent().args[0].queueableFns.length).toBe(2);
runQueue.calls.mostRecent().args[0].queueableFns[1].fn();
expect(runQueue.calls.count()).toBe(3);
runQueue.calls.mostRecent().args[0].queueableFns[1].fn('done');
expect(
jasmineUnderTest.TreeRunner.prototype._executeSpec
).toHaveBeenCalledWith(spec1, 'done');
queueableFns[1].fn('done');
expect(
jasmineUnderTest.TreeRunner.prototype._executeSpec
).toHaveBeenCalledWith(spec4, 'done');
queueableFns[2].fn();
expect(runQueue.calls.count()).toBe(4);
expect(runQueue.calls.mostRecent().args[0].queueableFns.length).toBe(2);
runQueue.calls.mostRecent().args[0].queueableFns[1].fn();
expect(runQueue.calls.count()).toBe(5);
runQueue.calls.mostRecent().args[0].queueableFns[1].fn('done');
expect(
jasmineUnderTest.TreeRunner.prototype._executeSpec
).toHaveBeenCalledWith(spec2, 'done');
queueableFns[3].fn('done');
expect(
jasmineUnderTest.TreeRunner.prototype._executeSpec
).toHaveBeenCalledWith(spec5, 'done');
queueableFns[4].fn();
expect(runQueue.calls.count()).toBe(6);
expect(runQueue.calls.mostRecent().args[0].queueableFns.length).toBe(2);
runQueue.calls.mostRecent().args[0].queueableFns[1].fn();
expect(runQueue.calls.count()).toBe(7);
runQueue.calls.mostRecent().args[0].queueableFns[1].fn('done');
expect(
jasmineUnderTest.TreeRunner.prototype._executeSpec
).toHaveBeenCalledWith(spec3, 'done');
await expectAsync(promise).toBePending();
});
it('runs large segments of nodes in the order they were declared', async function() {
const specs = [];
for (let i = 0; i < 11; i++) {
specs.push(new StubSpec());
}
const topSuite = new StubSuite({ children: specs });
const subject = makeRunner(topSuite);
const promise = subject.execute();
await Promise.resolve();
expect(runQueue).toHaveBeenCalledTimes(1);
const queueableFns = runQueue.calls.mostRecent().args[0].queueableFns;
expect(queueableFns.length).toBe(11);
for (let i = 0; i < 11; i++) {
queueableFns[i].fn('done');
expect(
jasmineUnderTest.TreeRunner.prototype._executeSpec
).toHaveBeenCalledWith(specs[i], 'done');
}
await expectAsync(promise).toBePending();
});
});
});

View File

@@ -1,12 +1,12 @@
describe('Spec', function() {
it('#isPendingSpecException returns true for a pending spec exception', function() {
var e = new Error(jasmineUnderTest.Spec.pendingSpecExceptionMessage);
const e = new Error(jasmineUnderTest.Spec.pendingSpecExceptionMessage);
expect(jasmineUnderTest.Spec.isPendingSpecException(e)).toBe(true);
});
it('#isPendingSpecException returns true for a pending spec exception (even when FF bug is present)', function() {
var fakeError = {
const fakeError = {
toString: function() {
return 'Error: ' + jasmineUnderTest.Spec.pendingSpecExceptionMessage;
}
@@ -24,7 +24,7 @@ describe('Spec', function() {
});
it('#isPendingSpecException returns false for not a pending spec exception', function() {
var e = new Error('foo');
const e = new Error('foo');
expect(jasmineUnderTest.Spec.isPendingSpecException(e)).toBe(false);
});
@@ -33,535 +33,345 @@ describe('Spec', function() {
expect(jasmineUnderTest.Spec.isPendingSpecException(void 0)).toBe(false);
});
it('delegates execution to a QueueRunner', function() {
var fakeQueueRunner = jasmine.createSpy('fakeQueueRunner'),
spec = new jasmineUnderTest.Spec({
description: 'my test',
id: 'some-id',
queueableFn: { fn: function() {} },
queueRunnerFactory: fakeQueueRunner
});
spec.execute();
expect(fakeQueueRunner).toHaveBeenCalled();
});
it('should call the start callback on execution', function() {
var fakeQueueRunner = jasmine.createSpy('fakeQueueRunner'),
startCallback = jasmine.createSpy('startCallback'),
spec = new jasmineUnderTest.Spec({
id: 123,
description: 'foo bar',
queueableFn: { fn: function() {} },
onStart: startCallback,
queueRunnerFactory: fakeQueueRunner
});
spec.execute();
fakeQueueRunner.calls.mostRecent().args[0].queueableFns[0].fn();
expect(startCallback).toHaveBeenCalled();
expect(startCallback.calls.first().object).toEqual(spec);
});
it('should call the start callback on execution but before any befores are called', function() {
var fakeQueueRunner = jasmine.createSpy('fakeQueueRunner'),
beforesWereCalled = false,
startCallback = jasmine
.createSpy('start-callback')
.and.callFake(function() {
expect(beforesWereCalled).toBe(false);
}),
spec = new jasmineUnderTest.Spec({
queueableFn: { fn: function() {} },
beforeFns: function() {
return [
function() {
beforesWereCalled = true;
}
];
},
onStart: startCallback,
queueRunnerFactory: fakeQueueRunner
});
spec.execute();
fakeQueueRunner.calls.mostRecent().args[0].queueableFns[0].fn();
expect(startCallback).toHaveBeenCalled();
});
it('provides all before fns and after fns to be run', function() {
var fakeQueueRunner = jasmine.createSpy('fakeQueueRunner'),
before = jasmine.createSpy('before'),
after = jasmine.createSpy('after'),
queueableFn = {
fn: jasmine.createSpy('test body').and.callFake(function() {
expect(before).toHaveBeenCalled();
expect(after).not.toHaveBeenCalled();
})
},
spec = new jasmineUnderTest.Spec({
queueableFn: queueableFn,
beforeAndAfterFns: function() {
return { befores: [before], afters: [after] };
},
queueRunnerFactory: fakeQueueRunner
});
spec.execute();
var options = fakeQueueRunner.calls.mostRecent().args[0];
expect(options.queueableFns).toEqual([
{ fn: jasmine.any(Function) },
before,
queueableFn,
after,
{
fn: jasmine.any(Function),
type: 'specCleanup'
}
]);
});
it("tells the queue runner that it's a leaf node", function() {
var fakeQueueRunner = jasmine.createSpy('fakeQueueRunner'),
spec = new jasmineUnderTest.Spec({
queueableFn: { fn: function() {} },
beforeAndAfterFns: function() {
return { befores: [], afters: [] };
},
queueRunnerFactory: fakeQueueRunner
});
spec.execute();
expect(fakeQueueRunner).toHaveBeenCalledWith(
jasmine.objectContaining({
isLeaf: true
})
);
});
it('is marked pending if created without a function body', function() {
var 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');
});
it('can be excluded at execution time by a parent', function() {
var fakeQueueRunner = jasmine.createSpy('fakeQueueRunner'),
startCallback = jasmine.createSpy('startCallback'),
specBody = jasmine.createSpy('specBody'),
resultCallback = jasmine.createSpy('resultCallback'),
spec = new jasmineUnderTest.Spec({
onStart: startCallback,
queueableFn: { fn: specBody },
resultCallback: resultCallback,
queueRunnerFactory: fakeQueueRunner
describe('#executionFinished', function() {
it('removes the fn if autoCleanClosures is true', function() {
const spec = new jasmineUnderTest.Spec({
queueableFn: { fn: () => {} },
autoCleanClosures: true
});
spec.execute('cally-back', true);
spec.executionFinished();
expect(spec.queueableFn.fn).toBeFalsy();
});
expect(fakeQueueRunner).toHaveBeenCalledWith(
jasmine.objectContaining({
onComplete: jasmine.any(Function),
queueableFns: [
{ fn: jasmine.any(Function) },
{
fn: jasmine.any(Function),
type: 'specCleanup'
}
]
})
);
expect(specBody).not.toHaveBeenCalled();
var args = fakeQueueRunner.calls.mostRecent().args[0];
args.queueableFns[0].fn();
expect(startCallback).toHaveBeenCalled();
args.queueableFns[args.queueableFns.length - 1].fn();
expect(resultCallback).toHaveBeenCalled();
expect(spec.result.status).toBe('excluded');
});
it('can be marked pending, but still calls callbacks when executed', function() {
var fakeQueueRunner = jasmine.createSpy('fakeQueueRunner'),
startCallback = jasmine.createSpy('startCallback'),
resultCallback = jasmine.createSpy('resultCallback'),
spec = new jasmineUnderTest.Spec({
onStart: startCallback,
resultCallback: resultCallback,
description: 'with a spec',
getSpecName: function() {
return 'a suite with a spec';
},
queueRunnerFactory: fakeQueueRunner,
queueableFn: { fn: null }
it('removes the fn after execution if autoCleanClosures is undefined', function() {
const spec = new jasmineUnderTest.Spec({
queueableFn: { fn: () => {} },
autoCleanClosures: undefined
});
spec.pend();
spec.executionFinished();
expect(spec.queueableFn.fn).toBeFalsy();
});
expect(spec.status()).toBe('pending');
spec.execute();
expect(fakeQueueRunner).toHaveBeenCalled();
var args = fakeQueueRunner.calls.mostRecent().args[0];
args.queueableFns[0].fn();
expect(startCallback).toHaveBeenCalled();
args.queueableFns[1].fn('things');
expect(resultCallback).toHaveBeenCalledWith(
{
id: spec.id,
status: 'pending',
description: 'with a spec',
fullName: 'a suite with a spec',
failedExpectations: [],
passedExpectations: [],
deprecationWarnings: [],
pendingReason: '',
duration: jasmine.any(Number),
properties: null,
debugLogs: null
},
'things'
);
});
it('should call the done callback on execution complete', function() {
var done = jasmine.createSpy('done callback'),
spec = new jasmineUnderTest.Spec({
queueableFn: { fn: function() {} },
catchExceptions: function() {
return false;
},
resultCallback: function() {},
queueRunnerFactory: function(attrs) {
attrs.onComplete();
}
it('does not remove the fn after execution if autoCleanClosures is false', function() {
function originalFn() {}
const spec = new jasmineUnderTest.Spec({
queueableFn: { fn: originalFn },
autoCleanClosures: false
});
spec.execute(done);
expect(done).toHaveBeenCalled();
spec.executionFinished();
expect(spec.queueableFn.fn).toBe(originalFn);
});
});
it('should call the done callback with an error if the spec is failed', function() {
var done = jasmine.createSpy('done callback'),
spec = new jasmineUnderTest.Spec({
queueableFn: { fn: function() {} },
catchExceptions: function() {
return false;
},
resultCallback: function() {},
queueRunnerFactory: function(attrs) {
spec.result.status = 'failed';
attrs.onComplete();
}
describe('#getSpecProperty', function() {
it('get the property value', function() {
const spec = new jasmineUnderTest.Spec({
queueableFn: { fn: () => {} }
});
spec.execute(done);
expect(done).toHaveBeenCalledWith(
jasmine.any(jasmineUnderTest.StopExecutionError)
);
spec.setSpecProperty('a', 4);
expect(spec.getSpecProperty('a')).toBe(4);
});
});
it('should report the duration of the test', function() {
var timer = jasmine.createSpyObj('timer', { start: null, elapsed: 77000 }),
spec = new jasmineUnderTest.Spec({
queueableFn: { fn: jasmine.createSpy('spec body') },
catchExceptions: function() {
return false;
},
resultCallback: function(result) {
duration = result.duration;
},
queueRunnerFactory: function(config) {
config.queueableFns.forEach(function(qf) {
qf.fn();
});
config.onComplete();
},
timer: timer
}),
duration = undefined;
spec.execute(function() {});
expect(duration).toBe(77000);
});
it('should report properties set during the test', function() {
var done = jasmine.createSpy('done callback'),
spec = new jasmineUnderTest.Spec({
queueableFn: { fn: jasmine.createSpy('spec body') },
catchExceptions: function() {
return false;
},
resultCallback: function() {},
queueRunnerFactory: function(attrs) {
attrs.onComplete();
}
describe('#setSpecProperty', function() {
it('adds the property to the result', function() {
const spec = new jasmineUnderTest.Spec({
queueableFn: { fn: () => {} }
});
spec.setSpecProperty('a', 4);
spec.execute(done);
expect(spec.result.properties).toEqual({ a: 4 });
spec.setSpecProperty('a', 4);
expect(spec.result.properties).toEqual({ a: 4 });
});
it('replace the property result when it was previously set', function() {
const spec = new jasmineUnderTest.Spec({
queueableFn: { fn: () => {} }
});
spec.setSpecProperty('a', 'original-value');
spec.setSpecProperty('b', 'original-value');
spec.setSpecProperty('a', 'new-value');
expect(spec.result.properties).toEqual({
a: 'new-value',
b: 'original-value'
});
});
});
it('#status returns passing by default', function() {
var spec = new jasmineUnderTest.Spec({
const spec = new jasmineUnderTest.Spec({
queueableFn: { fn: jasmine.createSpy('spec body') }
});
expect(spec.status()).toBe('passed');
});
it('#status returns passed if all expectations in the spec have passed', function() {
var spec = new jasmineUnderTest.Spec({
queueableFn: { fn: jasmine.createSpy('spec body') }
});
spec.addExpectationResult(true);
expect(spec.status()).toBe('passed');
});
it('#status returns failed if any expectations in the spec have failed', function() {
var spec = new jasmineUnderTest.Spec({
queueableFn: { fn: jasmine.createSpy('spec body') }
});
spec.addExpectationResult(true);
spec.addExpectationResult(false);
expect(spec.status()).toBe('failed');
});
it('keeps track of passed and failed expectations', function() {
var fakeQueueRunner = jasmine.createSpy('queueRunner'),
resultCallback = jasmine.createSpy('resultCallback'),
spec = new jasmineUnderTest.Spec({
queueableFn: { fn: jasmine.createSpy('spec body') },
expectationResultFactory: function(data) {
return data;
},
queueRunnerFactory: fakeQueueRunner,
resultCallback: resultCallback
describe('#status', function() {
it('returns "passed"" by default', function() {
const spec = new jasmineUnderTest.Spec({
queueableFn: { fn: () => {} }
});
spec.addExpectationResult(true, 'expectation1');
spec.addExpectationResult(false, 'expectation2');
expect(spec.status()).toBe('passed');
});
spec.execute();
const fns = fakeQueueRunner.calls.mostRecent().args[0].queueableFns;
fns[fns.length - 1].fn();
expect(resultCallback.calls.first().args[0].passedExpectations).toEqual([
'expectation1'
]);
expect(resultCallback.calls.first().args[0].failedExpectations).toEqual([
'expectation2'
]);
});
it("throws an ExpectationFailed error upon receiving a failed expectation when 'throwOnExpectationFailure' is set", function() {
var fakeQueueRunner = jasmine.createSpy('queueRunner'),
resultCallback = jasmine.createSpy('resultCallback'),
spec = new jasmineUnderTest.Spec({
queueableFn: { fn: function() {} },
expectationResultFactory: function(data) {
return data;
},
queueRunnerFactory: fakeQueueRunner,
resultCallback: resultCallback,
throwOnExpectationFailure: true
it('returns "passed"" if all expectations passed', function() {
const spec = new jasmineUnderTest.Spec({
queueableFn: { fn: () => {} }
});
spec.addExpectationResult(true, 'passed');
expect(function() {
spec.addExpectationResult(false, 'failed');
}).toThrowError(jasmineUnderTest.errors.ExpectationFailed);
spec.addExpectationResult(true, {});
spec.execute();
expect(spec.status()).toBe('passed');
});
const fns = fakeQueueRunner.calls.mostRecent().args[0].queueableFns;
fns[fns.length - 1].fn();
expect(resultCallback.calls.first().args[0].passedExpectations).toEqual([
'passed'
]);
expect(resultCallback.calls.first().args[0].failedExpectations).toEqual([
'failed'
]);
it('returns "failed" if any expectation failed', function() {
const spec = new jasmineUnderTest.Spec({
queueableFn: { fn: () => {} }
});
spec.addExpectationResult(true, {});
spec.addExpectationResult(false, {});
expect(spec.status()).toBe('failed');
});
});
describe('#addExpectationResult', function() {
it('keeps track of passed and failed expectations', function() {
const spec = new jasmineUnderTest.Spec({
queueableFn: { fn: () => {} }
});
spec.addExpectationResult(true, { message: 'expectation1' });
spec.addExpectationResult(false, { message: 'expectation2' });
expect(spec.result.passedExpectations).toEqual([
jasmine.objectContaining({ message: 'expectation1' })
]);
expect(spec.result.failedExpectations).toEqual([
jasmine.objectContaining({ message: 'expectation2' })
]);
});
describe("when 'throwOnExpectationFailure' is set", function() {
it('throws an ExpectationFailed error', function() {
const spec = new jasmineUnderTest.Spec({
queueableFn: { fn: () => {} },
throwOnExpectationFailure: true
});
spec.addExpectationResult(true, { message: 'passed' });
expect(function() {
spec.addExpectationResult(false, { message: 'failed' });
}).toThrowError(jasmineUnderTest.errors.ExpectationFailed);
expect(spec.result.failedExpectations).toEqual([
jasmine.objectContaining({ message: 'failed' })
]);
});
});
describe("when 'throwOnExpectationFailure' is not set", function() {
it('does not throw', function() {
const spec = new jasmineUnderTest.Spec({
queueableFn: { fn: () => {} }
});
spec.addExpectationResult(false, { message: 'failed' });
expect(spec.result.failedExpectations).toEqual([
jasmine.objectContaining({ message: 'failed' })
]);
});
});
});
it('forwards late expectation failures to onLateError', function() {
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() {
var resultCallback = jasmine.createSpy('resultCallback'),
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() {
var specNameSpy = jasmine
.createSpy('specNameSpy')
.and.returnValue('expected val');
const getPath = jasmine
.createSpy('getPath')
.and.returnValue(['expected', 'val']);
var spec = new jasmineUnderTest.Spec({
getSpecName: specNameSpy,
const spec = new jasmineUnderTest.Spec({
getPath,
queueableFn: { fn: null }
});
expect(spec.getFullName()).toBe('expected val');
expect(specNameSpy.calls.mostRecent().args[0].id).toEqual(spec.id);
expect(getPath.calls.mostRecent().args[0]).toBe(spec);
});
describe('when a spec is marked pending during execution', function() {
it('should mark the spec as pending', function() {
var fakeQueueRunner = function(opts) {
opts.onException(
new Error(jasmineUnderTest.Spec.pendingSpecExceptionMessage)
);
},
spec = new jasmineUnderTest.Spec({
description: 'my test',
id: 'some-id',
queueableFn: { fn: function() {} },
queueRunnerFactory: fakeQueueRunner
});
it('can return its full path', function() {
const getPath = jasmine
.createSpy('getPath')
.and.returnValue(['expected val']);
spec.execute();
expect(spec.status()).toEqual('pending');
expect(spec.result.pendingReason).toEqual('');
const spec = new jasmineUnderTest.Spec({
getPath,
queueableFn: { fn: null }
});
it('should set the pendingReason', function() {
var fakeQueueRunner = function(opts) {
opts.onException(
new Error(
jasmineUnderTest.Spec.pendingSpecExceptionMessage +
'custom message'
)
);
},
spec = new jasmineUnderTest.Spec({
description: 'my test',
id: 'some-id',
queueableFn: { fn: function() {} },
queueRunnerFactory: fakeQueueRunner
});
expect(spec.getPath()).toEqual(['expected val']);
expect(getPath.calls.mostRecent().args[0]).toBe(spec);
spec.execute();
expect(spec.status()).toEqual('pending');
expect(spec.result.pendingReason).toEqual('custom message');
});
expect(spec.metadata.getPath()).toEqual(['expected val']);
});
it('should log a failure when handling an exception', function() {
var fakeQueueRunner = jasmine.createSpy('queueRunner'),
resultCallback = jasmine.createSpy('resultCallback'),
spec = new jasmineUnderTest.Spec({
queueableFn: { fn: function() {} },
expectationResultFactory: function(data) {
return data;
},
queueRunnerFactory: fakeQueueRunner,
resultCallback: resultCallback
describe('#handleException', function() {
it('records a failure', function() {
const spec = new jasmineUnderTest.Spec({
queueableFn: {}
});
spec.onException('foo');
spec.execute();
spec.handleException('foo');
var args = fakeQueueRunner.calls.mostRecent().args[0];
args.queueableFns[args.queueableFns.length - 1].fn();
expect(resultCallback.calls.first().args[0].failedExpectations).toEqual([
{
error: 'foo',
matcherName: '',
passed: false,
expected: '',
actual: ''
}
]);
});
it('should not log an additional failure when handling an ExpectationFailed error', function() {
var fakeQueueRunner = jasmine.createSpy('queueRunner'),
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();
var args = fakeQueueRunner.calls.mostRecent().args[0];
args.queueableFns[args.queueableFns.length - 1].fn();
expect(resultCallback.calls.first().args[0].failedExpectations).toEqual([]);
});
it('treats multiple done calls as late errors', function() {
var queueRunnerFactory = jasmine.createSpy('queueRunnerFactory'),
onLateError = jasmine.createSpy('onLateError'),
spec = new jasmineUnderTest.Spec({
onLateError: onLateError,
queueableFn: { fn: function() {} },
queueRunnerFactory: queueRunnerFactory,
getSpecName: function() {
return 'a spec';
expect(spec.result.failedExpectations).toEqual([
{
message: 'foo thrown',
matcherName: '',
passed: false,
expected: '',
actual: '',
stack: null
}
]);
});
it('does not record an additional failure when the error is ExpectationFailed', function() {
const spec = new jasmineUnderTest.Spec({
queueableFn: {}
});
spec.execute();
spec.handleException(new jasmineUnderTest.errors.ExpectationFailed());
expect(queueRunnerFactory).toHaveBeenCalled();
queueRunnerFactory.calls.argsFor(0)[0].onMultipleDone();
expect(onLateError).toHaveBeenCalledTimes(1);
expect(onLateError.calls.argsFor(0)[0]).toBeInstanceOf(Error);
expect(onLateError.calls.argsFor(0)[0].message).toEqual(
'An asynchronous spec, beforeEach, or afterEach function called its ' +
"'done' callback more than once.\n(in spec: a spec)"
);
expect(spec.result.failedExpectations).toEqual([]);
});
});
describe('#trace', function() {
describe('#debugLog', function() {
it('adds the messages to the result', function() {
var timer = jasmine.createSpyObj('timer', ['start', 'elapsed']),
spec = new jasmineUnderTest.Spec({
queueableFn: {
fn: function() {}
},
queueRunnerFactory: function() {},
timer: timer
}),
t1 = 123,
t2 = 456;
const timer = jasmine.createSpyObj('timer', ['start', 'elapsed']);
const spec = new jasmineUnderTest.Spec({
queueableFn: { fn: () => {} },
timer: timer
});
const t1 = 123;
const t2 = 456;
spec.execute();
expect(spec.result.debugLogs).toBeNull();
timer.elapsed.and.returnValue(t1);
spec.debugLog('msg 1');
@@ -577,81 +387,36 @@ describe('Spec', function() {
});
describe('When the spec passes', function() {
it('omits the messages from the reported result', function() {
var resultCallback = jasmine.createSpy('resultCallback'),
spec = new jasmineUnderTest.Spec({
queueableFn: {
fn: function() {}
},
resultCallback: resultCallback,
queueRunnerFactory: function(config) {
spec.debugLog('msg');
for (const fn of config.queueableFns) {
fn.fn();
}
config.onComplete(false);
}
});
it('removes the logs from the result', function() {
const spec = new jasmineUnderTest.Spec({
queueableFn: { fn: () => {} }
});
spec.execute(function() {});
expect(resultCallback).toHaveBeenCalledWith(
jasmine.objectContaining({ debugLogs: null }),
undefined
);
});
spec.debugLog('msg');
spec.executionFinished();
it('removes the messages to save memory', function() {
var resultCallback = jasmine.createSpy('resultCallback'),
spec = new jasmineUnderTest.Spec({
queueableFn: {
fn: function() {}
},
resultCallback: resultCallback,
queueRunnerFactory: function(config) {
spec.debugLog('msg');
for (const fn of config.queueableFns) {
fn.fn();
}
config.onComplete(false);
}
});
spec.execute(function() {});
expect(resultCallback).toHaveBeenCalled();
expect(spec.result.debugLogs).toBeNull();
});
});
describe('When the spec fails', function() {
it('includes the messages in the reported result', function() {
var resultCallback = jasmine.createSpy('resultCallback'),
timer = jasmine.createSpyObj('timer', ['start', 'elapsed']),
spec = new jasmineUnderTest.Spec({
queueableFn: {
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;
it('includes the messages in the result', function() {
const timer = jasmine.createSpyObj('timer', ['start', 'elapsed']);
const spec = new jasmineUnderTest.Spec({
queueableFn: { fn: () => {} },
timer: timer
});
const timestamp = 12345;
timer.elapsed.and.returnValue(timestamp);
spec.execute(function() {});
expect(resultCallback).toHaveBeenCalledWith(
jasmine.objectContaining({
debugLogs: [{ message: 'msg', timestamp: timestamp }]
}),
undefined
);
spec.debugLog('msg');
spec.handleException(new Error('nope'));
spec.executionFinished();
expect(spec.result.debugLogs).toEqual([
{ message: 'msg', timestamp: timestamp }
]);
});
});
});

View File

@@ -5,7 +5,7 @@ describe('SpyRegistry', function() {
describe('#spyOn', function() {
it('checks for the existence of the object', function() {
var spyRegistry = new jasmineUnderTest.SpyRegistry({
const spyRegistry = new jasmineUnderTest.SpyRegistry({
createSpy: createSpy
});
expect(function() {
@@ -14,7 +14,7 @@ describe('SpyRegistry', function() {
});
it('checks that a method name was passed', function() {
var spyRegistry = new jasmineUnderTest.SpyRegistry(),
const spyRegistry = new jasmineUnderTest.SpyRegistry(),
subject = {};
expect(function() {
@@ -23,14 +23,14 @@ describe('SpyRegistry', function() {
});
it('checks that the object is not `null`', function() {
var spyRegistry = new jasmineUnderTest.SpyRegistry();
const spyRegistry = new jasmineUnderTest.SpyRegistry();
expect(function() {
spyRegistry.spyOn(null, 'pants');
}).toThrowError(/could not find an object/);
});
it('checks that the method name is not `null`', function() {
var spyRegistry = new jasmineUnderTest.SpyRegistry(),
const spyRegistry = new jasmineUnderTest.SpyRegistry(),
subject = {};
expect(function() {
@@ -39,7 +39,7 @@ describe('SpyRegistry', function() {
});
it('checks for the existence of the method', function() {
var spyRegistry = new jasmineUnderTest.SpyRegistry(),
const spyRegistry = new jasmineUnderTest.SpyRegistry(),
subject = {};
expect(function() {
@@ -48,7 +48,7 @@ describe('SpyRegistry', function() {
});
it('checks if it has already been spied upon', function() {
var spies = [],
const spies = [],
spyRegistry = new jasmineUnderTest.SpyRegistry({
currentSpies: function() {
return spies;
@@ -65,7 +65,7 @@ describe('SpyRegistry', function() {
});
it('checks if it can be spied upon', function() {
var scope = {};
const scope = {};
function myFunc() {
return 1;
@@ -77,7 +77,7 @@ describe('SpyRegistry', function() {
}
});
var spies = [],
const spies = [],
spyRegistry = new jasmineUnderTest.SpyRegistry({
currentSpies: function() {
return spies;
@@ -94,8 +94,32 @@ describe('SpyRegistry', function() {
}).not.toThrowError(/is not declared writable or has no setter/);
});
it('throws if assigning to the property is a no-op', function() {
const scope = {};
function original() {
return 1;
}
Object.defineProperty(scope, 'myFunc', {
get() {
return original;
},
set() {}
});
const spyRegistry = new jasmineUnderTest.SpyRegistry({
createSpy: createSpy
});
expect(function() {
spyRegistry.spyOn(scope, 'myFunc');
}).toThrowError(
"<spyOn> : Can't spy on myFunc because assigning to it had no effect"
);
});
it('overrides the method on the object and returns the spy', function() {
var originalFunctionWasCalled = false,
const originalFunctionWasCalled = false,
spyRegistry = new jasmineUnderTest.SpyRegistry({
createSpy: createSpy
}),
@@ -105,7 +129,7 @@ describe('SpyRegistry', function() {
}
};
var spy = spyRegistry.spyOn(subject, 'spiedFunc');
const spy = spyRegistry.spyOn(subject, 'spiedFunc');
expect(subject.spiedFunc).toEqual(spy);
subject.spiedFunc();
@@ -115,14 +139,14 @@ describe('SpyRegistry', function() {
describe('#spyOnProperty', function() {
it('checks for the existence of the object', function() {
var spyRegistry = new jasmineUnderTest.SpyRegistry();
const spyRegistry = new jasmineUnderTest.SpyRegistry();
expect(function() {
spyRegistry.spyOnProperty(void 0, 'pants');
}).toThrowError(/could not find an object/);
});
it('checks that a property name was passed', function() {
var spyRegistry = new jasmineUnderTest.SpyRegistry(),
const spyRegistry = new jasmineUnderTest.SpyRegistry(),
subject = {};
expect(function() {
@@ -131,7 +155,7 @@ describe('SpyRegistry', function() {
});
it('checks for the existence of the method', function() {
var spyRegistry = new jasmineUnderTest.SpyRegistry(),
const spyRegistry = new jasmineUnderTest.SpyRegistry(),
subject = {};
expect(function() {
@@ -140,7 +164,7 @@ describe('SpyRegistry', function() {
});
it('checks for the existence of access type', function() {
var spyRegistry = new jasmineUnderTest.SpyRegistry(),
const spyRegistry = new jasmineUnderTest.SpyRegistry(),
subject = {};
Object.defineProperty(subject, 'pants', {
@@ -156,7 +180,7 @@ describe('SpyRegistry', function() {
});
it('checks if it can be spied upon', function() {
var subject = {};
const subject = {};
Object.defineProperty(subject, 'myProp', {
get: function() {}
@@ -167,7 +191,7 @@ describe('SpyRegistry', function() {
configurable: true
});
var spyRegistry = new jasmineUnderTest.SpyRegistry();
const spyRegistry = new jasmineUnderTest.SpyRegistry();
expect(function() {
spyRegistry.spyOnProperty(subject, 'myProp');
@@ -179,7 +203,7 @@ describe('SpyRegistry', function() {
});
it('overrides the property getter on the object and returns the spy', function() {
var spyRegistry = new jasmineUnderTest.SpyRegistry({
const spyRegistry = new jasmineUnderTest.SpyRegistry({
createSpy: createSpy
}),
subject = {},
@@ -194,8 +218,8 @@ describe('SpyRegistry', function() {
expect(subject.spiedProperty).toEqual(returnValue);
var spy = spyRegistry.spyOnProperty(subject, 'spiedProperty');
var getter = Object.getOwnPropertyDescriptor(subject, 'spiedProperty')
const spy = spyRegistry.spyOnProperty(subject, 'spiedProperty');
const getter = Object.getOwnPropertyDescriptor(subject, 'spiedProperty')
.get;
expect(getter).toEqual(spy);
@@ -203,7 +227,7 @@ describe('SpyRegistry', function() {
});
it('overrides the property setter on the object and returns the spy', function() {
var spyRegistry = new jasmineUnderTest.SpyRegistry({
const spyRegistry = new jasmineUnderTest.SpyRegistry({
createSpy: createSpy
}),
subject = {},
@@ -217,8 +241,8 @@ describe('SpyRegistry', function() {
configurable: true
});
var spy = spyRegistry.spyOnProperty(subject, 'spiedProperty', 'set');
var setter = Object.getOwnPropertyDescriptor(subject, 'spiedProperty')
const spy = spyRegistry.spyOnProperty(subject, 'spiedProperty', 'set');
const setter = Object.getOwnPropertyDescriptor(subject, 'spiedProperty')
.set;
expect(subject.spiedProperty).toEqual(returnValue);
@@ -227,7 +251,7 @@ describe('SpyRegistry', function() {
describe('when the property is already spied upon', function() {
it('throws an error if respy is not allowed', function() {
var spyRegistry = new jasmineUnderTest.SpyRegistry({
const spyRegistry = new jasmineUnderTest.SpyRegistry({
createSpy: createSpy
}),
subject = {};
@@ -247,7 +271,7 @@ describe('SpyRegistry', function() {
});
it('returns the original spy if respy is allowed', function() {
var spyRegistry = new jasmineUnderTest.SpyRegistry({
const spyRegistry = new jasmineUnderTest.SpyRegistry({
createSpy: createSpy
}),
subject = {};
@@ -261,7 +285,7 @@ describe('SpyRegistry', function() {
configurable: true
});
var originalSpy = spyRegistry.spyOnProperty(subject, 'spiedProp');
const originalSpy = spyRegistry.spyOnProperty(subject, 'spiedProp');
expect(spyRegistry.spyOnProperty(subject, 'spiedProp')).toBe(
originalSpy
@@ -272,33 +296,33 @@ describe('SpyRegistry', function() {
describe('#spyOnAllFunctions', function() {
it('checks for the existence of the object', function() {
var spyRegistry = new jasmineUnderTest.SpyRegistry();
const spyRegistry = new jasmineUnderTest.SpyRegistry();
expect(function() {
spyRegistry.spyOnAllFunctions(void 0);
}).toThrowError(/spyOnAllFunctions could not find an object to spy upon/);
});
it('overrides all writable and configurable functions of the object and its parents', function() {
var spyRegistry = new jasmineUnderTest.SpyRegistry({
const spyRegistry = new jasmineUnderTest.SpyRegistry({
createSpy: function() {
return 'I am a spy';
}
});
var createNoop = function() {
const createNoop = function() {
return function() {
/**/
};
};
var noop1 = createNoop();
var noop2 = createNoop();
var noop3 = createNoop();
var noop4 = createNoop();
var noop5 = createNoop();
const noop1 = createNoop();
const noop2 = createNoop();
const noop3 = createNoop();
const noop4 = createNoop();
const noop5 = createNoop();
var parent = {
const parent = {
parentSpied1: noop1
};
var subject = Object.create(parent);
const subject = Object.create(parent);
Object.defineProperty(subject, 'spied1', {
value: noop1,
writable: true,
@@ -311,7 +335,7 @@ describe('SpyRegistry', function() {
configurable: true,
enumerable: true
});
var _spied3 = noop3;
let _spied3 = noop3;
Object.defineProperty(subject, 'spied3', {
configurable: true,
set: function(val) {
@@ -337,7 +361,7 @@ describe('SpyRegistry', function() {
});
Object.defineProperty(subject, 'notSpied4', {
configurable: false,
set: function(val) {
set: function() {
/**/
},
get: function() {
@@ -353,7 +377,7 @@ describe('SpyRegistry', function() {
});
subject.notSpied6 = 6;
var spiedObject = spyRegistry.spyOnAllFunctions(subject);
const spiedObject = spyRegistry.spyOnAllFunctions(subject);
expect(subject.parentSpied1).toBe('I am a spy');
expect(subject.notSpied2).toBe(noop2);
@@ -369,21 +393,21 @@ describe('SpyRegistry', function() {
});
it('overrides prototype methods on the object', function() {
var spyRegistry = new jasmineUnderTest.SpyRegistry({
const spyRegistry = new jasmineUnderTest.SpyRegistry({
createSpy: function() {
return 'I am a spy';
}
});
var noop1 = function() {};
var noop2 = function() {};
const noop1 = function() {};
const noop2 = function() {};
var MyClass = function() {
const MyClass = function() {
this.spied1 = noop1;
};
MyClass.prototype.spied2 = noop2;
var subject = new MyClass();
const subject = new MyClass();
spyRegistry.spyOnAllFunctions(subject);
expect(subject.spied1).toBe('I am a spy');
@@ -392,12 +416,12 @@ describe('SpyRegistry', function() {
});
it('does not override non-enumerable properties (like Object.prototype methods)', function() {
var spyRegistry = new jasmineUnderTest.SpyRegistry({
const spyRegistry = new jasmineUnderTest.SpyRegistry({
createSpy: function() {
return 'I am a spy';
}
});
var subject = {
const subject = {
spied1: function() {}
};
@@ -409,12 +433,12 @@ describe('SpyRegistry', function() {
});
describe('when includeNonEnumerable is true', function() {
it('does not override Object.prototype methods', function() {
var spyRegistry = new jasmineUnderTest.SpyRegistry({
const spyRegistry = new jasmineUnderTest.SpyRegistry({
createSpy: function() {
return 'I am a spy';
}
});
var subject = {
const subject = {
spied1: function() {}
};
@@ -426,12 +450,12 @@ describe('SpyRegistry', function() {
});
it('overrides non-enumerable properties', function() {
var spyRegistry = new jasmineUnderTest.SpyRegistry({
const spyRegistry = new jasmineUnderTest.SpyRegistry({
createSpy: function() {
return 'I am a spy';
}
});
var subject = {
const subject = {
spied1: function() {},
spied2: function() {}
};
@@ -449,12 +473,12 @@ describe('SpyRegistry', function() {
});
it('should not spy on non-enumerable functions named constructor', function() {
var spyRegistry = new jasmineUnderTest.SpyRegistry({
const spyRegistry = new jasmineUnderTest.SpyRegistry({
createSpy: function() {
return 'I am a spy';
}
});
var subject = {
const subject = {
constructor: function() {}
};
@@ -470,12 +494,12 @@ describe('SpyRegistry', function() {
});
it('should spy on enumerable functions named constructor', function() {
var spyRegistry = new jasmineUnderTest.SpyRegistry({
const spyRegistry = new jasmineUnderTest.SpyRegistry({
createSpy: function() {
return 'I am a spy';
}
});
var subject = {
const subject = {
constructor: function() {}
};
@@ -485,13 +509,13 @@ describe('SpyRegistry', function() {
});
it('should not throw an exception if we try and access strict mode restricted properties', function() {
var spyRegistry = new jasmineUnderTest.SpyRegistry({
const spyRegistry = new jasmineUnderTest.SpyRegistry({
createSpy: function() {
return 'I am a spy';
}
});
var subject = function() {};
var fn = function() {
const subject = function() {};
const fn = function() {
spyRegistry.spyOnAllFunctions(subject, true);
};
@@ -499,24 +523,24 @@ describe('SpyRegistry', function() {
});
it('should not spy on properties which are more permissable further up the prototype chain', function() {
var spyRegistry = new jasmineUnderTest.SpyRegistry({
const spyRegistry = new jasmineUnderTest.SpyRegistry({
createSpy: function() {
return 'I am a spy';
}
});
var subjectParent = Object.defineProperty({}, 'sharedProp', {
const subjectParent = Object.defineProperty({}, 'sharedProp', {
value: function() {},
writable: true,
configurable: true
});
var subject = Object.create(subjectParent);
const subject = Object.create(subjectParent);
Object.defineProperty(subject, 'sharedProp', {
value: function() {}
});
var fn = function() {
const fn = function() {
spyRegistry.spyOnAllFunctions(subject, true);
};
@@ -528,7 +552,7 @@ describe('SpyRegistry', function() {
describe('#clearSpies', function() {
it('restores the original functions on the spied-upon objects', function() {
var spies = [],
const spies = [],
spyRegistry = new jasmineUnderTest.SpyRegistry({
currentSpies: function() {
return spies;
@@ -545,7 +569,7 @@ describe('SpyRegistry', function() {
});
it('restores the original functions, even when that spy has been replace and re-spied upon', function() {
var spies = [],
const spies = [],
spyRegistry = new jasmineUnderTest.SpyRegistry({
currentSpies: function() {
return spies;
@@ -569,7 +593,7 @@ describe('SpyRegistry', function() {
});
it("does not add a property that the spied-upon object didn't originally have", function() {
var spies = [],
const spies = [],
spyRegistry = new jasmineUnderTest.SpyRegistry({
currentSpies: function() {
return spies;
@@ -579,7 +603,7 @@ describe('SpyRegistry', function() {
originalFunction = function() {},
subjectParent = { spiedFunc: originalFunction };
var subject = Object.create(subjectParent);
const subject = Object.create(subjectParent);
expect(subject.hasOwnProperty('spiedFunc')).toBe(false);
@@ -591,7 +615,7 @@ describe('SpyRegistry', function() {
});
it("restores the original function when it's inherited and cannot be deleted", function() {
var spies = [],
const spies = [],
spyRegistry = new jasmineUnderTest.SpyRegistry({
currentSpies: function() {
return spies;
@@ -601,7 +625,7 @@ describe('SpyRegistry', function() {
originalFunction = function() {},
subjectParent = { spiedFunc: originalFunction };
var subject = Object.create(subjectParent);
const subject = Object.create(subjectParent);
spyRegistry.spyOn(subject, 'spiedFunc');
@@ -619,7 +643,7 @@ describe('SpyRegistry', function() {
function FakeWindow() {}
FakeWindow.prototype.onerror = function() {};
var spies = [],
const spies = [],
global = new FakeWindow(),
spyRegistry = new jasmineUnderTest.SpyRegistry({
currentSpies: function() {
@@ -638,7 +662,7 @@ describe('SpyRegistry', function() {
describe('spying on properties', function() {
it('restores the original properties on the spied-upon objects', function() {
var spies = [],
const spies = [],
spyRegistry = new jasmineUnderTest.SpyRegistry({
currentSpies: function() {
return spies;
@@ -662,7 +686,7 @@ describe('SpyRegistry', function() {
});
it("does not add a property that the spied-upon object didn't originally have", function() {
var spies = [],
const spies = [],
spyRegistry = new jasmineUnderTest.SpyRegistry({
currentSpies: function() {
return spies;
@@ -679,7 +703,7 @@ describe('SpyRegistry', function() {
configurable: true
});
var subject = Object.create(subjectParent);
const subject = Object.create(subjectParent);
expect(subject.hasOwnProperty('spiedProp')).toBe(false);

View File

@@ -1,5 +1,5 @@
describe('Spies', function() {
var env;
let env;
beforeEach(function() {
env = new jasmineUnderTest.Env();
@@ -10,7 +10,7 @@ describe('Spies', function() {
});
describe('createSpy', function() {
var TestClass;
let TestClass;
beforeEach(function() {
TestClass = function() {};
@@ -19,7 +19,7 @@ describe('Spies', function() {
});
it('preserves the properties of the spied function', function() {
var spy = env.createSpy(
const spy = env.createSpy(
TestClass.prototype,
TestClass.prototype.someFunction
);
@@ -28,8 +28,8 @@ describe('Spies', function() {
});
it('should allow you to omit the name argument and only pass the originalFn argument', function() {
var fn = function test() {};
var spy = env.createSpy(fn);
const fn = function test() {};
const spy = env.createSpy(fn);
expect(spy.and.identity).toEqual('test');
});
@@ -45,7 +45,7 @@ describe('Spies', function() {
});
it('adds a spyStrategy and callTracker to the spy', function() {
var spy = env.createSpy(
const spy = env.createSpy(
TestClass.prototype,
TestClass.prototype.someFunction
);
@@ -55,11 +55,11 @@ describe('Spies', function() {
});
it('tracks the argument of calls', function() {
var spy = env.createSpy(
const spy = env.createSpy(
TestClass.prototype,
TestClass.prototype.someFunction
);
var trackSpy = spyOn(spy.calls, 'track');
const trackSpy = spyOn(spy.calls, 'track');
spy('arg');
@@ -67,24 +67,24 @@ describe('Spies', function() {
});
it('tracks the context of calls', function() {
var spy = env.createSpy(
const spy = env.createSpy(
TestClass.prototype,
TestClass.prototype.someFunction
);
var trackSpy = spyOn(spy.calls, 'track');
const trackSpy = spyOn(spy.calls, 'track');
var contextObject = { spyMethod: spy };
const contextObject = { spyMethod: spy };
contextObject.spyMethod();
expect(trackSpy.calls.mostRecent().args[0].object).toEqual(contextObject);
});
it('tracks the return value of calls', function() {
var spy = env.createSpy(
const spy = env.createSpy(
TestClass.prototype,
TestClass.prototype.someFunction
);
var trackSpy = spyOn(spy.calls, 'track');
const trackSpy = spyOn(spy.calls, 'track');
spy.and.returnValue('return value');
spy();
@@ -95,7 +95,7 @@ describe('Spies', function() {
});
it('preserves arity of original function', function() {
var functions = [
const functions = [
function nullary() {},
function unary(arg) {},
function binary(arg1, arg2) {},
@@ -105,8 +105,8 @@ describe('Spies', function() {
function senary(arg1, arg2, arg3, arg4, arg5, arg6) {}
];
for (var arity = 0; arity < functions.length; arity++) {
var someFunction = functions[arity],
for (let arity = 0; arity < functions.length; arity++) {
const someFunction = functions[arity],
spy = env.createSpy(someFunction.name, someFunction);
expect(spy.length).toEqual(arity);
@@ -116,7 +116,7 @@ describe('Spies', function() {
describe('createSpyObj', function() {
it('should create an object with spy methods and corresponding return values when you call jasmine.createSpyObj() with an object', function() {
var spyObj = env.createSpyObj('BaseName', {
const spyObj = env.createSpyObj('BaseName', {
method1: 42,
method2: 'special sauce'
});
@@ -129,7 +129,7 @@ describe('Spies', function() {
});
it('should create an object with a bunch of spy methods when you call jasmine.createSpyObj()', function() {
var spyObj = env.createSpyObj('BaseName', ['method1', 'method2']);
const spyObj = env.createSpyObj('BaseName', ['method1', 'method2']);
expect(spyObj).toEqual({
method1: jasmine.any(Function),
@@ -140,7 +140,7 @@ describe('Spies', function() {
});
it('should allow you to omit the baseName', function() {
var spyObj = env.createSpyObj(['method1', 'method2']);
const spyObj = env.createSpyObj(['method1', 'method2']);
expect(spyObj).toEqual({
method1: jasmine.any(Function),
@@ -153,7 +153,7 @@ describe('Spies', function() {
it('should throw if you do not pass an array or object argument', function() {
expect(function() {
env.createSpyObj('BaseName');
}).toThrow(
}).toThrowError(
'createSpyObj requires a non-empty array or object of method names to create spies for'
);
});
@@ -161,7 +161,7 @@ describe('Spies', function() {
it('should throw if you pass an empty array argument', function() {
expect(function() {
env.createSpyObj('BaseName', []);
}).toThrow(
}).toThrowError(
'createSpyObj requires a non-empty array or object of method names to create spies for'
);
});
@@ -169,26 +169,26 @@ describe('Spies', function() {
it('should throw if you pass an empty object argument', function() {
expect(function() {
env.createSpyObj('BaseName', {});
}).toThrow(
}).toThrowError(
'createSpyObj requires a non-empty array or object of method names to create spies for'
);
});
it('creates an object with spy properties if a second list is passed', function() {
var spyObj = env.createSpyObj('base', ['method1'], ['prop1']);
const spyObj = env.createSpyObj('base', ['method1'], ['prop1']);
expect(spyObj).toEqual({
method1: jasmine.any(Function),
prop1: undefined
});
var descriptor = Object.getOwnPropertyDescriptor(spyObj, 'prop1');
const descriptor = Object.getOwnPropertyDescriptor(spyObj, 'prop1');
expect(descriptor.get.and.identity).toEqual('base.prop1.get');
expect(descriptor.set.and.identity).toEqual('base.prop1.set');
});
it('creates an object with property names and return values if second object is passed', function() {
var spyObj = env.createSpyObj('base', ['method1'], {
const spyObj = env.createSpyObj('base', ['method1'], {
prop1: 'foo',
prop2: 37
});
@@ -209,7 +209,7 @@ describe('Spies', function() {
});
it('allows base name to be omitted when assigning methods and properties', function() {
var spyObj = env.createSpyObj({ m: 3 }, { p: 4 });
const spyObj = env.createSpyObj({ m: 3 }, { p: 4 });
expect(spyObj.m()).toEqual(3);
expect(spyObj.p).toEqual(4);
@@ -220,7 +220,7 @@ describe('Spies', function() {
});
it('can use different strategies for different arguments', function() {
var spy = env.createSpy('foo');
const spy = env.createSpy('foo');
spy.and.returnValue(42);
spy.withArgs('baz', 'grault').and.returnValue(-1);
spy.withArgs('thud').and.returnValue('bob');
@@ -232,7 +232,7 @@ describe('Spies', function() {
});
it('uses asymmetric equality testers when selecting a strategy', function() {
var spy = env.createSpy('foo');
const spy = env.createSpy('foo');
spy.and.returnValue(42);
spy.withArgs(jasmineUnderTest.any(String)).and.returnValue(-1);
@@ -258,7 +258,7 @@ describe('Spies', function() {
});
it('can reconfigure an argument-specific strategy', function() {
var spy = env.createSpy('foo');
const spy = env.createSpy('foo');
spy.withArgs('foo').and.returnValue(42);
spy.withArgs('foo').and.returnValue(17);
expect(spy('foo')).toEqual(17);
@@ -266,7 +266,7 @@ describe('Spies', function() {
describe('any promise-based strategy', function() {
it('works with global Promise library', function(done) {
var spy = env.createSpy('foo').and.resolveTo(42);
const spy = env.createSpy('foo').and.resolveTo(42);
spy()
.then(function(result) {
expect(result).toEqual(42);
@@ -278,14 +278,14 @@ describe('Spies', function() {
describe('when withArgs is used without a base strategy', function() {
it('uses the matching strategy', function() {
var spy = env.createSpy('foo');
const spy = env.createSpy('foo');
spy.withArgs('baz').and.returnValue(-1);
expect(spy('baz')).toEqual(-1);
});
it("throws if the args don't match", function() {
var spy = env.createSpy('foo');
const spy = env.createSpy('foo');
spy.withArgs('bar').and.returnValue(-1);
expect(function() {

View File

@@ -1,18 +1,18 @@
describe('SpyStrategy', function() {
it('defaults its name to unknown', function() {
var spyStrategy = new jasmineUnderTest.SpyStrategy();
const spyStrategy = new jasmineUnderTest.SpyStrategy();
expect(spyStrategy.identity).toEqual('unknown');
});
it('takes a name', function() {
var spyStrategy = new jasmineUnderTest.SpyStrategy({ name: 'foo' });
const spyStrategy = new jasmineUnderTest.SpyStrategy({ name: 'foo' });
expect(spyStrategy.identity).toEqual('foo');
});
it('stubs an original function, if provided', function() {
var originalFn = jasmine.createSpy('original'),
const originalFn = jasmine.createSpy('original'),
spyStrategy = new jasmineUnderTest.SpyStrategy({ fn: originalFn });
spyStrategy.exec();
@@ -21,12 +21,11 @@ describe('SpyStrategy', function() {
});
it("allows an original function to be called, passed through the params and returns it's value", function() {
var originalFn = jasmine.createSpy('original').and.returnValue(42),
spyStrategy = new jasmineUnderTest.SpyStrategy({ fn: originalFn }),
returnValue;
const originalFn = jasmine.createSpy('original').and.returnValue(42),
spyStrategy = new jasmineUnderTest.SpyStrategy({ fn: originalFn });
spyStrategy.callThrough();
returnValue = spyStrategy.exec(null, ['foo']);
const returnValue = spyStrategy.exec(null, ['foo']);
expect(originalFn).toHaveBeenCalled();
expect(originalFn.calls.mostRecent().args).toEqual(['foo']);
@@ -34,19 +33,18 @@ describe('SpyStrategy', function() {
});
it('can return a specified value when executed', function() {
var originalFn = jasmine.createSpy('original'),
spyStrategy = new jasmineUnderTest.SpyStrategy({ fn: originalFn }),
returnValue;
const originalFn = jasmine.createSpy('original'),
spyStrategy = new jasmineUnderTest.SpyStrategy({ fn: originalFn });
spyStrategy.returnValue(17);
returnValue = spyStrategy.exec();
const returnValue = spyStrategy.exec();
expect(originalFn).not.toHaveBeenCalled();
expect(returnValue).toEqual(17);
});
it('can return specified values in order specified when executed', function() {
var originalFn = jasmine.createSpy('original'),
const originalFn = jasmine.createSpy('original'),
spyStrategy = new jasmineUnderTest.SpyStrategy({ fn: originalFn });
spyStrategy.returnValues('value1', 'value2', 'value3');
@@ -59,7 +57,7 @@ describe('SpyStrategy', function() {
});
it('allows an exception to be thrown when executed', function() {
var originalFn = jasmine.createSpy('original'),
const originalFn = jasmine.createSpy('original'),
spyStrategy = new jasmineUnderTest.SpyStrategy({ fn: originalFn });
spyStrategy.throwError(new TypeError('bar'));
@@ -71,7 +69,7 @@ describe('SpyStrategy', function() {
});
it('allows a string to be thrown, wrapping it into an exception when executed', function() {
var originalFn = jasmine.createSpy('original'),
const originalFn = jasmine.createSpy('original'),
spyStrategy = new jasmineUnderTest.SpyStrategy({ fn: originalFn });
spyStrategy.throwError('bar');
@@ -83,7 +81,7 @@ describe('SpyStrategy', function() {
});
it('allows a non-Error to be thrown when executed', function() {
var originalFn = jasmine.createSpy('original'),
const originalFn = jasmine.createSpy('original'),
spyStrategy = new jasmineUnderTest.SpyStrategy({ fn: originalFn });
spyStrategy.throwError({ code: 'ESRCH' });
@@ -95,23 +93,22 @@ describe('SpyStrategy', function() {
});
it('allows a fake function to be called instead', function() {
var originalFn = jasmine.createSpy('original'),
const originalFn = jasmine.createSpy('original'),
fakeFn = jasmine.createSpy('fake').and.returnValue(67),
spyStrategy = new jasmineUnderTest.SpyStrategy({ fn: originalFn }),
returnValue;
spyStrategy = new jasmineUnderTest.SpyStrategy({ fn: originalFn });
spyStrategy.callFake(fakeFn);
returnValue = spyStrategy.exec();
const returnValue = spyStrategy.exec();
expect(originalFn).not.toHaveBeenCalled();
expect(returnValue).toEqual(67);
});
it('allows a fake async function to be called instead', function(done) {
var originalFn = jasmine.createSpy('original'),
fakeFn = jasmine
.createSpy('fake')
.and.callFake(eval('async () => { return 67; }')),
const originalFn = jasmine.createSpy('original'),
fakeFn = jasmine.createSpy('fake').and.callFake(async () => {
return 67;
}),
spyStrategy = new jasmineUnderTest.SpyStrategy({ fn: originalFn });
spyStrategy.callFake(fakeFn);
@@ -130,7 +127,7 @@ describe('SpyStrategy', function() {
describe('#resolveTo', function() {
it('allows a resolved promise to be returned', function(done) {
var originalFn = jasmine.createSpy('original'),
const originalFn = jasmine.createSpy('original'),
spyStrategy = new jasmineUnderTest.SpyStrategy({
fn: originalFn
});
@@ -146,7 +143,7 @@ describe('SpyStrategy', function() {
});
it('allows an empty resolved promise to be returned', function(done) {
var originalFn = jasmine.createSpy('original'),
const originalFn = jasmine.createSpy('original'),
spyStrategy = new jasmineUnderTest.SpyStrategy({
fn: originalFn
});
@@ -164,7 +161,7 @@ describe('SpyStrategy', function() {
describe('#rejectWith', function() {
it('allows a rejected promise to be returned', function(done) {
var originalFn = jasmine.createSpy('original'),
const originalFn = jasmine.createSpy('original'),
spyStrategy = new jasmineUnderTest.SpyStrategy({
fn: originalFn
});
@@ -181,7 +178,7 @@ describe('SpyStrategy', function() {
});
it('allows an empty rejected promise to be returned', function(done) {
var originalFn = jasmine.createSpy('original'),
const originalFn = jasmine.createSpy('original'),
spyStrategy = new jasmineUnderTest.SpyStrategy({
fn: originalFn
});
@@ -198,7 +195,7 @@ describe('SpyStrategy', function() {
});
it('allows a non-Error to be rejected', function(done) {
var originalFn = jasmine.createSpy('original'),
const originalFn = jasmine.createSpy('original'),
spyStrategy = new jasmineUnderTest.SpyStrategy({
fn: originalFn
});
@@ -216,7 +213,7 @@ describe('SpyStrategy', function() {
});
it('allows a custom strategy to be used', function() {
var plan = jasmine
const plan = jasmine
.createSpy('custom strategy')
.and.returnValue('custom strategy result'),
customStrategy = jasmine
@@ -239,7 +236,7 @@ describe('SpyStrategy', function() {
});
it("throws an error if a custom strategy doesn't return a function", function() {
var originalFn = jasmine.createSpy('original'),
const originalFn = jasmine.createSpy('original'),
spyStrategy = new jasmineUnderTest.SpyStrategy({
fn: originalFn,
customStrategies: {
@@ -255,7 +252,7 @@ describe('SpyStrategy', function() {
});
it('does not allow custom strategies to overwrite existing methods', function() {
var spyStrategy = new jasmineUnderTest.SpyStrategy({
const spyStrategy = new jasmineUnderTest.SpyStrategy({
fn: function() {},
customStrategies: {
exec: function() {}
@@ -266,7 +263,7 @@ describe('SpyStrategy', function() {
});
it('throws an error when a non-function is passed to callFake strategy', function() {
var originalFn = jasmine.createSpy('original'),
const originalFn = jasmine.createSpy('original'),
spyStrategy = new jasmineUnderTest.SpyStrategy({ fn: originalFn });
spyOn(jasmineUnderTest, 'isFunction_').and.returnValue(false);
@@ -282,7 +279,7 @@ describe('SpyStrategy', function() {
});
it('allows generator functions to be passed to callFake strategy', function() {
var generator = function*() {
const generator = function*() {
yield 'ok';
},
spyStrategy = new jasmineUnderTest.SpyStrategy({ fn: function() {} });
@@ -293,13 +290,12 @@ describe('SpyStrategy', function() {
});
it('allows a return to plan stubbing after another strategy', function() {
var originalFn = jasmine.createSpy('original'),
const originalFn = jasmine.createSpy('original'),
fakeFn = jasmine.createSpy('fake').and.returnValue(67),
spyStrategy = new jasmineUnderTest.SpyStrategy({ fn: originalFn }),
returnValue;
spyStrategy = new jasmineUnderTest.SpyStrategy({ fn: originalFn });
spyStrategy.callFake(fakeFn);
returnValue = spyStrategy.exec();
let returnValue = spyStrategy.exec();
expect(originalFn).not.toHaveBeenCalled();
expect(returnValue).toEqual(67);
@@ -311,7 +307,7 @@ describe('SpyStrategy', function() {
});
it('returns the spy after changing the strategy', function() {
var spy = {},
const spy = {},
spyFn = jasmine.createSpy('spyFn').and.returnValue(spy),
spyStrategy = new jasmineUnderTest.SpyStrategy({ getSpy: spyFn });

View File

@@ -1,6 +1,6 @@
describe('StackTrace', function() {
it('understands Chrome/Edge style traces', function() {
var error = {
const error = {
message: 'nope',
stack:
'Error: nope\n' +
@@ -8,7 +8,7 @@ describe('StackTrace', function() {
' at QueueRunner.run (http://localhost:8888/__jasmine__/jasmine.js:4320:20)'
};
var result = new jasmineUnderTest.StackTrace(error);
const result = new jasmineUnderTest.StackTrace(error);
expect(result.message).toEqual('Error: nope');
expect(result.style).toEqual('v8');
@@ -31,7 +31,7 @@ describe('StackTrace', function() {
});
it('understands Chrome/Edge style traces with multiline messages', function() {
var error = {
const error = {
message: 'line 1\nline 2',
stack:
'Error: line 1\nline 2\n' +
@@ -39,10 +39,31 @@ describe('StackTrace', function() {
' at QueueRunner.run (http://localhost:8888/__jasmine__/jasmine.js:4320:20)'
};
var result = new jasmineUnderTest.StackTrace(error);
const result = new jasmineUnderTest.StackTrace(error);
expect(result.message).toEqual('Error: line 1\nline 2');
var rawFrames = result.frames.map(function(f) {
const rawFrames = result.frames.map(function(f) {
return f.raw;
});
expect(rawFrames).toEqual([
' at UserContext.<anonymous> (http://localhost:8888/__spec__/core/UtilSpec.js:115:19)',
' at QueueRunner.run (http://localhost:8888/__jasmine__/jasmine.js:4320:20)'
]);
});
it('understands Chrome/Edge style traces with messages containing blank lines', function() {
const error = {
message: 'line 1\n\nline 2',
stack:
'Error: line 1\n\nline 2\n' +
' at UserContext.<anonymous> (http://localhost:8888/__spec__/core/UtilSpec.js:115:19)\n' +
' at QueueRunner.run (http://localhost:8888/__jasmine__/jasmine.js:4320:20)'
};
const result = new jasmineUnderTest.StackTrace(error);
expect(result.message).toEqual('Error: line 1\n\nline 2');
const rawFrames = result.frames.map(function(f) {
return f.raw;
});
expect(rawFrames).toEqual([
@@ -52,7 +73,7 @@ describe('StackTrace', function() {
});
it('understands Node style traces', function() {
var error = {
const error = {
message: 'nope',
stack:
'Error\n' +
@@ -61,7 +82,7 @@ describe('StackTrace', function() {
' at Immediate.<anonymous> (/somewhere/jasmine/lib/jasmine-core/jasmine.js:4314:12)\n' +
' at runCallback (timers.js:672:20)'
};
var result = new jasmineUnderTest.StackTrace(error);
const result = new jasmineUnderTest.StackTrace(error);
expect(result.message).toEqual('Error');
expect(result.style).toEqual('v8');
@@ -95,14 +116,14 @@ describe('StackTrace', function() {
]);
});
it('understands Safari <=14/Firefox/Phantom-OS X style traces', function() {
var error = {
it('understands Safari <=14/Firefox style traces', function() {
const error = {
message: 'nope',
stack:
'http://localhost:8888/__spec__/core/UtilSpec.js:115:28\n' +
'run@http://localhost:8888/__jasmine__/jasmine.js:4320:27'
};
var result = new jasmineUnderTest.StackTrace(error);
const result = new jasmineUnderTest.StackTrace(error);
expect(result.message).toBeFalsy();
expect(result.style).toEqual('webkit');
@@ -123,13 +144,13 @@ describe('StackTrace', function() {
});
it('understands Safari 15 style traces', function() {
var error = {
const error = {
message: 'nope',
stack:
'@http://localhost:8888/__spec__/core/FooSpec.js:164:24\n' +
'attempt@http://localhost:8888/__jasmine__/jasmine.js:8074:44\n'
};
var result = new jasmineUnderTest.StackTrace(error);
const result = new jasmineUnderTest.StackTrace(error);
expect(result.message).toBeFalsy();
expect(result.style).toEqual('webkit');
@@ -149,54 +170,24 @@ describe('StackTrace', function() {
]);
});
it('does not mistake gibberish for Safari/Firefox/Phantom-OS X style traces', function() {
var error = {
it('does not mistake gibberish for Safari/Firefox style traces', function() {
const error = {
message: 'nope',
stack: 'randomcharsnotincludingwhitespace'
};
var result = new jasmineUnderTest.StackTrace(error);
const result = new jasmineUnderTest.StackTrace(error);
expect(result.style).toBeNull();
expect(result.frames).toEqual([{ raw: error.stack }]);
});
it('understands Phantom-Linux style traces', function() {
var error = {
message: 'nope',
stack:
' at UserContext.<anonymous> (http://localhost:8888/__spec__/core/UtilSpec.js:115:19)\n' +
' at QueueRunner.run (http://localhost:8888/__jasmine__/jasmine.js:4320:20)'
};
var result = new jasmineUnderTest.StackTrace(error);
expect(result.message).toBeFalsy();
expect(result.style).toEqual('v8');
expect(result.frames).toEqual([
{
raw:
' at UserContext.<anonymous> (http://localhost:8888/__spec__/core/UtilSpec.js:115:19)',
func: 'UserContext.<anonymous>',
file: 'http://localhost:8888/__spec__/core/UtilSpec.js',
line: 115
},
{
raw:
' at QueueRunner.run (http://localhost:8888/__jasmine__/jasmine.js:4320:20)',
func: 'QueueRunner.run',
file: 'http://localhost:8888/__jasmine__/jasmine.js',
line: 4320
}
]);
});
it('ignores blank lines', function() {
var error = {
const error = {
message: 'nope',
stack:
' at UserContext.<anonymous> (http://localhost:8888/__spec__/core/UtilSpec.js:115:19)\n'
};
var result = new jasmineUnderTest.StackTrace(error);
const result = new jasmineUnderTest.StackTrace(error);
expect(result.frames).toEqual([
{
@@ -210,7 +201,7 @@ describe('StackTrace', function() {
});
it("omits properties except 'raw' for frames that are not understood", function() {
var error = {
const error = {
message: 'nope',
stack:
' at UserContext.<anonymous> (http://localhost:8888/__spec__/core/UtilSpec.js:115:19)\n' +
@@ -218,7 +209,7 @@ describe('StackTrace', function() {
' at QueueRunner.run (http://localhost:8888/__jasmine__/jasmine.js:4320:20)'
};
var result = new jasmineUnderTest.StackTrace(error);
const result = new jasmineUnderTest.StackTrace(error);
expect(result.style).toEqual('v8');
expect(result.frames).toEqual([
{
@@ -241,8 +232,8 @@ describe('StackTrace', function() {
]);
});
it('consideres different types of errors', function() {
var error = {
it('considers different types of errors', function() {
const error = {
message: 'nope',
stack:
'TypeError: nope\n' +
@@ -250,7 +241,7 @@ describe('StackTrace', function() {
' at QueueRunner.run (http://localhost:8888/__jasmine__/jasmine.js:4320:20)'
};
var result = new jasmineUnderTest.StackTrace(error);
const result = new jasmineUnderTest.StackTrace(error);
expect(result.message).toEqual('TypeError: nope');
expect(result.frames).toEqual([
@@ -270,7 +261,7 @@ describe('StackTrace', function() {
}
]);
var no_error = {
const no_error = {
message: 'nope',
stack:
'Type Error: nope\n' +
@@ -278,7 +269,7 @@ describe('StackTrace', function() {
' at QueueRunner.run (http://localhost:8888/__jasmine__/jasmine.js:4320:20)'
};
var result_no_error = new jasmineUnderTest.StackTrace(no_error);
const result_no_error = new jasmineUnderTest.StackTrace(no_error);
expect(result_no_error.message).not.toEqual(jasmine.anything());
});

View File

@@ -0,0 +1,390 @@
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);
});
it('gives each spec a full path', function() {
const env = { configuration: () => ({}) };
const suiteBuilder = new jasmineUnderTest.SuiteBuilder({ env });
let spec;
suiteBuilder.describe('a suite', function() {
suiteBuilder.describe('a nested suite', function() {
spec = suiteBuilder[fnName]('a spec', function() {});
});
});
expect(spec.getPath()).toEqual(['a suite', 'a nested suite', 'a spec']);
});
}
function sameInstanceAs(expected) {
return {
asymmetricMatch: function(actual) {
return actual === expected;
},
jasmineToString: function(pp) {
return '<same instance as ' + pp(expected) + '>';
}
};
}
describe('Duplicate name handling', function() {
describe('When forbidDuplicateNames is true', function() {
let env;
beforeEach(function() {
env = { configuration: () => ({ forbidDuplicateNames: true }) };
});
it('forbids duplicate spec names', function() {
const suiteBuilder = new jasmineUnderTest.SuiteBuilder({ env });
expect(function() {
suiteBuilder.describe('a suite', function() {
suiteBuilder.describe('a nested suite', function() {
suiteBuilder.it('a spec');
suiteBuilder.it('a spec');
});
});
}).toThrowError(
'Duplicate spec name "a spec" found in "a suite a nested suite"'
);
});
it('forbids duplicate spec names in the top suite', function() {
const suiteBuilder = new jasmineUnderTest.SuiteBuilder({ env });
expect(function() {
suiteBuilder.it('another spec');
suiteBuilder.it('another spec');
}).toThrowError(
'Duplicate spec name "another spec" found in top suite'
);
});
it('forbids duplicate suite names', function() {
const suiteBuilder = new jasmineUnderTest.SuiteBuilder({ env });
expect(function() {
suiteBuilder.describe('a suite', function() {
suiteBuilder.describe('a nested suite', function() {
suiteBuilder.describe('another suite', function() {
suiteBuilder.it('a spec');
});
suiteBuilder.describe('another suite', function() {
suiteBuilder.it('a spec');
});
});
});
}).toThrowError(
'Duplicate suite name "another suite" found in "a suite a nested suite"'
);
});
it('forbids duplicate suite names in the top suite', function() {
const suiteBuilder = new jasmineUnderTest.SuiteBuilder({ env });
expect(function() {
suiteBuilder.describe('a suite', function() {
suiteBuilder.it('a spec');
});
suiteBuilder.describe('a suite', function() {
suiteBuilder.it('a spec');
});
}).toThrowError('Duplicate suite name "a suite" found in top suite');
});
it('allows spec and suite names to be duplicated in different suites', function() {
const suiteBuilder = new jasmineUnderTest.SuiteBuilder({ env });
expect(function() {
suiteBuilder.describe('suite a', function() {
suiteBuilder.describe('dupe suite', function() {
suiteBuilder.it('dupe spec');
suiteBuilder.describe('child suite', function() {
suiteBuilder.it('dupe spec');
});
});
});
suiteBuilder.describe('suite b', function() {
suiteBuilder.describe('dupe suite', function() {
suiteBuilder.it('dupe spec');
});
});
}).not.toThrow();
});
});
describe('When forbidDuplicateNames is false', function() {
let env;
beforeEach(function() {
env = { configuration: () => ({ forbidDuplicateNames: false }) };
});
it('allows duplicate spec and suite names', function() {
const suiteBuilder = new jasmineUnderTest.SuiteBuilder({ env });
expect(function() {
suiteBuilder.describe('dupe suite', function() {
suiteBuilder.it('dupe spec');
suiteBuilder.it('dupe spec');
});
suiteBuilder.describe('dupe suite', function() {
suiteBuilder.it('dupe spec');
suiteBuilder.it('dupe spec');
});
}).not.toThrow();
});
});
});
describe('#parallelReset', function() {
it('resets the top suite result', function() {
jasmineUnderTest.Suite.prototype.handleException.and.callThrough();
const env = { configuration: () => ({}) };
const suiteBuilder = new jasmineUnderTest.SuiteBuilder({ env });
suiteBuilder.topSuite.handleException(new Error('nope'));
suiteBuilder.parallelReset();
expect(suiteBuilder.topSuite.result).toEqual({
id: suiteBuilder.topSuite.id,
description: 'Jasmine__TopLevel__Suite',
fullName: '',
failedExpectations: [],
deprecationWarnings: [],
duration: null,
properties: null,
parentSuiteId: null,
filename: undefined
});
});
it('removes children of the top suite', function() {
const env = { configuration: () => ({}) };
const suiteBuilder = new jasmineUnderTest.SuiteBuilder({ env });
suiteBuilder.describe('a suite', function() {
suiteBuilder.it('a nested spec');
});
suiteBuilder.it('a spec');
suiteBuilder.parallelReset();
expect(suiteBuilder.topSuite.children).toEqual([]);
});
it('preserves top suite befores and afters', function() {
const env = { configuration: () => ({}) };
const suiteBuilder = new jasmineUnderTest.SuiteBuilder({ env });
function beforeAll() {}
function beforeEach() {}
function afterEach() {}
function afterAll() {}
suiteBuilder.beforeAll(beforeAll);
suiteBuilder.beforeEach(beforeEach);
suiteBuilder.afterEach(afterEach);
suiteBuilder.afterAll(afterAll);
suiteBuilder.parallelReset();
expect(suiteBuilder.topSuite.beforeAllFns).toEqual([
jasmine.objectContaining({ fn: beforeAll })
]);
expect(suiteBuilder.topSuite.beforeFns).toEqual([
jasmine.objectContaining({ fn: beforeEach })
]);
expect(suiteBuilder.topSuite.afterFns).toEqual([
jasmine.objectContaining({ fn: afterEach })
]);
expect(suiteBuilder.topSuite.afterAllFns).toEqual([
jasmine.objectContaining({ fn: afterAll })
]);
});
it('resets totalSpecsDefined', function() {
const env = { configuration: () => ({}) };
const suiteBuilder = new jasmineUnderTest.SuiteBuilder({ env });
suiteBuilder.it('a spec');
suiteBuilder.parallelReset();
expect(suiteBuilder.totalSpecsDefined).toEqual(0);
});
it('resets focusedRunables', function() {
const env = { configuration: () => ({}) };
const suiteBuilder = new jasmineUnderTest.SuiteBuilder({ env });
suiteBuilder.fit('a spec', function() {});
suiteBuilder.parallelReset();
expect(suiteBuilder.focusedRunables).toEqual([]);
});
});
});

View File

@@ -1,5 +1,5 @@
describe('Suite', function() {
var env;
let env;
beforeEach(function() {
env = new jasmineUnderTest.Env();
@@ -10,7 +10,7 @@ describe('Suite', function() {
});
it('keeps its id', function() {
var suite = new jasmineUnderTest.Suite({
const suite = new jasmineUnderTest.Suite({
env: env,
id: 456,
description: 'I am a suite'
@@ -20,7 +20,7 @@ describe('Suite', function() {
});
it('returns blank full name for top level suite', function() {
var suite = new jasmineUnderTest.Suite({
const suite = new jasmineUnderTest.Suite({
env: env,
description: 'I am a suite'
});
@@ -29,7 +29,7 @@ describe('Suite', function() {
});
it('returns its full name when it has parent suites', function() {
var parentSuite = new jasmineUnderTest.Suite({
const parentSuite = new jasmineUnderTest.Suite({
env: env,
description: 'I am a parent suite',
parentSuite: jasmine.createSpy('pretend top level suite')
@@ -44,7 +44,7 @@ describe('Suite', function() {
});
it('adds beforeEach functions in order of needed execution', function() {
var suite = new jasmineUnderTest.Suite({
const suite = new jasmineUnderTest.Suite({
env: env,
description: 'I am a suite'
}),
@@ -61,7 +61,7 @@ describe('Suite', function() {
});
it('adds beforeAll functions in order of needed execution', function() {
var suite = new jasmineUnderTest.Suite({
const suite = new jasmineUnderTest.Suite({
env: env,
description: 'I am a suite'
}),
@@ -71,25 +71,14 @@ 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) }
]);
});
it('adds afterEach functions in order of needed execution', function() {
var suite = new jasmineUnderTest.Suite({
const suite = new jasmineUnderTest.Suite({
env: env,
description: 'I am a suite'
}),
@@ -123,55 +112,133 @@ describe('Suite', function() {
});
it('has a status of failed if any expectations have failed', function() {
var suite = new jasmineUnderTest.Suite({
expectationResultFactory: function() {
return 'hi';
}
});
const suite = new jasmineUnderTest.Suite({});
suite.addExpectationResult(false);
suite.addExpectationResult(false, {});
expect(suite.status()).toBe('failed');
});
it('retrieves a result with updated status', function() {
var suite = new jasmineUnderTest.Suite({});
const suite = new jasmineUnderTest.Suite({});
expect(suite.getResult().status).toBe('passed');
});
it('retrieves a result with pending status', function() {
var suite = new jasmineUnderTest.Suite({});
const suite = new jasmineUnderTest.Suite({});
suite.pend();
expect(suite.getResult().status).toBe('pending');
});
it('throws an ExpectationFailed when receiving a failed expectation when throwOnExpectationFailure is set', function() {
var suite = new jasmineUnderTest.Suite({
expectationResultFactory: function(data) {
return data;
},
const suite = new jasmineUnderTest.Suite({
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() {
var suite = new jasmineUnderTest.Suite({});
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() {
var suite = new jasmineUnderTest.Suite({
const suite = new jasmineUnderTest.Suite({
env: env,
id: 456,
description: 'I am a suite',
@@ -196,7 +263,7 @@ describe('Suite', function() {
describe('attr.autoCleanClosures', function() {
function arrangeSuite(attrs) {
var suite = new jasmineUnderTest.Suite(attrs);
const suite = new jasmineUnderTest.Suite(attrs);
suite.beforeAll(function() {});
suite.beforeEach(function() {});
suite.afterEach(function() {});
@@ -205,7 +272,7 @@ describe('Suite', function() {
}
it('should clean closures when "attr.autoCleanClosures" is missing', function() {
var suite = arrangeSuite({});
const suite = arrangeSuite({});
suite.cleanupBeforeAfter();
expect(suite.beforeAllFns[0].fn).toBe(null);
expect(suite.beforeFns[0].fn).toBe(null);
@@ -214,7 +281,7 @@ describe('Suite', function() {
});
it('should clean closures when "attr.autoCleanClosures" is true', function() {
var suite = arrangeSuite({ autoCleanClosures: true });
const suite = arrangeSuite({ autoCleanClosures: true });
suite.cleanupBeforeAfter();
expect(suite.beforeAllFns[0].fn).toBe(null);
expect(suite.beforeFns[0].fn).toBe(null);
@@ -223,7 +290,7 @@ describe('Suite', function() {
});
it('should NOT clean closures when "attr.autoCleanClosures" is false', function() {
var suite = arrangeSuite({ autoCleanClosures: false });
const suite = arrangeSuite({ autoCleanClosures: false });
suite.cleanupBeforeAfter();
expect(suite.beforeAllFns[0].fn).not.toBe(null);
expect(suite.beforeFns[0].fn).not.toBe(null);
@@ -234,23 +301,23 @@ describe('Suite', function() {
describe('#reset', function() {
it('should reset the "pending" status', function() {
var suite = new jasmineUnderTest.Suite({});
const suite = new jasmineUnderTest.Suite({});
suite.pend();
suite.reset();
expect(suite.getResult().status).toBe('passed');
});
it('should not reset the "pending" status when the suite was excluded', function() {
var suite = new jasmineUnderTest.Suite({});
const suite = new jasmineUnderTest.Suite({});
suite.exclude();
suite.reset();
expect(suite.getResult().status).toBe('pending');
});
it('should also reset the children', function() {
var suite = new jasmineUnderTest.Suite({});
var child1 = jasmine.createSpyObj(['reset']);
var child2 = jasmine.createSpyObj(['reset']);
const suite = new jasmineUnderTest.Suite({});
const child1 = jasmine.createSpyObj(['reset']);
const child2 = jasmine.createSpyObj(['reset']);
suite.addChild(child1);
suite.addChild(child2);
@@ -261,16 +328,12 @@ describe('Suite', function() {
});
it('should reset the failedExpectations', function() {
var suite = new jasmineUnderTest.Suite({
expectationResultFactory: function(error) {
return error;
}
});
suite.onException(new Error());
const suite = new jasmineUnderTest.Suite({});
suite.handleException(new Error());
suite.reset();
var result = suite.getResult();
const result = suite.getResult();
expect(result.status).toBe('passed');
expect(result.failedExpectations).toHaveSize(0);
});
@@ -296,7 +359,7 @@ describe('Suite', function() {
it('reports an error including the suite name when it is a normal suite', function() {
const onLateError = jasmine.createSpy('onLateError');
var suite = new jasmineUnderTest.Suite({
const suite = new jasmineUnderTest.Suite({
onLateError,
description: 'the suite',
parentSuite: {
@@ -315,4 +378,31 @@ describe('Suite', function() {
);
});
});
describe('#hasChildWithDescription', function() {
it('returns true if there is a child with the given description', function() {
const subject = new jasmineUnderTest.Suite({});
const description = 'a spec';
subject.addChild({ description });
expect(subject.hasChildWithDescription(description)).toBeTrue();
});
it('returns false if there is no child with the given description', function() {
const subject = new jasmineUnderTest.Suite({});
subject.addChild({ description: 'a different spec' });
expect(subject.hasChildWithDescription('a spec')).toBeFalse();
});
it('does not recurse into child suites', function() {
const subject = new jasmineUnderTest.Suite({});
const childSuite = new jasmineUnderTest.Suite({});
subject.addChild(childSuite);
const description = 'a spec';
childSuite.addChild(description);
expect(subject.hasChildWithDescription('a spec')).toBeFalse();
});
});
});

View File

@@ -1,6 +1,6 @@
describe('Timer', function() {
it('reports the time elapsed', function() {
var fakeNow = jasmine.createSpy('fake Date.now'),
const fakeNow = jasmine.createSpy('fake Date.now'),
timer = new jasmineUnderTest.Timer({ now: fakeNow });
fakeNow.and.returnValue(100);
@@ -12,7 +12,7 @@ describe('Timer', function() {
});
describe('when date is stubbed, perhaps by other testing helpers', function() {
var origDate = Date;
const origDate = Date;
beforeEach(function() {
// eslint-disable-next-line no-implicit-globals
Date = jasmine.createSpy('date spy');
@@ -24,7 +24,7 @@ describe('Timer', function() {
});
it('does not throw even though Date was taken away', function() {
var timer = new jasmineUnderTest.Timer();
const timer = new jasmineUnderTest.Timer();
expect(timer.start).not.toThrow();
expect(timer.elapsed()).toEqual(jasmine.any(Number));

File diff suppressed because it is too large Load Diff

542
spec/core/TreeRunnerSpec.js Normal file
View File

@@ -0,0 +1,542 @@
describe('TreeRunner', function() {
describe('spec execution', function() {
it('starts the timer, reports the spec started, and updates run state at the start of the queue', async function() {
const timer = jasmine.createSpyObj('timer', ['start']);
const spec = new jasmineUnderTest.Spec({
id: 'spec1',
queueableFn: {},
timer
});
const {
runQueue,
currentRunableTracker,
runableResources,
reportDispatcher,
suiteRunQueueArgs,
executePromise
} = runSingleSpecSuite(spec);
suiteRunQueueArgs.queueableFns[0].fn();
expect(runQueue).toHaveBeenCalledTimes(1);
const specRunQueueArgs = runQueue.calls.mostRecent().args[0];
const next = jasmine.createSpy('next');
specRunQueueArgs.queueableFns[0].fn(next);
expect(timer.start).toHaveBeenCalled();
expect(currentRunableTracker.currentRunable()).toBe(spec);
expect(runableResources.initForRunable).toHaveBeenCalledWith(
spec.id,
spec.parentSuiteId
);
expect(reportDispatcher.specStarted).toHaveBeenCalledWith(spec.result);
await Promise.resolve();
expect(reportDispatcher.specStarted).toHaveBeenCalledBefore(next);
await expectAsync(executePromise).toBePending();
});
it('stops the timer, updates run state, and reports the spec done at the end of the queue', async function() {
const timer = jasmine.createSpyObj('timer', ['start', 'elapsed']);
const spec = new jasmineUnderTest.Spec({
id: 'spec1',
queueableFn: {},
timer
});
const {
runQueue,
currentRunableTracker,
runableResources,
reportDispatcher,
suiteRunQueueArgs,
executePromise
} = runSingleSpecSuite(spec);
suiteRunQueueArgs.queueableFns[0].fn();
expect(runQueue).toHaveBeenCalledTimes(1);
const specRunQueueArgs = runQueue.calls.mostRecent().args[0];
const next = jasmine.createSpy('next');
timer.elapsed.and.returnValue('the elapsed time');
currentRunableTracker.setCurrentSpec(spec);
specRunQueueArgs.queueableFns[1].fn(next);
expect(currentRunableTracker.currentSpec()).toBeFalsy();
expect(runableResources.clearForRunable).toHaveBeenCalledWith(spec.id);
expect(reportDispatcher.specDone).toHaveBeenCalledWith(spec.result);
expect(spec.result.duration).toEqual('the elapsed time');
expect(spec.reportedDone).toEqual(true);
await Promise.resolve();
await Promise.resolve();
await Promise.resolve();
expect(reportDispatcher.specDone).toHaveBeenCalledBefore(next);
await expectAsync(executePromise).toBePending();
});
it('runs before and after fns', function() {
const before = { fn: jasmine.createSpy('before') };
const after = { fn: jasmine.createSpy('after') };
const queueableFn = {
fn: jasmine.createSpy('test body').and.callFake(function() {
expect(before).toHaveBeenCalled();
expect(after).not.toHaveBeenCalled();
})
};
const spec = new jasmineUnderTest.Spec({
queueableFn: queueableFn,
beforeAndAfterFns: function() {
return { befores: [before], afters: [after] };
}
});
const { runQueue, suiteRunQueueArgs } = runSingleSpecSuite(spec);
suiteRunQueueArgs.queueableFns[0].fn();
expect(runQueue).toHaveBeenCalledTimes(1);
const specRunQueueArgs = runQueue.calls.mostRecent().args[0];
expect(specRunQueueArgs.queueableFns[1]).toEqual(before);
expect(specRunQueueArgs.queueableFns[2]).toEqual(queueableFn);
expect(specRunQueueArgs.queueableFns[3]).toEqual(after);
});
it('marks specs pending at runtime', function() {
let spec;
const queueableFn = {
fn() {
spec.pend();
}
};
spec = new jasmineUnderTest.Spec({ queueableFn });
const { runQueue, suiteRunQueueArgs } = runSingleSpecSuite(spec);
suiteRunQueueArgs.queueableFns[0].fn();
expect(runQueue).toHaveBeenCalledTimes(1);
const specRunQueueArgs = runQueue.calls.mostRecent().args[0];
expect(specRunQueueArgs.queueableFns[1]).toEqual(queueableFn);
queueableFn.fn();
expect(spec.status()).toEqual('pending');
expect(spec.getResult().status).toEqual('pending');
expect(spec.getResult().pendingReason).toEqual('');
});
it('marks specs pending at runtime with a message', function() {
let spec;
const queueableFn = {
fn() {
spec.pend('some reason');
}
};
spec = new jasmineUnderTest.Spec({ queueableFn });
const { runQueue, suiteRunQueueArgs } = runSingleSpecSuite(spec);
suiteRunQueueArgs.queueableFns[0].fn();
expect(runQueue).toHaveBeenCalledTimes(1);
const specRunQueueArgs = runQueue.calls.mostRecent().args[0];
expect(specRunQueueArgs.queueableFns[1]).toEqual(queueableFn);
queueableFn.fn();
expect(spec.status()).toEqual('pending');
expect(spec.getResult().status).toEqual('pending');
expect(spec.getResult().pendingReason).toEqual('some reason');
});
it('passes failSpecWithNoExp to Spec#executionFinished', async function() {
const spec = new jasmineUnderTest.Spec({
id: 'spec1',
queueableFn: {}
});
spyOn(spec, 'executionFinished');
const {
runQueue,
suiteRunQueueArgs,
executePromise
} = runSingleSpecSuite(spec, { failSpecWithNoExpectations: true });
suiteRunQueueArgs.queueableFns[0].fn();
expect(runQueue).toHaveBeenCalledTimes(1);
const specRunQueueArgs = runQueue.calls.mostRecent().args[0];
expect(specRunQueueArgs.queueableFns[1].type).toEqual('specCleanup');
specRunQueueArgs.queueableFns[1].fn();
expect(spec.executionFinished).toHaveBeenCalledWith(false, true);
await expectAsync(executePromise).toBePending();
});
describe('Late promise rejection handling', function() {
it('is enabled when the detectLateRejectionHandling param is true', function() {
const before = jasmine.createSpy('before');
const after = jasmine.createSpy('after');
const queueableFn = {
fn: jasmine.createSpy('test body').and.callFake(function() {
expect(before).toHaveBeenCalled();
expect(after).not.toHaveBeenCalled();
})
};
const spec = new jasmineUnderTest.Spec({
queueableFn,
beforeAndAfterFns: function() {
return { befores: [before], afters: [after] };
}
});
const {
runQueue,
setTimeout,
suiteRunQueueArgs,
globalErrors
} = runSingleSpecSuite(spec, { detectLateRejectionHandling: true });
suiteRunQueueArgs.queueableFns[0].fn();
expect(runQueue).toHaveBeenCalledTimes(1);
const specRunQueueOpts = runQueue.calls.mostRecent().args[0];
expect(specRunQueueOpts.queueableFns).toEqual([
{ fn: jasmine.any(Function) },
before,
queueableFn,
after,
{ fn: jasmine.any(Function) },
{
fn: jasmine.any(Function),
type: 'specCleanup'
}
]);
const done = jasmine.createSpy('done');
specRunQueueOpts.queueableFns[4].fn(done);
expect(globalErrors.reportUnhandledRejections).not.toHaveBeenCalled();
expect(done).not.toHaveBeenCalled();
expect(setTimeout).toHaveBeenCalledOnceWith(jasmine.any(Function));
setTimeout.calls.argsFor(0)[0]();
expect(globalErrors.reportUnhandledRejections).toHaveBeenCalled();
expect(globalErrors.reportUnhandledRejections).toHaveBeenCalledBefore(
done
);
});
});
function runSingleSpecSuite(spec, optionalConfig) {
const topSuiteId = 'suite1';
spec.parentSuiteId = topSuiteId;
const topSuite = new jasmineUnderTest.Suite({ id: topSuiteId });
topSuite.addChild(spec);
const executionTree = {
topSuite,
childrenOfTopSuite() {
return [{ spec }];
},
isExcluded() {
return false;
}
};
const runQueue = jasmine.createSpy('runQueue');
const reportDispatcher = mockReportDispatcher();
const runableResources = mockRunableResources();
const globalErrors = mockGlobalErrors();
const setTimeout = jasmine.createSpy('setTimeout');
const currentRunableTracker = new jasmineUnderTest.CurrentRunableTracker();
const subject = new jasmineUnderTest.TreeRunner({
executionTree,
runQueue,
globalErrors,
setTimeout,
runableResources,
reportDispatcher,
currentRunableTracker,
getConfig() {
return optionalConfig || {};
},
reportChildrenOfBeforeAllFailure() {}
});
const executePromise = subject.execute();
expect(runQueue).toHaveBeenCalledTimes(1);
const suiteRunQueueArgs = runQueue.calls.mostRecent().args[0];
runQueue.calls.reset();
return {
runQueue,
globalErrors,
setTimeout,
currentRunableTracker,
runableResources,
reportDispatcher,
suiteRunQueueArgs,
executePromise
};
}
});
describe('Suite execution', function() {
it('reports the duration of the suite', async function() {
const timer = jasmine.createSpyObj('timer', ['start', 'elapsed']);
const topSuite = new jasmineUnderTest.Suite({ id: 'topSuite' });
const suite = new jasmineUnderTest.Suite({
id: 'suite1',
parentSuite: topSuite,
timer
});
topSuite.addChild(suite);
const executionTree = {
topSuite,
childrenOfTopSuite() {
return [{ suite }];
},
childrenOfSuiteSegment() {
return [];
},
isExcluded() {
return false;
}
};
const runQueue = jasmine.createSpy('runQueue');
const reportDispatcher = mockReportDispatcher();
const subject = new jasmineUnderTest.TreeRunner({
executionTree,
runQueue,
globalErrors: mockGlobalErrors(),
runableResources: mockRunableResources(),
reportDispatcher,
currentRunableTracker: new jasmineUnderTest.CurrentRunableTracker(),
getConfig() {
return {};
},
reportChildrenOfBeforeAllFailure() {}
});
const executePromise = subject.execute();
expect(runQueue).toHaveBeenCalledTimes(1);
const topSuiteRunQueueOpts = runQueue.calls.mostRecent().args[0];
runQueue.calls.reset();
topSuiteRunQueueOpts.queueableFns[0].fn(function() {});
expect(runQueue).toHaveBeenCalledTimes(1);
expect(timer.start).not.toHaveBeenCalled();
const suiteRunQueueOpts = runQueue.calls.mostRecent().args[0];
suiteRunQueueOpts.queueableFns[0].fn();
expect(timer.start).toHaveBeenCalled();
expect(timer.elapsed).not.toHaveBeenCalled();
timer.elapsed.and.returnValue('the duration');
suiteRunQueueOpts.onComplete();
expect(timer.elapsed).toHaveBeenCalled();
const result = suite.getResult();
expect(result.duration).toEqual('the duration');
expect(reportDispatcher.suiteDone).toHaveBeenCalledWith(result);
await expectAsync(executePromise).toBePending();
});
it('returns false if a suite failed', async function() {
const topSuite = new jasmineUnderTest.Suite({ id: 'topSuite' });
const failingSuite = new jasmineUnderTest.Suite({
id: 'failingSuite',
parentSuite: topSuite
});
const passingSuite = new jasmineUnderTest.Suite({
id: 'passingSuite',
parentSuite: topSuite
});
const executionTree = {
topSuite,
childrenOfTopSuite() {
return [{ suite: failingSuite }, { suite: passingSuite }];
},
childrenOfSuiteSegment() {
return [];
},
isExcluded() {
return false;
}
};
const runQueue = jasmine.createSpy('runQueue');
const reportDispatcher = mockReportDispatcher();
const subject = new jasmineUnderTest.TreeRunner({
executionTree,
runQueue,
globalErrors: mockGlobalErrors(),
runableResources: mockRunableResources(),
reportDispatcher,
currentRunableTracker: new jasmineUnderTest.CurrentRunableTracker(),
getConfig() {
return {};
},
reportChildrenOfBeforeAllFailure() {}
});
const executePromise = subject.execute();
expect(runQueue).toHaveBeenCalledTimes(1);
const topSuiteRunQueueOpts = runQueue.calls.mostRecent().args[0];
runQueue.calls.reset();
topSuiteRunQueueOpts.queueableFns[0].fn(function() {});
// Fail the first suite.
expect(runQueue).toHaveBeenCalledTimes(1);
const failingSuiteRunQueueOpts = runQueue.calls.mostRecent().args[0];
runQueue.calls.reset();
failingSuiteRunQueueOpts.queueableFns[0].fn();
failingSuite.addExpectationResult(false, {});
failingSuiteRunQueueOpts.onComplete();
// Passing the second suite should not reset the overall result.
topSuiteRunQueueOpts.queueableFns[1].fn(function() {});
expect(runQueue).toHaveBeenCalledTimes(1);
const passingSuiteRunQueueOpts = runQueue.calls.mostRecent().args[0];
passingSuiteRunQueueOpts.queueableFns[0].fn();
passingSuiteRunQueueOpts.onComplete();
topSuiteRunQueueOpts.onComplete();
const result = await executePromise;
expect(result.hasFailures).toEqual(true);
});
it('reports children when there is a beforeAll failure', async function() {
const topSuite = new jasmineUnderTest.Suite({ id: 'topSuite' });
const suite = new jasmineUnderTest.Suite({
id: 'suite',
parentSuite: topSuite
});
suite.beforeAll({ fn() {} });
const spec = new jasmineUnderTest.Spec({
id: 'spec',
parentSuite: suite,
queueableFn: { fn() {} }
});
suite.addChild(spec);
topSuite.addChild(suite);
const executionTree = {
topSuite,
childrenOfTopSuite() {
return [{ suite }];
},
childrenOfSuiteSegment() {
return [{ spec }];
},
isExcluded() {
return false;
}
};
const runQueue = jasmine.createSpy('runQueue');
const reportDispatcher = mockReportDispatcher();
const reportChildrenOfBeforeAllFailure = jasmine
.createSpy('reportChildrenOfBeforeAllFailure')
.and.returnValue(Promise.resolve());
const subject = new jasmineUnderTest.TreeRunner({
executionTree,
runQueue,
globalErrors: mockGlobalErrors(),
runableResources: mockRunableResources(),
reportDispatcher,
currentRunableTracker: new jasmineUnderTest.CurrentRunableTracker(),
reportChildrenOfBeforeAllFailure,
getConfig() {
return {};
}
});
const executePromise = subject.execute();
expect(runQueue).toHaveBeenCalledTimes(1);
const topSuiteRunQueueOpts = runQueue.calls.mostRecent().args[0];
runQueue.calls.reset();
topSuiteRunQueueOpts.queueableFns[0].fn(function() {});
expect(runQueue).toHaveBeenCalledTimes(1);
const suiteRunQueueOpts = runQueue.calls.mostRecent().args[0];
suiteRunQueueOpts.queueableFns[0].fn();
suite.hadBeforeAllFailure = true;
suiteRunQueueOpts.onComplete();
while (reportDispatcher.suiteDone.calls.count() === 0) {
await Promise.resolve();
}
expect(reportDispatcher.specDone).toHaveBeenCalledBefore(
reportDispatcher.suiteDone
);
await expectAsync(executePromise).toBePending();
});
it('throws if the wrong suite is completed', async function() {
const topSuite = new jasmineUnderTest.Suite({ id: 'topSuite' });
const suite = new jasmineUnderTest.Suite({
id: 'suite',
parentSuite: topSuite
});
const spec = new jasmineUnderTest.Spec({
id: 'spec',
parentSuite: suite,
queueableFn: { fn() {} }
});
const executionTree = {
topSuite,
childrenOfTopSuite() {
return [{ suite }];
},
childrenOfSuiteSegment() {
return [{ spec }];
},
isExcluded() {
return false;
}
};
const runQueue = jasmine.createSpy('runQueue');
const reportDispatcher = mockReportDispatcher();
const subject = new jasmineUnderTest.TreeRunner({
executionTree,
runQueue,
globalErrors: mockGlobalErrors(),
runableResources: mockRunableResources(),
reportDispatcher,
currentRunableTracker: new jasmineUnderTest.CurrentRunableTracker(),
getConfig() {
return {};
},
reportChildrenOfBeforeAllFailure() {}
});
const executePromise = subject.execute();
expect(runQueue).toHaveBeenCalledTimes(1);
const topSuiteRunQueueOpts = runQueue.calls.mostRecent().args[0];
runQueue.calls.reset();
topSuiteRunQueueOpts.queueableFns[0].fn(function() {});
expect(runQueue).toHaveBeenCalledTimes(1);
const suiteRunQueueOpts = runQueue.calls.mostRecent().args[0];
// Complete the suite without starting it
expect(function() {
suiteRunQueueOpts.onComplete();
}).toThrowError('Tried to complete the wrong suite');
await expectAsync(executePromise).toBePending();
});
});
function mockReportDispatcher() {
const reportDispatcher = jasmine.createSpyObj(
'reportDispatcher',
jasmineUnderTest.reporterEvents
);
for (const k of jasmineUnderTest.reporterEvents) {
reportDispatcher[k].and.returnValue(Promise.resolve());
}
return reportDispatcher;
}
function mockRunableResources() {
return jasmine.createSpyObj('runableResources', [
'initForRunable',
'clearForRunable'
]);
}
function mockGlobalErrors() {
return jasmine.createSpyObj('globalErrors', ['reportUnhandledRejections']);
}
});

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