Compare commits

...

354 Commits

Author SHA1 Message Date
Steve Gravrock
dbcc1c924a Bump version to 6.0.0-alpha.0 2025-09-29 18:45:30 -07:00
Steve Gravrock
90ee9a0cac Merge branch '5.99' into 6.0 2025-09-29 18:11:46 -07:00
Steve Gravrock
b9f04f8702 Merge branch 'main' into 5.99 2025-09-29 18:11:33 -07:00
Steve Gravrock
e11f320df3 Also require setSpecProperty/setSuiteProperty args to be JSON serializable 2025-09-27 15:40:19 -07:00
Steve Gravrock
c2ce55580c Remove support for excution orders that re-enter suites 2025-09-27 14:41:35 -07:00
Steve Gravrock
4598e4049c Revert accidental change to 3.9 release notes 2025-09-27 13:22:51 -07:00
Steve Gravrock
168ff0a751 Move private APIs to private namespace
Fixes #2078
2025-09-27 13:21:09 -07:00
Steve Gravrock
fbec066837 rm unused deprecatingSpecProxy 2025-09-27 12:58:48 -07:00
Steve Gravrock
0688db88e9 Bump version to 5.11.0 2025-09-26 16:53:15 -07:00
Steve Gravrock
190a13ed96 Prevent mock clock timing fns from being spied on
Fixes #826
2025-09-26 16:14:51 -07:00
Steve Gravrock
979e4a5d0f Fixed naming in SpyRegistry specs 2025-09-25 20:41:19 -07:00
Steve Gravrock
8863643d55 Fixed broken SpyRegistry spec 2025-09-25 20:40:31 -07:00
Steve Gravrock
7214ccd3dc Validate that setSuiteProperty and setSpecProperty args are cloneable 2025-09-21 15:09:42 -07:00
Steve Gravrock
970cbdc69c Omit irrelevant properties from specStarted 2025-09-21 15:09:38 -07:00
Steve Gravrock
4020da25a4 Refactor Suite#addExpectationResult to use named arguments 2025-09-21 09:05:00 -07:00
Steve Gravrock
4a36ece65b Revert "Drop support for Safari 16 and Firefox 102"
This reverts commit e2a7740322.

structuredClone errors in these browsers were a symptom of inavertently
including Error objects in reporter events. In newer browsers,
structuredClone can copy those objects, but it's lossy: if an instance of
an Error subclass is cloned, the result is an instance of Error.

With that fixed, Jasmine is compatible with Safari 16 and FF 102. At least
for now. And keeping them around may provide a way to detect similar bugs.
2025-09-21 08:44:17 -07:00
Steve Gravrock
17c0567bae Don't leak errorWithStack in late async expectation failures 2025-09-21 08:38:46 -07:00
Steve Gravrock
e2a7740322 Drop support for Safari 16 and Firefox 102
Neither of these browsers has sufficient structuredClone support. FF 102
is well past EOL and Safari 16 only runs on past-EOL OS versions.
2025-09-20 17:19:24 -07:00
Steve Gravrock
6e0342fc8e Deep clone reporter events 2025-09-20 16:18:56 -07:00
Steve Gravrock
d333ecb5b1 Removed useless passed property from ThrowUnlessFailure 2025-09-20 16:17:02 -07:00
Steve Gravrock
1e98a4b61b Removed remaining paths that pass expected and actual of expectations to reporters 2025-09-20 16:16:35 -07:00
Steve Gravrock
7aaa16f576 Removed ReportDispatcher support for multiple args and non-object args
All reporter calls take a single argument of object type, and always have.
2025-09-20 15:52:42 -07:00
Steve Gravrock
ee696cbbf6 Depend on the 4.0 branch of jasmine-browser-runner 2025-09-20 10:48:43 -07:00
Steve Gravrock
88289f592e Revert "Don't remove existing unhandled exception and promise rejection handlers in Node"
This reverts commit 6da88ec19eea2780c030dc95fcc55d059fed69e5.

Removing existing handlers turns out to be load-bearing for Jasmine's
parallel mode. ParallelWorker (in the jasmine package) installs a pair
of handlers before booting core so that it can catch late async errors
that happen after one spec file has finished executing and before the
next starts. If those aren't uninstalled, errors that get routed through
jasmine-core's normal error handling mechanism will also be reported
via ParallelWorker's handlers. It might be possible for ParallelWorker
to uninstall and install its handlers at the right time, but it's
likely that there would be gaps in between when core uninstalls its
handlers and when ParallelWorker installs. And in any case, the
old behavior of GlobalErrors was a perfect match for what ParallelWorker
needs, so let's keep it.
2025-09-20 10:48:43 -07:00
Steve Gravrock
0462500c31 Drop support for Node 18, to match jasmine-browser-runner
Selenium-webdriver 4.34 and later don't support it.
2025-09-20 10:48:43 -07:00
Steve Gravrock
72ecc70c5d Move jasmine-core.js source from lib to src 2025-09-20 10:48:43 -07:00
Steve Gravrock
f86f8c3331 Remove useless reinitialize option from noGlobals() 2025-09-20 10:48:43 -07:00
Steve Gravrock
70fbdc98b5 Revert to pre-5.0 default of creating a new core instance in each call to Node boot() 2025-09-20 10:48:43 -07:00
Steve Gravrock
4166ea791c Clean up global error listener leaks in Jasmine's own tests 2025-09-20 10:48:43 -07:00
Steve Gravrock
5439c8c9cd Don't remove existing unhandled exception and promise rejection handlers in Node 2025-09-20 10:48:43 -07:00
Steve Gravrock
2c6ce35ccc Pass spec metadata to filters, not internal Spec instance 2025-09-20 10:48:43 -07:00
Steve Gravrock
7c34b43607 Default forbidDuplicateNames to true 2025-09-20 10:48:43 -07:00
Steve Gravrock
3040abe23d Treat {verboseDeprecations: undefined} as a no-op, like other boolean config props 2025-09-20 10:48:43 -07:00
Steve Gravrock
4d3f6b272a Remove expected and actual properties of expectation results 2025-09-20 10:48:43 -07:00
Steve Gravrock
6ab83e25d1 Don't expose Order instances to reporters 2025-09-20 10:48:43 -07:00
Steve Gravrock
27297de3b8 Drop support for the eval form of setTimeout and setInterval in the mock clock 2025-09-20 10:48:43 -07:00
Steve Gravrock
3cbf4dc27b Deprecate access to non-public Spec properties in spec filters 2025-09-20 10:48:08 -07:00
Steve Gravrock
6d77f3e7f0 Deprecate the eval forms of setTimeout and setInterval 2025-09-20 10:48:08 -07:00
Steve Gravrock
124effe04b API reference docs for QueryString 2025-09-17 20:22:47 -07:00
Steve Gravrock
418e9a7728 Convert QueryString to an ES6 class 2025-09-17 18:35:01 -07:00
Steve Gravrock
6715f24fd0 Move knowledge of query parameters out of boot1.js 2025-09-17 18:23:45 -07:00
Steve Gravrock
fa481b2bd1 API reference docs for HTML reporter and spec filters 2025-09-17 07:30:34 -07:00
Steve Gravrock
8309416cb2 Clicking a link in the HTML reporter does exact filtering
This feature requires an update to boot1.js, as shown in this commit.
Users with an older boot1.js will get the older inexact filtering.
2025-09-17 07:30:20 -07:00
Steve Gravrock
4ccc7bf3ac Document the order property of jasmineStarted and jasmineDone 2025-09-15 18:38:09 -07:00
Steve Gravrock
cca6b2aa07 Adopt forbidDuplicateNames: true in jasmine-core's own tests 2025-09-15 18:38:09 -07:00
Steve Gravrock
78940aa0fb Drop support for Safari 15
Safari 15:

* Lacks structuredClone, which is starting to become useful
* Has stack trace quirks that are not well understood, not properly handled
  by Jasmine, and can cause problems in Jasmine's own tests
* Is not widely used
* Does not run on any OS that still receives security updates
2025-09-15 18:37:59 -07:00
Steve Gravrock
3d8396da0a More precisely characterize suite/spec reporting 2025-09-13 10:10:42 -07:00
Steve Gravrock
0bf9aff195 Extract configuration out of Env 2025-09-07 15:53:24 -07:00
Steve Gravrock
55b2e8846f Disambiguate options params in Env 2025-09-06 13:04:32 -07:00
Steve Gravrock
3493519c9f Fixed global error handling when the env is executed repeatedly 2025-09-06 10:32:56 -07:00
Steve Gravrock
62b5698a99 Clean up TreeRunner onComplete callback 2025-09-06 09:42:39 -07:00
Steve Gravrock
98849882a2 rm TODO comment about integrating detectLateRejectionHandling with clearStack
In theory, resetting clearStack's inline call count every time late
rejection handling does a setTimeout should reduce the performance penalty
in some environments.  In practice, it doesn't:

* In Chrome and FF, late rejection handling has no measurable penalty.
* In Safari, resetting the inline call count actually slows things down
  considerably(!).
* In Node, clearStack doesn't use setTimeout so there is no benefit.
2025-09-06 08:32:37 -07:00
Steve Gravrock
6665c4e123 Don't remove before and after fns from the top suite 2025-09-02 10:23:44 -07:00
Steve Gravrock
3698f6fb5d Support detectLateRejectionHandling in beforeAll and afterAll 2025-09-02 09:58:46 -07:00
Steve Gravrock
60f34ec087 Unify top suite and regular suite execution 2025-09-02 08:05:30 -07:00
Steve Gravrock
91bd3f8201 Optionally test in a subset of browsers locally
When run with --not-actually-all, scripts/run-all-browsers skips all
but the oldest and newest supported Firefox and Safari versions. This
provides a faster but still quite reliable mergeability check.

CI still tests against all supported browsers.
2025-09-01 08:58:32 -07:00
Steve Gravrock
ca4fbcbccb Clarify what's currently treated as private vs internal in Suite and Spec 2025-09-01 08:58:32 -07:00
Steve Gravrock
e1532be726 Convert Suite and SuiteMetadata to ES6 classes 2025-09-01 08:58:32 -07:00
Steve Gravrock
54465f6f6a Convert Spec to an es6 class 2025-09-01 08:58:32 -07:00
Steve Gravrock
fa9939ae94 Improve description of detectLateRejectionHandling feature in release notes 2025-08-30 14:00:00 -07:00
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
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
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
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
21f25972bb Converted ReportDispatcher to promises 2022-07-01 17:25:22 -07:00
279 changed files with 17898 additions and 9637 deletions

View File

@@ -4,25 +4,17 @@
version: 2.1
executors:
node18:
node24:
docker:
- image: cimg/node:18.0.0 # Latest 18.x
- image: cimg/node:24.0.0
working_directory: ~/workspace
node16:
node22:
docker:
- image: cimg/node:16.14.2 # Latest 16.x
- image: cimg/node:22.0.0
working_directory: ~/workspace
node14:
node20:
docker:
- image: cimg/node:14.17.4 # Latest 14.x
working_directory: ~/workspace
node12_latest:
docker:
- image: cimg/node:12.22.10 # Latest 12.x
working_directory: ~/workspace
node12_17:
docker:
- image: cimg/node:12.17.0 # Oldest version supported by Jasmine
- image: cimg/node:20.0.0
working_directory: ~/workspace
jobs:
@@ -59,20 +51,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: node20
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: |
@@ -80,13 +86,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:
@@ -95,48 +101,47 @@ workflows:
push:
jobs:
- build:
executor: node18
name: build_node_18
executor: node24
name: build_node_24
- build:
executor: node16
name: build_node_16
executor: node22
name: build_node_22
- build:
executor: node14
name: build_node_14
- build:
executor: node12_latest
name: build_node_12_latest
- build:
executor: node12_17
name: build_node_12_17
executor: node20
name: build_node_20
- test_node:
executor: node18
name: test_node_18
executor: node22
name: test_node_22
requires:
- build_node_18
- build_node_22
- test_node:
executor: node16
name: test_node_16
executor: node20
name: test_node_20
requires:
- build_node_16
- test_node:
executor: node14
name: test_node_14
- build_node_20
- test_parallel:
executor: node24
name: test_parallel_node_24
requires:
- build_node_14
- test_node:
executor: node12_latest
name: test_node_12_latest
- build_node_24
- test_parallel:
executor: node22
name: test_parallel_node_22
requires:
- build_node_12_latest
- test_node:
executor: node12_17
name: test_node_12_17
- build_node_22
- test_parallel:
executor: node20
name: test_parallel_node_20
requires:
- build_node_12_17
- build_node_20
- test_parallel:
executor: node20
name: test_parallel_node_20
requires:
- build_node_20
- test_browsers:
requires:
- build_node_14
- build_node_20
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,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,13 +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)
<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.
Upgrading from Jasmine 3.x? Check out 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
@@ -30,18 +27,22 @@ for information on writing specs, and [the FAQ](https://jasmine.github.io/pages/
Jasmine tests itself across popular browsers (Safari, Chrome, Firefox, and
Microsoft Edge) as well as Node.
| Environment | Supported versions |
|-------------------|--------------------|
| Node | 12.17+, 14, 16, 18 |
| Safari | 14-15 |
| Chrome | Evergreen |
| Firefox | Evergreen, 91 |
| Edge | Evergreen |
| Environment | Supported versions |
|-------------------|----------------------------------|
| Node | 20, 22, 24 |
| Safari | 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.
\* Supported on a best-effort basis. Support for these versions may be dropped
if it becomes impractical, and bugs affecting only these versions may not be
treated as release blockers.
To find out what environments work with a particular Jasmine release, see the [release notes](https://github.com/jasmine/jasmine/tree/main/release_notes).
## Maintainers
@@ -58,4 +59,6 @@ To find out what environments work with a particular Jasmine release, see the [r
* [Christian Williams](mailto:antixian666@gmail.com)
* Sheel Choksi
Copyright (c) 2008-2022 Jasmine Maintainers. This software is licensed under the [MIT License](https://github.com/jasmine/jasmine/blob/main/MIT.LICENSE).
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
@@ -42,13 +41,13 @@ When ready to release - specs are all green and the stories are done:
### Build standalone distribution
1. Build the standalone distribution with `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. `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
@@ -56,25 +55,13 @@ 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. `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

@@ -1,3 +1,27 @@
/*
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
"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.
*/
/**
* Note: Only available on Node.
* @module jasmine-core
@@ -6,36 +30,52 @@
const jasmineRequire = require('./jasmine-core/jasmine.js');
module.exports = jasmineRequire;
const bootWithoutGlobals = (function() {
let jasmine, jasmineInterface;
return function bootWithoutGlobals(reinitialize) {
if (!jasmineInterface || reinitialize === true) {
jasmine = jasmineRequire.core(jasmineRequire);
const env = jasmine.getEnv({ suppressLoadErrors: true });
jasmineInterface = jasmineRequire.interface(jasmine, env);
}
return { jasmine, jasmineInterface };
};
})();
/**
* Boots a copy of Jasmine and returns an object as described in {@link jasmine}.
* @param {boolean} [reinitialize=true] Whether to create a new copy of Jasmine if one already exists
* @type {function}
* @return {jasmine}
*/
module.exports.boot = require('./jasmine-core/node_boot.js');
module.exports.boot = function(reinitialize) {
if (reinitialize === undefined) {
reinitialize = true;
}
const { jasmine, jasmineInterface } = bootWithoutGlobals(reinitialize);
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.
*
* Do not call boot() if you also call noGlobals().
*
* @example
* const {describe, beforeEach, it, expect, jasmine} = require('jasmine-core').noGlobals();
*/
module.exports.noGlobals = (function() {
let jasmineInterface;
return function bootWithoutGlobals() {
if (!jasmineInterface) {
const jasmine = jasmineRequire.core(jasmineRequire);
const env = jasmine.getEnv({ suppressLoadErrors: true });
jasmineInterface = jasmineRequire.interface(jasmine, env);
}
return jasmineInterface;
};
}());
module.exports.noGlobals = function() {
const { jasmineInterface } = bootWithoutGlobals(false);
return jasmineInterface;
};
const path = require('path'),
fs = require('fs');
@@ -43,31 +83,30 @@ const path = require('path'),
const rootPath = path.join(__dirname, 'jasmine-core'),
bootFiles = ['boot0.js', 'boot1.js'],
legacyBootFiles = ['boot.js'],
nodeBootFiles = ['node_boot.js'],
cssFiles = [],
jsFiles = [],
jsFilesToSkip = ['jasmine.js'].concat(bootFiles, legacyBootFiles, nodeBootFiles);
jsFilesToSkip = ['jasmine.js'].concat(bootFiles, legacyBootFiles);
fs.readdirSync(rootPath).forEach(function(file) {
if(fs.statSync(path.join(rootPath, file)).isFile()) {
switch(path.extname(file)) {
if (fs.statSync(path.join(rootPath, file)).isFile()) {
switch (path.extname(file)) {
case '.css':
cssFiles.push(file);
break;
break;
case '.js':
if (jsFilesToSkip.indexOf(file) < 0) {
jsFiles.push(file);
}
break;
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

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
@@ -36,20 +38,12 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
(function() {
const env = jasmine.getEnv();
/**
* ## Runner Parameters
*
* More browser specific code - wrap the query string in an object and to allow for getting/setting parameters from the runner user interface.
*/
const queryString = new jasmine.QueryString({
getWindowLocation: function() {
return window.location;
}
});
const filterSpecs = !!queryString.getParam('spec');
const config = {
stopOnSpecFailure: queryString.getParam('stopOnSpecFailure'),
stopSpecOnExpectationFailure: queryString.getParam(
@@ -91,7 +85,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
return document.createTextNode.apply(document, arguments);
},
timer: new jasmine.Timer(),
filterSpecs: filterSpecs
queryString
});
/**
@@ -103,14 +97,9 @@ 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.
*/
const specFilter = new jasmine.HtmlSpecFilter({
filterString: function() {
return queryString.getParam('spec');
}
});
const specFilter = new jasmine.HtmlExactSpecFilter({ queryString });
config.specFilter = function(spec) {
return specFilter.matches(spec.getFullName());
return specFilter.matches(spec);
};
env.configure(config);

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,28 +21,32 @@ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
// eslint-disable-next-line no-var
var jasmineRequire = window.jasmineRequire || require('./jasmine.js');
jasmineRequire.html = function(j$) {
j$.ResultsNode = jasmineRequire.ResultsNode();
j$.private.ResultsNode = jasmineRequire.ResultsNode();
j$.HtmlReporter = jasmineRequire.HtmlReporter(j$);
j$.QueryString = jasmineRequire.QueryString();
j$.HtmlSpecFilter = jasmineRequire.HtmlSpecFilter();
j$.HtmlExactSpecFilter = jasmineRequire.HtmlExactSpecFilter();
};
jasmineRequire.HtmlReporter = function(j$) {
function ResultsStateBuilder() {
this.topResults = new j$.ResultsNode({}, '', null);
this.topResults = new j$.private.ResultsNode({}, '', null);
this.currentParent = this.topResults;
this.specsExecuted = 0;
this.failureCount = 0;
this.pendingSpecCount = 0;
this.suitesById = [];
}
ResultsStateBuilder.prototype.suiteStarted = function(result) {
this.currentParent.addChild(result, 'suite');
this.currentParent = this.currentParent.last();
this.suitesById[result.id] = this.currentParent;
};
ResultsStateBuilder.prototype.suiteDone = function(result) {
@@ -79,6 +84,13 @@ jasmineRequire.HtmlReporter = function(j$) {
}
};
/**
* @class HtmlReporter
* @classdesc Displays results and allows re-running individual specs and suites.
* @implements {Reporter}
* @param options Options object. See lib/jasmine-core/boot1.js for details.
* @since 1.2.0
*/
function HtmlReporter(options) {
function config() {
return (options.env && options.env.configuration()) || {};
@@ -87,15 +99,24 @@ jasmineRequire.HtmlReporter = function(j$) {
const getContainer = options.getContainer;
const createElement = options.createElement;
const createTextNode = options.createTextNode;
// TODO: in the next major release, replace navigateWithNewParam and
// addToExistingQueryString with direct usage of options.queryString
const navigateWithNewParam = options.navigateWithNewParam || function() {};
const addToExistingQueryString =
options.addToExistingQueryString || defaultQueryString;
const filterSpecs = options.filterSpecs;
const filterSpecs = options.queryString
? !!options.queryString.getParam('spec')
: options.filterSpecs; // For compatibility with pre-5.11 boot files
let htmlReporterMain;
let symbols;
const deprecationWarnings = [];
const failures = [];
/**
* Initializes the reporter. Should be called before {@link Env#execute}.
* @function
* @name HtmlReporter#initialize
*/
this.initialize = function() {
clearPrior();
htmlReporterMain = createDom(
@@ -154,8 +175,10 @@ jasmineRequire.HtmlReporter = function(j$) {
if (noExpectations(result)) {
const noSpecMsg = "Spec '" + result.fullName + "' has no expectations.";
if (result.status === 'failed') {
// eslint-disable-next-line no-console
console.error(noSpecMsg);
} else {
// eslint-disable-next-line no-console
console.warn(noSpecMsg);
}
}
@@ -462,7 +485,11 @@ jasmineRequire.HtmlReporter = function(j$) {
'tr',
{},
createDom('td', {}, entry.timestamp.toString()),
createDom('td', {}, entry.message)
createDom(
'td',
{ className: 'jasmine-debug-log-msg' },
entry.message
)
)
);
});
@@ -530,14 +557,13 @@ jasmineRequire.HtmlReporter = function(j$) {
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(
@@ -550,6 +576,11 @@ jasmineRequire.HtmlReporter = function(j$) {
'a',
{ href: specHref(resultNode.result) },
specDescription
),
createDom(
'span',
{ className: 'jasmine-spec-duration' },
'(' + resultNode.result.duration + 'ms)'
)
)
);
@@ -704,21 +735,6 @@ jasmineRequire.HtmlReporter = function(j$) {
return wrapper;
}
function suiteHref(suite) {
const els = [];
while (suite && suite.parent) {
els.unshift(suite.result.description);
suite = suite.parent;
}
// include window.location.pathname to fix issue with karma-jasmine-html-reporter in angular: see https://github.com/jasmine/jasmine/issues/1906
return (
(window.location.pathname || '') +
addToExistingQueryString('spec', els.join(' '))
);
}
function addDeprecationWarnings(result, runnableType) {
if (result && result.deprecationWarnings) {
for (let i = 0; i < result.deprecationWarnings.length; i++) {
@@ -777,7 +793,7 @@ jasmineRequire.HtmlReporter = function(j$) {
const el = createElement(type);
let children;
if (j$.isArray_(childrenArrayOrVarArgs)) {
if (j$.private.isArray(childrenArrayOrVarArgs)) {
children = childrenArrayOrVarArgs;
} else {
children = [];
@@ -816,11 +832,33 @@ jasmineRequire.HtmlReporter = function(j$) {
return '' + count + ' ' + word;
}
function suitePath(suite) {
const els = [];
while (suite && suite.parent) {
els.unshift(suite.result.description);
suite = suite.parent;
}
return els;
}
function suiteHref(suite) {
return pathHref(suitePath(suite));
}
function specHref(result) {
const suite = stateBuilder.suitesById[result.parentSuiteId];
const path = suitePath(suite);
path.push(result.description);
return pathHref(path);
}
function pathHref(path) {
// include window.location.pathname to fix issue with karma-jasmine-html-reporter in angular: see https://github.com/jasmine/jasmine/issues/1906
return (
(window.location.pathname || '') +
addToExistingQueryString('spec', result.fullName)
addToExistingQueryString('spec', JSON.stringify(path))
);
}
@@ -869,13 +907,36 @@ jasmineRequire.HtmlReporter = function(j$) {
};
jasmineRequire.HtmlSpecFilter = function() {
/**
* @name HtmlSpecFilter
* @classdesc Legacy HTML spec filter, for backward compatibility
* with boot files that predate {@link HtmlExactSpecFilter}.
* @param options Object with a filterString method
* @constructor
* @deprecated
* @since 1.2.0
*/
// Legacy HTML spec filter, preserved for backward compatibility with
// boot files that predate HtmlExactSpecFilterV2
function HtmlSpecFilter(options) {
const filterString =
options &&
options.filterString() &&
options.filterString().replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&');
let filterString = (options && options.filterString()) || '';
if (filterString.startsWith('[')) {
// Convert an HtmlExactSpecFilterV2 string into something we can use
filterString = JSON.parse(filterString).join(' ');
}
filterString = filterString.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&');
const filterPattern = new RegExp(filterString);
/**
* Determines whether the spec with the specified name should be executed.
* @name HtmlSpecFilter#matches
* @function
* @param {string} specName The full name of the spec
* @returns {boolean}
*/
this.matches = function(specName) {
return filterPattern.test(specName);
};
@@ -909,38 +970,57 @@ jasmineRequire.ResultsNode = function() {
};
jasmineRequire.QueryString = function() {
function QueryString(options) {
this.navigateWithNewParam = function(key, value) {
options.getWindowLocation().search = this.fullStringWithNewParam(
/**
* Reads and manipulates the query string.
* @since 2.0.0
*/
class QueryString {
#getWindowLocation;
/**
* @param options Object with a getWindowLocation property, which should be
* a function returning the current value of window.location.
*/
constructor(options) {
this.#getWindowLocation = options.getWindowLocation;
}
/**
* Sets the specified query parameter and navigates to the resulting URL.
* @param {string} key
* @param {string} value
*/
navigateWithNewParam(key, value) {
this.#getWindowLocation().search = this.fullStringWithNewParam(
key,
value
);
};
this.fullStringWithNewParam = function(key, value) {
const paramMap = queryStringToParamMap();
paramMap[key] = value;
return toQueryString(paramMap);
};
this.getParam = function(key) {
return queryStringToParamMap()[key];
};
return this;
function toQueryString(paramMap) {
const qStrPairs = [];
for (const prop in paramMap) {
qStrPairs.push(
encodeURIComponent(prop) + '=' + encodeURIComponent(paramMap[prop])
);
}
return '?' + qStrPairs.join('&');
}
function queryStringToParamMap() {
const paramStr = options.getWindowLocation().search.substring(1);
/**
* Returns a new URL based on the current location, with the specified
* query parameter set.
* @param {string} key
* @param {string} value
* @return {string}
*/
fullStringWithNewParam(key, value) {
const paramMap = this.#queryStringToParamMap();
paramMap[key] = value;
return toQueryString(paramMap);
}
/**
* Gets the value of the specified query parameter.
* @param {string} key
* @return {string}
*/
getParam(key) {
return this.#queryStringToParamMap()[key];
}
#queryStringToParamMap() {
const paramStr = this.#getWindowLocation().search.substring(1);
let params = [];
const paramMap = {};
@@ -960,5 +1040,68 @@ jasmineRequire.QueryString = function() {
}
}
function toQueryString(paramMap) {
const qStrPairs = [];
for (const prop in paramMap) {
qStrPairs.push(
encodeURIComponent(prop) + '=' + encodeURIComponent(paramMap[prop])
);
}
return '?' + qStrPairs.join('&');
}
return QueryString;
};
jasmineRequire.HtmlExactSpecFilter = function() {
/**
* Spec filter for use with {@link HtmlReporter}
*
* See lib/jasmine-core/boot1.js for usage.
* @since 5.11.0
*/
class HtmlExactSpecFilter {
#getFilterString;
/**
* Create a filter instance.
* @param options Object with a queryString property, which should be an
* instance of {@link QueryString}.
*/
constructor(options) {
this.#getFilterString = function() {
return options.queryString.getParam('spec');
};
}
/**
* Determines whether the specified spec should be executed.
* @param {Spec} spec
* @returns {boolean}
*/
matches(spec) {
const filterString = this.#getFilterString();
if (!filterString) {
return true;
}
const filterPath = JSON.parse(this.#getFilterString());
const specPath = spec.getPath();
if (filterPath.length > specPath.length) {
return false;
}
for (let i = 0; i < filterPath.length; i++) {
if (specPath[i] !== filterPath[i]) {
return false;
}
}
return true;
}
}
return HtmlExactSpecFilter;
};

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) {
const jasmine = jasmineRequire.core(jasmineRequire);
const env = jasmine.getEnv({ suppressLoadErrors: true });
const jasmineInterface = jasmineRequire.interface(jasmine, env);
extend(global, jasmineInterface);
function extend(destination, source) {
for (const property in source) destination[property] = source[property];
return destination;
}
return jasmine;
};

View File

@@ -1,7 +1,7 @@
{
"name": "jasmine-core",
"license": "MIT",
"version": "4.4.0",
"version": "6.0.0-alpha.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,84 +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": "^4.1.0",
"jasmine-browser-runner": "^1.0.0",
"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#4.0",
"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",
"no-eval": "error",
"no-var": "error"
}
"sass": "^1.58.3"
},
"browserslist": [
"Safari >= 14",
"Safari >= 16",
"Firefox >= 102",
"last 2 Chrome versions",
"last 2 Firefox versions",
"Firefox >= 91",
"last 2 Edge versions"
]
}

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

54
release_notes/5.10.0.md Normal file
View File

@@ -0,0 +1,54 @@
# Jasmine Core 5.10.0 Release Notes
## New Features
* Optionally detect promise rejections that are handled after an initial
unhandled promise rejection event 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)_

66
release_notes/5.11.0.md Normal file
View File

@@ -0,0 +1,66 @@
# Jasmine Core 5.11.0 Release Notes
## New features
* `detectLateRejectionHandling` works in `beforeAll` and `afterAll` as well as
in specs.
* Clicking a link in the HTML reporter does exact filtering rather than a
substring match.
If you use jasmine-browser-runner or load boot1.js directly from jasmine-core,
you'll get the new filtering behavior automatically. Otherwise, it requires
several changes to boot1.js:
1. Add `queryString` to the options object passed to `HtmlReporter`, and
remove `filterSpecs`.
2. Instantiate a `jasmine.HtmlExactSpecFilter` instead of a
`jasmine.HtmlSpecFilter`.
2. Change the body of `config.specFilter` from
`return specFilter.matches(spec.getFullName());` to
`return specFilter.matches(spec)`
For a working example, see lib/jasmine-core/boot1.js in this package.
Old boot1.js files will still work, but you'll get the old filtering behavior.
## Bug fixes
* Fixed global error handling when the env is executed repeatedly
## Changes to supported environments
* Safari 15 is no longer supported.
## Documentation improvements
* Added API reference docs for classes used in browser boot files
* Documented the order properties of `jasmineStarted` and `jasmineDone` events
## Internal improvements
* Unified top suite and regular suite execution
* Converted `Spec`, `Suite`, and `QueryString` to ES6 classes
* Extracted configuration out of `Env`
* Updated tests to characterize suite/spec reporting more completely
* Adopted `forbidDuplicateNames: true` in jasmine-core's own tests
## Supported environments
This version has been tested in the following environments.
| Environment | Supported versions |
|-------------------|--------------------------------|
| Node | 18.20.5**, 20, 22, 24 |
| Safari | 16**, 17** |
| Chrome | 140* |
| Firefox | 102**, 115**, 128**, 140, 143* |
| Edge | 140* |
\* 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,100 @@
# Jasmine Core 6.0.0-alpha.0 Release Notes
This is a pre-release, intended to offer a preview of breaking changes and to
solicit feedback.
## A Note About Pre-Release Compatibility
There may be additional breaking changes in future 6.0 pre-releases or in the
final 6.0 release. That's allowed by the semver specification, but users are
sometimes unpleasantly surprised by it.
NPM's implementation of carat version ranges assumes that subsequent
pre-releases and final releases are fully compatible with earlier pre-releases.
If your package.json contains `"jasmine-core": "^6.0.0-alpha.0`,
NPM might install any later 6.x version even though there is no guarantee of
compatibility. If that isn't ok, you should specify an exact pre-release version:
`"jasmine-core": "6.0.0-alpha.0`.
## Changes to supported environments
* Node 18 is no longer supported.
## Breaking changes
### General
* Private APIs have been removed from the `jasmine` namespace.
The purpose of this change is to reduce the risk of users inadvertently
depending on private APIs. Anything that's not covered by
[the documentation](https://jasmine.github.io/pages/docs_home.html) remains
private regardless of namespacing. Private APIs may be changed or removed in
any release. This change is being made in a major release as a courtesy to users of
libraries that depend on private APIs.
* Arguments to `setSpecProperty`/`setSuiteProperty` must be both
structured-cloneable and JSON-serializable.
* Mock clock timing functions cannot be spied on. Previously this "worked" but
prevented the mock clock from uninstalling itself.
* The default value of the `forbidDuplicateNames` config option has been
changed to true.
* The mock clock no longer supports the eval forms of `setTimeout` and
`setInterval`.
* If an execution order is passed to `Env#execute`, it must not enter any suite
more than once.
* The argument passed to spec filters is a
[spec metadata](https://jasmine.github.io/api/6.0.0-alpha.0/Spec.html)
instance, not the internal spec object.
### Changes that affect reporters
This release includes changes that are intended to streamline and clarify the
reporter interface, prevent sharing of mutable state, and prevent bugs involving
non-serializable objects. These changes should be compatible with most existing
reporters but could break reporters that manage their internal state in unusual
ways. Please [open an issue](https://github.com/jasmine/jasmine/issues/new?template=bug_report.yml)
if you find a published reporter package that works with jasmine-core 5.x but
not with this release.
* Irrelevant properties such as `status` and `failedExpectations` are omitted
from [the event passed to specStarted](https://jasmine.github.io/api/6.0.0-alpha.0/global.html#SpecStartedEvent).
* Reporter events are deep-cloned before being passed to each reporter. This
protects reporters against later mutation by jasmine-core or other reporters.
* The `expected` and `actual` properties of
[passed and failed expectations](https://jasmine.github.io/api/6.0.0-alpha.0/global.html#ExpectationResult)
have been removed.
* The [order](https://jasmine.github.io/api/6.0.0-alpha.0/global.html#Order)
property of the`jasmineStarted` and `jasmineDone` reporter events no longer
includes undocumented properties.
### Changes to Node boot functions
* [boot](https://jasmine.github.io/api/6.0.0-alpha.0/module-jasmine-core.html#.boot)
defaults to creating a new core instance each time it's called. This restores
the pre-5.0 default behavior.
* [noGlobals](https://jasmine.github.io/api/6.0.0-alpha.0/module-jasmine-core.html#.noGlobals)
no longer takes a parameter. It always returns the same object when called
repeatedly.
## Supported environments
This version has been tested in the following environments.
| Environment | Supported versions |
|-------------------|--------------------------------|
| Node | 20, 22, 24 |
| Safari | 16**, 17** |
| Chrome | 140* |
| Firefox | 102**, 115**, 128**, 140, 143* |
| Edge | 140* |
\* 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,133 @@
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'],
},
{
dest: 'lib/jasmine-core.js',
src: ['src/boot/jasmine-core.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,12 +24,24 @@ 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
if [ "$1" = "--not-actually-all" ]; then
echo "SKIPPED: firefox 140" >> "$passfile"
echo "SKIPPED: firefox 128" >> "$passfile"
echo "SKIPPED: firefox 115" >> "$passfile"
else
run_browser firefox 140
run_browser firefox 128
run_browser firefox 115
fi
run_browser firefox 102
run_browser firefox 91
run_browser safari 15
run_browser safari 14
run_browser safari 17
run_browser safari 16
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

@@ -1,7 +1,7 @@
describe('AsyncExpectation', function() {
beforeEach(function() {
jasmineUnderTest.Expectation.addAsyncCoreMatchers(
jasmineUnderTest.asyncMatchers
privateUnderTest.Expectation.addAsyncCoreMatchers(
privateUnderTest.asyncMatchers
);
});
@@ -9,9 +9,9 @@ describe('AsyncExpectation', function() {
it('converts a pass to a fail', function() {
const addExpectationResult = jasmine.createSpy('addExpectationResult'),
actual = Promise.resolve(),
pp = jasmineUnderTest.makePrettyPrinter(),
expectation = jasmineUnderTest.Expectation.asyncFactory({
matchersUtil: new jasmineUnderTest.MatchersUtil({ pp: pp }),
pp = privateUnderTest.makePrettyPrinter(),
expectation = privateUnderTest.Expectation.asyncFactory({
matchersUtil: new privateUnderTest.MatchersUtil({ pp: pp }),
actual: actual,
addExpectationResult: addExpectationResult
});
@@ -29,9 +29,9 @@ describe('AsyncExpectation', function() {
it('converts a fail to a pass', function() {
const addExpectationResult = jasmine.createSpy('addExpectationResult'),
actual = Promise.reject(),
expectation = jasmineUnderTest.Expectation.asyncFactory({
matchersUtil: new jasmineUnderTest.MatchersUtil({
actual = Promise.reject(new Error('nope')),
expectation = privateUnderTest.Expectation.asyncFactory({
matchersUtil: new privateUnderTest.MatchersUtil({
pp: function() {}
}),
actual: actual,
@@ -55,7 +55,7 @@ describe('AsyncExpectation', function() {
const addExpectationResult = jasmine.createSpy('addExpectationResult'),
actual = dummyPromise(),
expectation = jasmineUnderTest.Expectation.asyncFactory({
expectation = privateUnderTest.Expectation.asyncFactory({
actual: actual,
addExpectationResult: addExpectationResult
});
@@ -80,7 +80,7 @@ describe('AsyncExpectation', function() {
}
},
addExpectationResult = jasmine.createSpy('addExpectationResult'),
expectation = jasmineUnderTest.Expectation.asyncFactory({
expectation = privateUnderTest.Expectation.asyncFactory({
actual: Promise.reject('rejected'),
addExpectationResult: addExpectationResult,
matchersUtil: matchersUtil
@@ -105,10 +105,10 @@ describe('AsyncExpectation', function() {
buildFailureMessage: function() {
return 'failure message';
},
pp: jasmineUnderTest.makePrettyPrinter()
pp: privateUnderTest.makePrettyPrinter()
},
addExpectationResult = jasmine.createSpy('addExpectationResult'),
expectation = jasmineUnderTest.Expectation.asyncFactory({
expectation = privateUnderTest.Expectation.asyncFactory({
actual: Promise.reject('b'),
addExpectationResult: addExpectationResult,
matchersUtil: matchersUtil
@@ -129,43 +129,43 @@ 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');
it('prepends the context to a custom failure message from a matcher', async function() {
const matchersUtil = {
buildFailureMessage: function() {
return 'failure message';
}
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 = privateUnderTest.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() {
const addExpectationResult = jasmine.createSpy('addExpectationResult'),
actual = Promise.resolve(),
pp = jasmineUnderTest.makePrettyPrinter(),
expectation = jasmineUnderTest.Expectation.asyncFactory({
pp = privateUnderTest.makePrettyPrinter(),
expectation = privateUnderTest.Expectation.asyncFactory({
actual: actual,
addExpectationResult: addExpectationResult,
matchersUtil: new jasmineUnderTest.MatchersUtil({ pp: pp })
matchersUtil: new privateUnderTest.MatchersUtil({ pp: pp })
});
return expectation
@@ -185,11 +185,11 @@ describe('AsyncExpectation', function() {
it('works with #not and a custom message', function() {
const addExpectationResult = jasmine.createSpy('addExpectationResult'),
actual = Promise.resolve('a'),
expectation = jasmineUnderTest.Expectation.asyncFactory({
expectation = privateUnderTest.Expectation.asyncFactory({
actual: actual,
addExpectationResult: addExpectationResult,
matchersUtil: new jasmineUnderTest.MatchersUtil({
pp: jasmineUnderTest.makePrettyPrinter()
matchersUtil: new privateUnderTest.MatchersUtil({
pp: privateUnderTest.makePrettyPrinter()
})
});
@@ -214,7 +214,7 @@ describe('AsyncExpectation', function() {
toFoo: function() {},
toBar: function() {}
},
expectation = jasmineUnderTest.Expectation.asyncFactory({
expectation = privateUnderTest.Expectation.asyncFactory({
customAsyncMatchers: asyncMatchers
});
@@ -236,7 +236,7 @@ describe('AsyncExpectation', function() {
buildFailureMessage: jasmine.createSpy('buildFailureMessage')
},
addExpectationResult = jasmine.createSpy('addExpectationResult'),
expectation = jasmineUnderTest.Expectation.asyncFactory({
expectation = privateUnderTest.Expectation.asyncFactory({
matchersUtil: matchersUtil,
customAsyncMatchers: matchers,
actual: 'an actual',
@@ -263,7 +263,7 @@ describe('AsyncExpectation', function() {
buildFailureMessage: jasmine.createSpy('buildFailureMessage')
},
addExpectationResult = jasmine.createSpy('addExpectationResult'),
expectation = jasmineUnderTest.Expectation.asyncFactory({
expectation = privateUnderTest.Expectation.asyncFactory({
matchersUtil: matchersUtil,
customAsyncMatchers: matchers,
actual: 'an actual',
@@ -277,25 +277,20 @@ describe('AsyncExpectation', function() {
it('reports a passing result to the spec when the comparison passes', function() {
const matchers = {
toFoo: function() {
return {
compare: function() {
return Promise.resolve({ pass: true });
}
};
}
},
matchersUtil = {
buildFailureMessage: jasmine.createSpy('buildFailureMessage')
},
addExpectationResult = jasmine.createSpy('addExpectationResult'),
errorWithStack = new Error('errorWithStack');
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
);
const expectation = jasmineUnderTest.Expectation.asyncFactory({
const expectation = privateUnderTest.Expectation.asyncFactory({
customAsyncMatchers: matchers,
matchersUtil: matchersUtil,
actual: 'an actual',
@@ -308,9 +303,7 @@ describe('AsyncExpectation', function() {
passed: true,
message: '',
error: undefined,
expected: 'hello',
actual: 'an actual',
errorForStack: errorWithStack
errorForStack: jasmine.any(Error)
});
});
});
@@ -329,15 +322,10 @@ describe('AsyncExpectation', function() {
buildFailureMessage: function() {
return '';
}
},
addExpectationResult = jasmine.createSpy('addExpectationResult'),
errorWithStack = new Error('errorWithStack');
};
const addExpectationResult = jasmine.createSpy('addExpectationResult');
spyOn(jasmineUnderTest.util, 'errorWithStack').and.returnValue(
errorWithStack
);
const expectation = jasmineUnderTest.Expectation.asyncFactory({
const expectation = privateUnderTest.Expectation.asyncFactory({
customAsyncMatchers: matchers,
matchersUtil: matchersUtil,
actual: 'an actual',
@@ -348,36 +336,29 @@ describe('AsyncExpectation', function() {
expect(addExpectationResult).toHaveBeenCalledWith(false, {
matcherName: 'toFoo',
passed: false,
expected: 'hello',
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() {
const matchers = {
toFoo: function() {
return {
compare: function() {
return Promise.resolve({
pass: false,
message: 'I am a custom message'
});
}
};
}
},
addExpectationResult = jasmine.createSpy('addExpectationResult'),
errorWithStack = new Error('errorWithStack');
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
);
const expectation = jasmineUnderTest.Expectation.asyncFactory({
const expectation = privateUnderTest.Expectation.asyncFactory({
actual: 'an actual',
customAsyncMatchers: matchers,
addExpectationResult: addExpectationResult
@@ -387,38 +368,31 @@ describe('AsyncExpectation', function() {
expect(addExpectationResult).toHaveBeenCalledWith(false, {
matcherName: 'toFoo',
passed: false,
expected: 'hello',
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() {
const matchers = {
toFoo: function() {
return {
compare: function() {
return Promise.resolve({
pass: false,
message: function() {
return 'I am a custom message';
}
});
}
};
}
},
addExpectationResult = jasmine.createSpy('addExpectationResult'),
errorWithStack = new Error('errorWithStack');
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
);
const expectation = jasmineUnderTest.Expectation.asyncFactory({
const expectation = privateUnderTest.Expectation.asyncFactory({
customAsyncMatchers: matchers,
actual: 'an actual',
addExpectationResult: addExpectationResult
@@ -428,34 +402,26 @@ describe('AsyncExpectation', function() {
expect(addExpectationResult).toHaveBeenCalledWith(false, {
matcherName: 'toFoo',
passed: false,
expected: 'hello',
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() {
const matchers = {
toFoo: function() {
return {
compare: function() {
return Promise.resolve({ pass: false });
}
};
}
},
addExpectationResult = jasmine.createSpy('addExpectationResult'),
actual = 'an actual',
errorWithStack = new Error('errorWithStack');
toFoo: function() {
return {
compare: function() {
return Promise.resolve({ pass: false });
}
};
}
};
const addExpectationResult = jasmine.createSpy('addExpectationResult');
spyOn(jasmineUnderTest.util, 'errorWithStack').and.returnValue(
errorWithStack
);
const expectation = jasmineUnderTest.Expectation.asyncFactory({
const expectation = privateUnderTest.Expectation.asyncFactory({
customAsyncMatchers: matchers,
actual: 'an actual',
addExpectationResult: addExpectationResult
@@ -467,9 +433,7 @@ describe('AsyncExpectation', function() {
passed: true,
message: '',
error: undefined,
expected: 'hello',
actual: actual,
errorForStack: errorWithStack
errorForStack: jasmine.any(Error)
});
});
});
@@ -488,16 +452,10 @@ describe('AsyncExpectation', function() {
buildFailureMessage: function() {
return 'default message';
}
},
addExpectationResult = jasmine.createSpy('addExpectationResult'),
actual = 'an actual',
errorWithStack = new Error('errorWithStack');
};
const addExpectationResult = jasmine.createSpy('addExpectationResult');
spyOn(jasmineUnderTest.util, 'errorWithStack').and.returnValue(
errorWithStack
);
const expectation = jasmineUnderTest.Expectation.asyncFactory({
const expectation = privateUnderTest.Expectation.asyncFactory({
customAsyncMatchers: matchers,
actual: 'an actual',
matchersUtil: matchersUtil,
@@ -508,37 +466,29 @@ describe('AsyncExpectation', function() {
expect(addExpectationResult).toHaveBeenCalledWith(false, {
matcherName: 'toFoo',
passed: false,
expected: 'hello',
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() {
const matchers = {
toFoo: function() {
return {
compare: function() {
return Promise.resolve({
pass: true,
message: 'I am a custom message'
});
}
};
}
},
addExpectationResult = jasmine.createSpy('addExpectationResult'),
actual = 'an actual',
errorWithStack = new Error('errorWithStack');
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
);
const expectation = jasmineUnderTest.Expectation.asyncFactory({
const expectation = privateUnderTest.Expectation.asyncFactory({
customAsyncMatchers: matchers,
actual: 'an actual',
addExpectationResult: addExpectationResult
@@ -548,37 +498,29 @@ describe('AsyncExpectation', function() {
expect(addExpectationResult).toHaveBeenCalledWith(false, {
matcherName: 'toFoo',
passed: false,
expected: 'hello',
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() {
const matchers = {
toFoo: function() {
return {
compare: function() {
return Promise.resolve({ pass: true });
},
negativeCompare: function() {
return Promise.resolve({ pass: true });
}
};
}
},
addExpectationResult = jasmine.createSpy('addExpectationResult'),
actual = 'an actual',
errorWithStack = new Error('errorWithStack');
toFoo: function() {
return {
compare: function() {
return Promise.resolve({ pass: true });
},
negativeCompare: function() {
return Promise.resolve({ pass: true });
}
};
}
};
const addExpectationResult = jasmine.createSpy('addExpectationResult');
spyOn(jasmineUnderTest.util, 'errorWithStack').and.returnValue(
errorWithStack
);
const expectation = jasmineUnderTest.Expectation.asyncFactory({
const expectation = privateUnderTest.Expectation.asyncFactory({
customAsyncMatchers: matchers,
actual: 'an actual',
addExpectationResult: addExpectationResult
@@ -588,40 +530,32 @@ describe('AsyncExpectation', function() {
expect(addExpectationResult).toHaveBeenCalledWith(true, {
matcherName: 'toFoo',
passed: true,
expected: 'hello',
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() {
const matchers = {
toFoo: function() {
return {
compare: function() {
return Promise.resolve({ pass: true });
},
negativeCompare: function() {
return Promise.resolve({
pass: false,
message: "I'm a custom message"
});
}
};
}
},
addExpectationResult = jasmine.createSpy('addExpectationResult'),
actual = 'an actual',
errorWithStack = new Error('errorWithStack');
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');
spyOn(jasmineUnderTest.util, 'errorWithStack').and.returnValue(
errorWithStack
);
const expectation = jasmineUnderTest.Expectation.asyncFactory({
const expectation = privateUnderTest.Expectation.asyncFactory({
customAsyncMatchers: matchers,
actual: 'an actual',
addExpectationResult: addExpectationResult
@@ -631,11 +565,9 @@ describe('AsyncExpectation', function() {
expect(addExpectationResult).toHaveBeenCalledWith(false, {
matcherName: 'toFoo',
passed: false,
expected: 'hello',
actual: actual,
message: "I'm a custom message",
error: undefined,
errorForStack: errorWithStack
errorForStack: jasmine.any(Error)
});
});
});
@@ -643,26 +575,21 @@ describe('AsyncExpectation', function() {
it('reports errorWithStack when a custom error message is returned', function() {
const customError = new Error('I am a custom error');
const matchers = {
toFoo: function() {
return {
compare: function() {
return Promise.resolve({
pass: false,
message: 'I am a custom message',
error: customError
});
}
};
}
},
addExpectationResult = jasmine.createSpy('addExpectationResult'),
errorWithStack = new Error('errorWithStack');
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
);
const expectation = jasmineUnderTest.Expectation.asyncFactory({
const expectation = privateUnderTest.Expectation.asyncFactory({
actual: 'an actual',
customAsyncMatchers: matchers,
addExpectationResult: addExpectationResult
@@ -672,36 +599,29 @@ describe('AsyncExpectation', function() {
expect(addExpectationResult).toHaveBeenCalledWith(false, {
matcherName: 'toFoo',
passed: false,
expected: 'hello',
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() {
const matchers = {
toFoo: function() {
return {
compare: function() {
return Promise.resolve({
pass: true,
message: 'I am a custom message'
});
}
};
}
},
addExpectationResult = jasmine.createSpy('addExpectationResult'),
errorWithStack = new Error('errorWithStack');
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
);
const expectation = jasmineUnderTest.Expectation.asyncFactory({
const expectation = privateUnderTest.Expectation.asyncFactory({
actual: 'an actual',
customAsyncMatchers: matchers,
addExpectationResult: addExpectationResult
@@ -711,38 +631,31 @@ describe('AsyncExpectation', function() {
expect(addExpectationResult).toHaveBeenCalledWith(false, {
matcherName: 'toFoo',
passed: false,
expected: 'hello',
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() {
const matchers = {
toFoo: function() {
return {
compare: function() {
return Promise.resolve({
pass: true,
message: function() {
return 'I am a custom message';
}
});
}
};
}
},
addExpectationResult = jasmine.createSpy('addExpectationResult'),
errorWithStack = new Error('errorWithStack');
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
);
let expectation = jasmineUnderTest.Expectation.asyncFactory({
let expectation = privateUnderTest.Expectation.asyncFactory({
actual: 'an actual',
customAsyncMatchers: matchers,
addExpectationResult: addExpectationResult
@@ -752,11 +665,9 @@ describe('AsyncExpectation', function() {
expect(addExpectationResult).toHaveBeenCalledWith(false, {
matcherName: 'toFoo',
passed: false,
expected: 'hello',
actual: 'an actual',
message: 'I am a custom message',
error: undefined,
errorForStack: errorWithStack
errorForStack: jasmine.any(Error)
});
});
});

View File

@@ -1,6 +1,6 @@
describe('CallTracker', function() {
it('tracks that it was called when executed', function() {
const callTracker = new jasmineUnderTest.CallTracker();
const callTracker = new privateUnderTest.CallTracker();
expect(callTracker.any()).toBe(false);
@@ -10,7 +10,7 @@ describe('CallTracker', function() {
});
it('tracks that number of times that it is executed', function() {
const callTracker = new jasmineUnderTest.CallTracker();
const callTracker = new privateUnderTest.CallTracker();
expect(callTracker.count()).toEqual(0);
@@ -20,7 +20,7 @@ describe('CallTracker', function() {
});
it('tracks the params from each execution', function() {
const callTracker = new jasmineUnderTest.CallTracker();
const callTracker = new privateUnderTest.CallTracker();
callTracker.track({ object: void 0, args: [] });
callTracker.track({ object: {}, args: [0, 'foo'] });
@@ -31,7 +31,7 @@ describe('CallTracker', function() {
});
it("tracks the 'this' object from each execution", function() {
const callTracker = new jasmineUnderTest.CallTracker();
const callTracker = new privateUnderTest.CallTracker();
const this0 = {},
this1 = {};
@@ -45,13 +45,13 @@ describe('CallTracker', function() {
});
it('returns any empty array when there was no call', function() {
const callTracker = new jasmineUnderTest.CallTracker();
const callTracker = new privateUnderTest.CallTracker();
expect(callTracker.argsFor(0)).toEqual([]);
});
it('allows access for the arguments for all calls', function() {
const callTracker = new jasmineUnderTest.CallTracker();
const callTracker = new privateUnderTest.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() {
const callTracker = new jasmineUnderTest.CallTracker();
const callTracker = new privateUnderTest.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() {
const callTracker = new jasmineUnderTest.CallTracker();
const callTracker = new privateUnderTest.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() {
const callTracker = new jasmineUnderTest.CallTracker();
const callTracker = new privateUnderTest.CallTracker();
expect(callTracker.mostRecent()).toBeFalsy();
});
it('simplifies access to the arguments for the first (oldest) call', function() {
const callTracker = new jasmineUnderTest.CallTracker();
const callTracker = new privateUnderTest.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() {
const callTracker = new jasmineUnderTest.CallTracker();
const callTracker = new privateUnderTest.CallTracker();
expect(callTracker.first()).toBeFalsy();
});
it('allows the tracking to be reset', function() {
const callTracker = new jasmineUnderTest.CallTracker();
const callTracker = new privateUnderTest.CallTracker();
callTracker.track();
callTracker.track({ object: {}, args: [0, 'foo'] });
@@ -117,7 +117,7 @@ describe('CallTracker', function() {
});
it('allows object arguments to be shallow cloned', function() {
const callTracker = new jasmineUnderTest.CallTracker();
const callTracker = new privateUnderTest.CallTracker();
callTracker.saveArgumentsByValue();
const objectArg = { foo: 'bar' },
@@ -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 privateUnderTest.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 privateUnderTest.CallTracker();
callTracker.saveArgumentsByValue(JSON.stringify);
const objectArg = { foo: { bar: { baz: ['qux'] } } },
arrayArg = ['foo', 'bar'],
args = [objectArg, arrayArg, false, undefined, null, NaN, '', 0, 1.0];
callTracker.track({ object: {}, args });
expect(callTracker.mostRecent().args).toEqual(JSON.stringify(args));
});
it('saves primitive arguments by value', function() {
const callTracker = new jasmineUnderTest.CallTracker(),
const callTracker = new privateUnderTest.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) {
const clearStack = jasmineUnderTest.getClearStack(
const clearStack = privateUnderTest.getClearStack(
jasmineUnderTest.getGlobal()
);
@@ -20,6 +20,47 @@ describe('ClearStack', function() {
MessageChannel: fakeMessageChannel
};
});
it('uses MessageChannel to reduce setTimeout clamping', function() {
const fakeChannel = fakeMessageChannel();
spyOn(fakeChannel.port2, 'postMessage');
const queueMicrotask = jasmine.createSpy('queueMicrotask');
const global = {
navigator: {
userAgent:
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit/605.0.8 (KHTML, like Gecko) Version/15.1 Safari/605.0.8'
},
MessageChannel: function() {
return fakeChannel;
},
queueMicrotask
};
const clearStack = privateUnderTest.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() {
@@ -63,7 +104,7 @@ describe('ClearStack', function() {
...makeGlobal(),
MessageChannel: fakeMessageChannel
};
const clearStack = jasmineUnderTest.getClearStack(global);
const clearStack = privateUnderTest.getClearStack(global);
let called = false;
clearStack(function() {
@@ -84,7 +125,7 @@ describe('ClearStack', function() {
return fakeChannel;
}
};
const clearStack = jasmineUnderTest.getClearStack(global);
const clearStack = privateUnderTest.getClearStack(global);
for (let i = 0; i < 9; i++) {
clearStack(function() {});
@@ -109,7 +150,7 @@ describe('ClearStack', function() {
setTimeout,
MessageChannel: fakeMessageChannel
};
const clearStack = jasmineUnderTest.getClearStack(global);
const clearStack = privateUnderTest.getClearStack(global);
const fn = jasmine.createSpy('second clearStack function');
clearStack(function() {
@@ -129,7 +170,7 @@ describe('ClearStack', function() {
fn();
}
};
const clearStack = jasmineUnderTest.getClearStack(global);
const clearStack = privateUnderTest.getClearStack(global);
let called = false;
clearStack(function() {
@@ -147,7 +188,7 @@ describe('ClearStack', function() {
queueMicrotask,
setTimeout
};
const clearStack = jasmineUnderTest.getClearStack(global);
const clearStack = privateUnderTest.getClearStack(global);
for (let i = 0; i < 9; i++) {
clearStack(function() {});
@@ -174,7 +215,7 @@ describe('ClearStack', function() {
fn();
}
};
const clearStack = jasmineUnderTest.getClearStack(global);
const clearStack = privateUnderTest.getClearStack(global);
let called = false;
clearStack(function() {
@@ -192,7 +233,7 @@ describe('ClearStack', function() {
queueMicrotask,
setTimeout
};
const clearStack = jasmineUnderTest.getClearStack(global);
const clearStack = privateUnderTest.getClearStack(global);
clearStack(function() {});
clearStack(function() {});

View File

@@ -17,7 +17,7 @@ describe('Clock', function() {
tick: function() {},
uninstall: function() {}
},
clock = new jasmineUnderTest.Clock(
clock = new privateUnderTest.Clock(
fakeGlobal,
function() {
return delayedFunctionScheduler;
@@ -51,7 +51,7 @@ describe('Clock', function() {
tick: function() {},
uninstall: function() {}
},
clock = new jasmineUnderTest.Clock(
clock = new privateUnderTest.Clock(
fakeGlobal,
function() {
return delayedFunctionScheduler;
@@ -88,7 +88,7 @@ describe('Clock', function() {
tick: function() {},
uninstall: function() {}
},
clock = new jasmineUnderTest.Clock(
clock = new privateUnderTest.Clock(
fakeGlobal,
function() {
return delayedFunctionScheduler;
@@ -122,7 +122,7 @@ describe('Clock', function() {
tick: function() {},
uninstall: function() {}
},
clock = new jasmineUnderTest.Clock(
clock = new privateUnderTest.Clock(
fakeGlobal,
function() {
return delayedFunctionScheduler;
@@ -154,7 +154,7 @@ describe('Clock', function() {
'delayedFunctionSchedulerFactory'
),
mockDate = {},
clock = new jasmineUnderTest.Clock(
clock = new privateUnderTest.Clock(
fakeGlobal,
delayedFunctionSchedulerFactory,
mockDate
@@ -178,7 +178,7 @@ describe('Clock', function() {
'delayedFunctionSchedulerFactory'
),
mockDate = {},
clock = new jasmineUnderTest.Clock(
clock = new privateUnderTest.Clock(
fakeGlobal,
delayedFunctionSchedulerFactory,
mockDate
@@ -202,7 +202,7 @@ describe('Clock', function() {
'delayedFunctionSchedulerFactory'
),
mockDate = {},
clock = new jasmineUnderTest.Clock(
clock = new privateUnderTest.Clock(
fakeGlobal,
delayedFunctionSchedulerFactory,
mockDate
@@ -226,7 +226,7 @@ describe('Clock', function() {
'delayedFunctionSchedulerFactory'
),
mockDate = {},
clock = new jasmineUnderTest.Clock(
clock = new privateUnderTest.Clock(
fakeGlobal,
delayedFunctionSchedulerFactory,
mockDate
@@ -242,7 +242,7 @@ describe('Clock', function() {
expect(fakeGlobal.clearInterval).toBe(replacedClearInterval);
});
it('replaces the global timer functions on uninstall', function() {
it('restores the global timer functions on uninstall', function() {
const fakeSetTimeout = jasmine.createSpy('global setTimeout'),
fakeClearTimeout = jasmine.createSpy('global clearTimeout'),
fakeSetInterval = jasmine.createSpy('global setInterval'),
@@ -263,7 +263,7 @@ describe('Clock', function() {
tick: function() {},
uninstall: function() {}
},
clock = new jasmineUnderTest.Clock(
clock = new privateUnderTest.Clock(
fakeGlobal,
function() {
return delayedFunctionScheduler;
@@ -306,7 +306,7 @@ describe('Clock', function() {
tick: function() {},
uninstall: function() {}
},
clock = new jasmineUnderTest.Clock(
clock = new privateUnderTest.Clock(
fakeGlobal,
function() {
return delayedFunctionScheduler;
@@ -366,7 +366,7 @@ describe('Clock', function() {
tick: function() {},
uninstall: function() {}
},
clock = new jasmineUnderTest.Clock(
clock = new privateUnderTest.Clock(
fakeGlobal,
function() {
return delayedFunctionScheduler;
@@ -408,215 +408,258 @@ describe('Clock', function() {
expect(delayedFunctionScheduler.scheduleFunction).not.toHaveBeenCalled();
});
it('schedules the delayed function (via setTimeout) with the fake timer', function() {
const fakeSetTimeout = jasmine.createSpy('setTimeout'),
scheduleFunction = jasmine.createSpy('scheduleFunction'),
delayedFunctionScheduler = { scheduleFunction: scheduleFunction },
fakeGlobal = { setTimeout: fakeSetTimeout },
delayedFn = jasmine.createSpy('delayedFn'),
mockDate = {
install: function() {},
tick: function() {},
uninstall: function() {}
},
clock = new jasmineUnderTest.Clock(
fakeGlobal,
function() {
return delayedFunctionScheduler;
},
mockDate
),
timeout = new clock.FakeTimeout();
clock.install();
clock.setTimeout(delayedFn, 0, 'a', 'b');
expect(fakeSetTimeout).not.toHaveBeenCalled();
if (!NODE_JS) {
expect(delayedFunctionScheduler.scheduleFunction).toHaveBeenCalledWith(
delayedFn,
0,
['a', 'b']
);
} else {
expect(delayedFunctionScheduler.scheduleFunction).toHaveBeenCalledWith(
delayedFn,
0,
['a', 'b'],
false,
timeout
);
}
});
it('returns an id for the delayed function', function() {
const fakeSetTimeout = jasmine.createSpy('setTimeout'),
scheduleId = 123,
scheduleFunction = jasmine
.createSpy('scheduleFunction')
.and.returnValue(scheduleId),
delayedFunctionScheduler = { scheduleFunction: scheduleFunction },
fakeGlobal = { setTimeout: fakeSetTimeout },
delayedFn = jasmine.createSpy('delayedFn'),
mockDate = {
install: function() {},
tick: function() {},
uninstall: function() {}
},
clock = new jasmineUnderTest.Clock(
fakeGlobal,
function() {
return delayedFunctionScheduler;
},
mockDate
);
clock.install();
const timeout = clock.setTimeout(delayedFn, 0);
if (!NODE_JS) {
expect(timeout).toEqual(123);
} else {
expect(timeout.constructor.name).toEqual('FakeTimeout');
}
});
it('clears the scheduled function with the scheduler', function() {
const fakeClearTimeout = jasmine.createSpy('clearTimeout'),
delayedFunctionScheduler = jasmine.createSpyObj(
'delayedFunctionScheduler',
['removeFunctionWithId']
),
fakeGlobal = { setTimeout: fakeClearTimeout },
mockDate = {
install: function() {},
tick: function() {},
uninstall: function() {}
},
clock = new jasmineUnderTest.Clock(
fakeGlobal,
function() {
return delayedFunctionScheduler;
},
mockDate
);
clock.install();
clock.clearTimeout(123);
expect(fakeClearTimeout).not.toHaveBeenCalled();
expect(delayedFunctionScheduler.removeFunctionWithId).toHaveBeenCalledWith(
123
it('identifies its timing functions', function() {
const fakeSetTimeout = jasmine.createSpy('global setTimeout');
const fakeGlobal = { setTimeout: fakeSetTimeout };
const delayedFunctionScheduler = jasmine.createSpyObj(
'delayedFunctionScheduler',
['scheduleFunction']
);
});
it('schedules the delayed function with the fake timer', function() {
const fakeSetInterval = jasmine.createSpy('setInterval'),
scheduleFunction = jasmine.createSpy('scheduleFunction'),
delayedFunctionScheduler = { scheduleFunction: scheduleFunction },
fakeGlobal = { setInterval: fakeSetInterval },
delayedFn = jasmine.createSpy('delayedFn'),
mockDate = {
install: function() {},
tick: function() {},
uninstall: function() {}
const mockDate = {
install: function() {},
tick: function() {},
uninstall: function() {}
};
const clock = new privateUnderTest.Clock(
fakeGlobal,
function() {
return delayedFunctionScheduler;
},
clock = new jasmineUnderTest.Clock(
fakeGlobal,
function() {
return delayedFunctionScheduler;
},
mockDate
),
timeout = new clock.FakeTimeout();
clock.install();
clock.setInterval(delayedFn, 0, 'a', 'b');
expect(fakeSetInterval).not.toHaveBeenCalled();
if (!NODE_JS) {
expect(delayedFunctionScheduler.scheduleFunction).toHaveBeenCalledWith(
delayedFn,
0,
['a', 'b'],
true
);
} else {
expect(delayedFunctionScheduler.scheduleFunction).toHaveBeenCalledWith(
delayedFn,
0,
['a', 'b'],
true,
timeout
);
}
});
it('returns an id for the delayed function', function() {
const fakeSetInterval = jasmine.createSpy('setInterval'),
scheduleId = 123,
scheduleFunction = jasmine
.createSpy('scheduleFunction')
.and.returnValue(scheduleId),
delayedFunctionScheduler = { scheduleFunction: scheduleFunction },
fakeGlobal = { setInterval: fakeSetInterval },
delayedFn = jasmine.createSpy('delayedFn'),
mockDate = {
install: function() {},
tick: function() {},
uninstall: function() {}
},
clock = new jasmineUnderTest.Clock(
fakeGlobal,
function() {
return delayedFunctionScheduler;
},
mockDate
);
clock.install();
const interval = clock.setInterval(delayedFn, 0);
if (!NODE_JS) {
expect(interval).toEqual(123);
} else {
expect(interval.constructor.name).toEqual('FakeTimeout');
}
});
it('clears the scheduled function with the scheduler', function() {
const clearInterval = jasmine.createSpy('clearInterval'),
delayedFunctionScheduler = jasmine.createSpyObj(
'delayedFunctionScheduler',
['removeFunctionWithId']
),
fakeGlobal = { setInterval: clearInterval },
mockDate = {
install: function() {},
tick: function() {},
uninstall: function() {}
},
clock = new jasmineUnderTest.Clock(
fakeGlobal,
function() {
return delayedFunctionScheduler;
},
mockDate
);
clock.install();
clock.clearInterval(123);
expect(clearInterval).not.toHaveBeenCalled();
expect(delayedFunctionScheduler.removeFunctionWithId).toHaveBeenCalledWith(
123
mockDate
);
clock.install();
expect(
fakeGlobal.setTimeout[privateUnderTest.Clock.IsMockClockTimingFn]
).toEqual(true);
expect(
fakeGlobal.clearTimeout[privateUnderTest.Clock.IsMockClockTimingFn]
).toEqual(true);
expect(
fakeGlobal.setInterval[privateUnderTest.Clock.IsMockClockTimingFn]
).toEqual(true);
expect(
fakeGlobal.clearInterval[privateUnderTest.Clock.IsMockClockTimingFn]
).toEqual(true);
});
describe('setTimeout', function() {
it('schedules the delayed function with the fake timer', function() {
const fakeSetTimeout = jasmine.createSpy('setTimeout'),
scheduleFunction = jasmine.createSpy('scheduleFunction'),
delayedFunctionScheduler = { scheduleFunction: scheduleFunction },
fakeGlobal = { setTimeout: fakeSetTimeout },
delayedFn = jasmine.createSpy('delayedFn'),
mockDate = {
install: function() {},
tick: function() {},
uninstall: function() {}
},
clock = new privateUnderTest.Clock(
fakeGlobal,
function() {
return delayedFunctionScheduler;
},
mockDate
),
timeout = new clock.FakeTimeout();
clock.install();
clock.setTimeout(delayedFn, 0, 'a', 'b');
expect(fakeSetTimeout).not.toHaveBeenCalled();
if (!NODE_JS) {
expect(delayedFunctionScheduler.scheduleFunction).toHaveBeenCalledWith(
delayedFn,
0,
['a', 'b']
);
} else {
expect(delayedFunctionScheduler.scheduleFunction).toHaveBeenCalledWith(
delayedFn,
0,
['a', 'b'],
false,
timeout
);
}
});
it('returns an id for the delayed function', function() {
const fakeSetTimeout = jasmine.createSpy('setTimeout'),
scheduleId = 123,
scheduleFunction = jasmine
.createSpy('scheduleFunction')
.and.returnValue(scheduleId),
delayedFunctionScheduler = { scheduleFunction: scheduleFunction },
fakeGlobal = { setTimeout: fakeSetTimeout },
delayedFn = jasmine.createSpy('delayedFn'),
mockDate = {
install: function() {},
tick: function() {},
uninstall: function() {}
},
clock = new privateUnderTest.Clock(
fakeGlobal,
function() {
return delayedFunctionScheduler;
},
mockDate
);
clock.install();
const timeout = clock.setTimeout(delayedFn, 0);
if (!NODE_JS) {
expect(timeout).toEqual(123);
} else {
expect(timeout.constructor.name).toEqual('FakeTimeout');
}
});
});
describe('clearTimeout', function() {
it('clears the scheduled function with the scheduler', function() {
const fakeClearTimeout = jasmine.createSpy('clearTimeout'),
delayedFunctionScheduler = jasmine.createSpyObj(
'delayedFunctionScheduler',
['removeFunctionWithId']
),
fakeGlobal = { setTimeout: fakeClearTimeout },
mockDate = {
install: function() {},
tick: function() {},
uninstall: function() {}
},
clock = new privateUnderTest.Clock(
fakeGlobal,
function() {
return delayedFunctionScheduler;
},
mockDate
);
clock.install();
clock.clearTimeout(123);
expect(fakeClearTimeout).not.toHaveBeenCalled();
expect(
delayedFunctionScheduler.removeFunctionWithId
).toHaveBeenCalledWith(123);
});
});
describe('setInterval', function() {
it('schedules the delayed function with the fake timer', function() {
const fakeSetInterval = jasmine.createSpy('setInterval'),
scheduleFunction = jasmine.createSpy('scheduleFunction'),
delayedFunctionScheduler = { scheduleFunction: scheduleFunction },
fakeGlobal = { setInterval: fakeSetInterval },
delayedFn = jasmine.createSpy('delayedFn'),
mockDate = {
install: function() {},
tick: function() {},
uninstall: function() {}
},
clock = new privateUnderTest.Clock(
fakeGlobal,
function() {
return delayedFunctionScheduler;
},
mockDate
),
timeout = new clock.FakeTimeout();
clock.install();
clock.setInterval(delayedFn, 0, 'a', 'b');
expect(fakeSetInterval).not.toHaveBeenCalled();
if (!NODE_JS) {
expect(delayedFunctionScheduler.scheduleFunction).toHaveBeenCalledWith(
delayedFn,
0,
['a', 'b'],
true
);
} else {
expect(delayedFunctionScheduler.scheduleFunction).toHaveBeenCalledWith(
delayedFn,
0,
['a', 'b'],
true,
timeout
);
}
});
it('returns an id for the delayed function', function() {
const fakeSetInterval = jasmine.createSpy('setInterval'),
scheduleId = 123,
scheduleFunction = jasmine
.createSpy('scheduleFunction')
.and.returnValue(scheduleId),
delayedFunctionScheduler = { scheduleFunction: scheduleFunction },
fakeGlobal = { setInterval: fakeSetInterval },
delayedFn = jasmine.createSpy('delayedFn'),
mockDate = {
install: function() {},
tick: function() {},
uninstall: function() {}
},
clock = new privateUnderTest.Clock(
fakeGlobal,
function() {
return delayedFunctionScheduler;
},
mockDate
);
clock.install();
const interval = clock.setInterval(delayedFn, 0);
if (!NODE_JS) {
expect(interval).toEqual(123);
} else {
expect(interval.constructor.name).toEqual('FakeTimeout');
}
});
});
describe('clearInterval', function() {
it('clears the scheduled function with the scheduler', function() {
const clearInterval = jasmine.createSpy('clearInterval'),
delayedFunctionScheduler = jasmine.createSpyObj(
'delayedFunctionScheduler',
['removeFunctionWithId']
),
fakeGlobal = { setInterval: clearInterval },
mockDate = {
install: function() {},
tick: function() {},
uninstall: function() {}
},
clock = new privateUnderTest.Clock(
fakeGlobal,
function() {
return delayedFunctionScheduler;
},
mockDate
);
clock.install();
clock.clearInterval(123);
expect(clearInterval).not.toHaveBeenCalled();
expect(
delayedFunctionScheduler.removeFunctionWithId
).toHaveBeenCalledWith(123);
});
});
it('gives you a friendly reminder if the Clock is not installed and you tick', function() {
const clock = new jasmineUnderTest.Clock(
const clock = new privateUnderTest.Clock(
{},
jasmine.createSpyObj('delayedFunctionScheduler', ['tick'])
);
@@ -632,13 +675,13 @@ describe('Clock (acceptance)', function() {
delayedFn2 = jasmine.createSpy('delayedFn2'),
delayedFn3 = jasmine.createSpy('delayedFn3'),
recurring1 = jasmine.createSpy('recurring1'),
delayedFunctionScheduler = new jasmineUnderTest.DelayedFunctionScheduler(),
delayedFunctionScheduler = new privateUnderTest.DelayedFunctionScheduler(),
mockDate = {
install: function() {},
tick: function() {},
uninstall: function() {}
},
clock = new jasmineUnderTest.Clock(
clock = new privateUnderTest.Clock(
{ setTimeout: setTimeout },
function() {
return delayedFunctionScheduler;
@@ -687,15 +730,168 @@ describe('Clock (acceptance)', function() {
expect(recurring1.calls.count()).toBe(4);
});
describe('auto tick mode', () => {
let delayedFunctionScheduler;
let mockDate;
let clock;
beforeEach(() => {
delayedFunctionScheduler = new privateUnderTest.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 privateUnderTest.Clock(
_global,
function() {
return delayedFunctionScheduler;
},
mockDate
);
clock.install().autoTick();
});
afterEach(() => {
clock.uninstall();
});
it('flushes microtask queue between macrotasks', async () => {
const log = [];
await new Promise(r => clock.setTimeout(r, 10)).then(() => {
log.push(1);
Promise.resolve().then(() => log.push(2));
Promise.resolve().then(() => log.push(3));
});
await new Promise(r => clock.setTimeout(r, 10)).then(() => {
log.push(4);
Promise.resolve().then(() => log.push(5));
});
expect(log).toEqual([1, 2, 3, 4, 5]);
});
it('can run setTimeouts/setIntervals asynchronously', function() {
const recurring = jasmine.createSpy('recurring'),
fn1 = jasmine.createSpy('fn1'),
fn2 = jasmine.createSpy('fn2'),
fn3 = jasmine.createSpy('fn3');
const intervalId = clock.setInterval(recurring, 50);
// In a microtask, add some timeouts.
Promise.resolve()
.then(function() {
return new Promise(function(resolve) {
clock.setTimeout(resolve, 25);
});
})
.then(function() {
fn1();
return new Promise(function(resolve) {
clock.setTimeout(resolve, 200);
});
})
.then(function() {
fn2();
return new Promise(function(resolve) {
clock.setTimeout(resolve, 100);
});
})
.then(function() {
fn3();
});
expect(recurring).not.toHaveBeenCalled();
expect(fn1).not.toHaveBeenCalled();
expect(fn2).not.toHaveBeenCalled();
expect(fn3).not.toHaveBeenCalled();
return new Promise(resolve => clock.setTimeout(resolve, 50))
.then(function() {
expect(recurring).toHaveBeenCalledTimes(1);
expect(fn1).toHaveBeenCalled();
expect(fn2).not.toHaveBeenCalled();
expect(fn3).not.toHaveBeenCalled();
return new Promise(resolve => clock.setTimeout(resolve, 175));
})
.then(function() {
expect(recurring).toHaveBeenCalledTimes(4);
expect(fn1).toHaveBeenCalled();
expect(fn2).toHaveBeenCalled();
expect(fn3).not.toHaveBeenCalled();
clock.clearInterval(intervalId);
return new Promise(resolve => clock.setTimeout(resolve, 100));
})
.then(function() {
expect(recurring).toHaveBeenCalledTimes(4);
expect(fn1).toHaveBeenCalled();
expect(fn2).toHaveBeenCalled();
expect(fn3).toHaveBeenCalled();
});
});
it('aborts auto ticking when uninstalled, even if installed again synchonrously', async () => {
clock.uninstall();
clock.install();
let resolved = false;
const promise = new Promise(resolve => {
clock.setTimeout(resolve, 1);
}).then(() => {
resolved = true;
});
// wait some real time and verify that the clock did not flush the timer above automatically
await new Promise(resolve => setTimeout(resolve, 2));
expect(resolved).toBe(false);
// enabling auto tick again will flush the timer
clock.autoTick();
await expectAsync(promise).toBeResolved();
});
it('speeds up the execution of the timers in all browsers', async () => {
const startTimeMs = performance.now() / 1000;
await new Promise(resolve => clock.setTimeout(resolve, 5000));
await new Promise(resolve => clock.setTimeout(resolve, 5000));
await new Promise(resolve => clock.setTimeout(resolve, 5000));
await new Promise(resolve => clock.setTimeout(resolve, 5000));
const endTimeMs = performance.now() / 1000;
// Ensure we didn't take 20s to complete the awaits above and, in fact, can do it in a fraction of a second
expect(endTimeMs - startTimeMs).toBeLessThan(100);
});
it('is easy to test async functions with interleaved timers and microtasks', async () => {
async function blackBoxWithLotsOfAsyncStuff() {
await new Promise(r => clock.setTimeout(r, 10));
await Promise.resolve();
await Promise.resolve();
await new Promise(r => clock.setTimeout(r, 20));
await Promise.resolve();
await Promise.resolve();
await Promise.resolve();
return 'done';
}
const result = await blackBoxWithLotsOfAsyncStuff();
expect(result).toBe('done');
});
});
it('can clear a previously set timeout', function() {
const clearedFn = jasmine.createSpy('clearedFn'),
delayedFunctionScheduler = new jasmineUnderTest.DelayedFunctionScheduler(),
delayedFunctionScheduler = new privateUnderTest.DelayedFunctionScheduler(),
mockDate = {
install: function() {},
tick: function() {},
uninstall: function() {}
},
clock = new jasmineUnderTest.Clock(
clock = new privateUnderTest.Clock(
{ setTimeout: function() {} },
function() {
return delayedFunctionScheduler;
@@ -716,13 +912,13 @@ describe('Clock (acceptance)', function() {
it("can clear a previously set interval using that interval's handler", function() {
const spy = jasmine.createSpy('spy'),
delayedFunctionScheduler = new jasmineUnderTest.DelayedFunctionScheduler(),
delayedFunctionScheduler = new privateUnderTest.DelayedFunctionScheduler(),
mockDate = {
install: function() {},
tick: function() {},
uninstall: function() {}
},
clock = new jasmineUnderTest.Clock(
clock = new privateUnderTest.Clock(
{ setInterval: function() {} },
function() {
return delayedFunctionScheduler;
@@ -743,13 +939,13 @@ describe('Clock (acceptance)', function() {
it('correctly schedules functions after the Clock has advanced', function() {
const delayedFn1 = jasmine.createSpy('delayedFn1'),
delayedFunctionScheduler = new jasmineUnderTest.DelayedFunctionScheduler(),
delayedFunctionScheduler = new privateUnderTest.DelayedFunctionScheduler(),
mockDate = {
install: function() {},
tick: function() {},
uninstall: function() {}
},
clock = new jasmineUnderTest.Clock(
clock = new privateUnderTest.Clock(
{ setTimeout: function() {} },
function() {
return delayedFunctionScheduler;
@@ -770,13 +966,13 @@ describe('Clock (acceptance)', function() {
it('correctly schedules functions while the Clock is advancing', function() {
const delayedFn1 = jasmine.createSpy('delayedFn1'),
delayedFn2 = jasmine.createSpy('delayedFn2'),
delayedFunctionScheduler = new jasmineUnderTest.DelayedFunctionScheduler(),
delayedFunctionScheduler = new privateUnderTest.DelayedFunctionScheduler(),
mockDate = {
install: function() {},
tick: function() {},
uninstall: function() {}
},
clock = new jasmineUnderTest.Clock(
clock = new privateUnderTest.Clock(
{ setTimeout: function() {} },
function() {
return delayedFunctionScheduler;
@@ -801,13 +997,13 @@ describe('Clock (acceptance)', function() {
it('correctly calls functions scheduled while the Clock is advancing', function() {
const delayedFn1 = jasmine.createSpy('delayedFn1'),
delayedFn2 = jasmine.createSpy('delayedFn2'),
delayedFunctionScheduler = new jasmineUnderTest.DelayedFunctionScheduler(),
delayedFunctionScheduler = new privateUnderTest.DelayedFunctionScheduler(),
mockDate = {
install: function() {},
tick: function() {},
uninstall: function() {}
},
clock = new jasmineUnderTest.Clock(
clock = new privateUnderTest.Clock(
{ setTimeout: function() {} },
function() {
return delayedFunctionScheduler;
@@ -829,13 +1025,13 @@ describe('Clock (acceptance)', function() {
it('correctly schedules functions scheduled while the Clock is advancing but after the Clock is uninstalled', function() {
const delayedFn1 = jasmine.createSpy('delayedFn1'),
delayedFn2 = jasmine.createSpy('delayedFn2'),
delayedFunctionScheduler = new jasmineUnderTest.DelayedFunctionScheduler(),
delayedFunctionScheduler = new privateUnderTest.DelayedFunctionScheduler(),
mockDate = {
install: function() {},
tick: function() {},
uninstall: function() {}
},
clock = new jasmineUnderTest.Clock(
clock = new privateUnderTest.Clock(
{ setTimeout: function() {} },
function() {
return delayedFunctionScheduler;
@@ -861,10 +1057,10 @@ describe('Clock (acceptance)', function() {
});
it('does not mock the Date object by default', function() {
const delayedFunctionScheduler = new jasmineUnderTest.DelayedFunctionScheduler(),
const delayedFunctionScheduler = new privateUnderTest.DelayedFunctionScheduler(),
global = { Date: Date },
mockDate = new jasmineUnderTest.MockDate(global),
clock = new jasmineUnderTest.Clock(
mockDate = new privateUnderTest.MockDate(global),
clock = new privateUnderTest.Clock(
{ setTimeout: setTimeout },
function() {
return delayedFunctionScheduler;
@@ -884,10 +1080,10 @@ describe('Clock (acceptance)', function() {
});
it('mocks the Date object and sets it to current time', function() {
const delayedFunctionScheduler = new jasmineUnderTest.DelayedFunctionScheduler(),
const delayedFunctionScheduler = new privateUnderTest.DelayedFunctionScheduler(),
global = { Date: Date },
mockDate = new jasmineUnderTest.MockDate(global),
clock = new jasmineUnderTest.Clock(
mockDate = new privateUnderTest.MockDate(global),
clock = new privateUnderTest.Clock(
{ setTimeout: setTimeout },
function() {
return delayedFunctionScheduler;
@@ -914,10 +1110,10 @@ describe('Clock (acceptance)', function() {
});
it('mocks the Date object and sets it to a given time', function() {
const delayedFunctionScheduler = new jasmineUnderTest.DelayedFunctionScheduler(),
const delayedFunctionScheduler = new privateUnderTest.DelayedFunctionScheduler(),
global = { Date: Date },
mockDate = new jasmineUnderTest.MockDate(global),
clock = new jasmineUnderTest.Clock(
mockDate = new privateUnderTest.MockDate(global),
clock = new privateUnderTest.Clock(
{ setTimeout: setTimeout },
function() {
return delayedFunctionScheduler;
@@ -947,10 +1143,10 @@ describe('Clock (acceptance)', function() {
});
it('throws mockDate is called with a non-Date', function() {
const delayedFunctionScheduler = new jasmineUnderTest.DelayedFunctionScheduler(),
const delayedFunctionScheduler = new privateUnderTest.DelayedFunctionScheduler(),
global = { Date: Date },
mockDate = new jasmineUnderTest.MockDate(global),
clock = new jasmineUnderTest.Clock(
mockDate = new privateUnderTest.MockDate(global),
clock = new privateUnderTest.Clock(
{ setTimeout: setTimeout },
function() {
return delayedFunctionScheduler;
@@ -965,10 +1161,10 @@ describe('Clock (acceptance)', function() {
});
it('mocks the Date object and updates the date per delayed function', function() {
const delayedFunctionScheduler = new jasmineUnderTest.DelayedFunctionScheduler(),
const delayedFunctionScheduler = new privateUnderTest.DelayedFunctionScheduler(),
global = { Date: Date },
mockDate = new jasmineUnderTest.MockDate(global),
clock = new jasmineUnderTest.Clock(
mockDate = new privateUnderTest.MockDate(global),
clock = new privateUnderTest.Clock(
{ setTimeout: setTimeout },
function() {
return delayedFunctionScheduler;
@@ -1004,10 +1200,10 @@ describe('Clock (acceptance)', function() {
});
it('correctly clears a scheduled timeout while the Clock is advancing', function() {
const delayedFunctionScheduler = new jasmineUnderTest.DelayedFunctionScheduler(),
const delayedFunctionScheduler = new privateUnderTest.DelayedFunctionScheduler(),
global = { Date: Date, setTimeout: undefined },
mockDate = new jasmineUnderTest.MockDate(global),
clock = new jasmineUnderTest.Clock(
mockDate = new privateUnderTest.MockDate(global),
clock = new privateUnderTest.Clock(
global,
function() {
return delayedFunctionScheduler;
@@ -1029,10 +1225,10 @@ describe('Clock (acceptance)', function() {
});
it('correctly clears a scheduled interval while the Clock is advancing', function() {
const delayedFunctionScheduler = new jasmineUnderTest.DelayedFunctionScheduler(),
const delayedFunctionScheduler = new privateUnderTest.DelayedFunctionScheduler(),
global = { Date: Date, setTimeout: undefined },
mockDate = new jasmineUnderTest.MockDate(global),
clock = new jasmineUnderTest.Clock(
mockDate = new privateUnderTest.MockDate(global),
clock = new privateUnderTest.Clock(
global,
function() {
return delayedFunctionScheduler;

View File

@@ -2,7 +2,7 @@ describe('CompleteOnFirstErrorSkipPolicy', function() {
describe('#skipTo', function() {
describe('Before anything has errored', function() {
it('returns the next index', function() {
const policy = new jasmineUnderTest.CompleteOnFirstErrorSkipPolicy(
const policy = new privateUnderTest.CompleteOnFirstErrorSkipPolicy(
arrayOfArbitraryFns(4),
4
);
@@ -15,7 +15,7 @@ describe('CompleteOnFirstErrorSkipPolicy', function() {
const fns = arrayOfArbitraryFns(4);
fns[2].type = arbitraryCleanupType();
fns[3].type = arbitraryCleanupType();
const policy = new jasmineUnderTest.CompleteOnFirstErrorSkipPolicy(fns);
const policy = new privateUnderTest.CompleteOnFirstErrorSkipPolicy(fns);
policy.fnErrored(0);
expect(policy.skipTo(0)).toEqual(2);
@@ -27,7 +27,7 @@ describe('CompleteOnFirstErrorSkipPolicy', function() {
it(`does not skip ${type} fns`, function() {
const fns = arrayOfArbitraryFns(2);
fns[1].type = type;
const policy = new jasmineUnderTest.CompleteOnFirstErrorSkipPolicy(
const policy = new privateUnderTest.CompleteOnFirstErrorSkipPolicy(
fns
);
@@ -58,7 +58,7 @@ describe('CompleteOnFirstErrorSkipPolicy', function() {
type: arbitraryCleanupType()
}
];
const policy = new jasmineUnderTest.CompleteOnFirstErrorSkipPolicy(
const policy = new privateUnderTest.CompleteOnFirstErrorSkipPolicy(
fns
);
@@ -90,7 +90,7 @@ describe('CompleteOnFirstErrorSkipPolicy', function() {
type: arbitraryCleanupType()
}
];
const policy = new jasmineUnderTest.CompleteOnFirstErrorSkipPolicy(
const policy = new privateUnderTest.CompleteOnFirstErrorSkipPolicy(
fns
);
@@ -107,7 +107,7 @@ describe('CompleteOnFirstErrorSkipPolicy', function() {
type: arbitraryCleanupType()
}
];
const policy = new jasmineUnderTest.CompleteOnFirstErrorSkipPolicy(fns);
const policy = new privateUnderTest.CompleteOnFirstErrorSkipPolicy(fns);
policy.fnErrored(0);
expect(policy.skipTo(0)).toEqual(1);

View File

@@ -0,0 +1,113 @@
describe('Configuration', function() {
const standardBooleanKeys = [
'random',
'stopOnSpecFailure',
'stopSpecOnExpectationFailure',
'failSpecWithNoExpectations',
'hideDisabled',
'autoCleanClosures',
'forbidDuplicateNames',
'detectLateRejectionHandling',
'verboseDeprecations'
];
const allKeys = [...standardBooleanKeys, 'seed', 'specFilter'];
Object.freeze(standardBooleanKeys);
Object.freeze(allKeys);
it('provides defaults', function() {
const subject = new privateUnderTest.Configuration();
expect(subject.random).toEqual(true);
expect(subject.seed).toBeNull();
expect(subject.stopOnSpecFailure).toEqual(false);
expect(subject.stopSpecOnExpectationFailure).toEqual(false);
expect(subject.failSpecWithNoExpectations).toEqual(false);
expect(subject.specFilter).toEqual(jasmine.any(Function));
expect(subject.specFilter()).toEqual(true);
expect(subject.hideDisabled).toEqual(false);
expect(subject.autoCleanClosures).toEqual(true);
expect(subject.forbidDuplicateNames).toEqual(true);
expect(subject.verboseDeprecations).toEqual(false);
expect(subject.detectLateRejectionHandling).toEqual(false);
});
describe('copy()', function() {
it('returns a copy of the configuration as a plain old JS object', function() {
const subject = new privateUnderTest.Configuration();
const copy = subject.copy();
expect(copy.constructor.name).toEqual('Object');
expect(new Set(Object.keys(copy))).toEqual(new Set(allKeys));
for (const k of allKeys) {
expect(copy[k]).toEqual(subject[k]);
}
});
});
describe('update()', function() {
it('does not update properties that are absent from the parameter', function() {
const subject = new privateUnderTest.Configuration();
const originalValues = subject.copy();
subject.update({});
expect(subject.copy()).toEqual(originalValues);
});
function booleanPropertyBehavior(key) {
it('does not update the property if the specified value is undefined', function() {
const subject = new privateUnderTest.Configuration();
const orig = subject[key];
subject.update({ [key]: undefined });
expect(subject[key]).toEqual(orig);
});
it('updates the property if the specified value is not undefined', function() {
const subject = new privateUnderTest.Configuration();
const orig = subject[key];
subject.update({ [key]: !orig });
expect(subject[key]).toEqual(!orig);
subject.update({ [key]: orig });
expect(subject[key]).toEqual(orig);
});
}
for (const k of standardBooleanKeys) {
describe(k, function() {
booleanPropertyBehavior(k);
});
}
it('sets specFilter when truthy', function() {
const subject = new privateUnderTest.Configuration();
const orig = subject.specFilter;
subject.update({ specFilter: undefined });
expect(subject.specFilter).toBe(orig);
subject.update({ specFilter: false });
expect(subject.specFilter).toBe(orig);
function newSpecFilter() {}
subject.update({ specFilter: newSpecFilter });
expect(subject.specFilter).toBe(newSpecFilter);
});
it('sets seed when not undefined', function() {
const subject = new privateUnderTest.Configuration();
subject.update({ seed: undefined });
expect(subject.seed).toBeNull();
subject.update({ seed: 1234 });
expect(subject.seed).toEqual(1234);
subject.update({ seed: null });
expect(subject.seed).toBeNull();
});
});
});

View File

@@ -1,6 +1,8 @@
describe('DelayedFunctionScheduler', function() {
'use strict';
it('schedules a function for later execution', function() {
const scheduler = new jasmineUnderTest.DelayedFunctionScheduler(),
const scheduler = new privateUnderTest.DelayedFunctionScheduler(),
fn = jasmine.createSpy('fn');
scheduler.scheduleFunction(fn, 0);
@@ -12,19 +14,18 @@ describe('DelayedFunctionScheduler', function() {
expect(fn).toHaveBeenCalled();
});
it('schedules a string for later execution', function() {
const scheduler = new jasmineUnderTest.DelayedFunctionScheduler(),
strfn = 'horrible = true;';
it('throws if a string is passed', function() {
const scheduler = new privateUnderTest.DelayedFunctionScheduler();
scheduler.scheduleFunction(strfn, 0);
scheduler.tick(0);
expect(horrible).toEqual(true);
expect(function() {
scheduler.scheduleFunction('horrible = true;', 0);
}).toThrowError(
'The mock clock does not support the eval form of setTimeout and setInterval. Pass a function instead of a string.'
);
});
it('#tick defaults to 0', function() {
const scheduler = new jasmineUnderTest.DelayedFunctionScheduler(),
const scheduler = new privateUnderTest.DelayedFunctionScheduler(),
fn = jasmine.createSpy('fn');
scheduler.scheduleFunction(fn, 0);
@@ -37,7 +38,7 @@ describe('DelayedFunctionScheduler', function() {
});
it('defaults delay to 0', function() {
const scheduler = new jasmineUnderTest.DelayedFunctionScheduler(),
const scheduler = new privateUnderTest.DelayedFunctionScheduler(),
fn = jasmine.createSpy('fn');
scheduler.scheduleFunction(fn);
@@ -50,7 +51,7 @@ describe('DelayedFunctionScheduler', function() {
});
it('optionally passes params to scheduled functions', function() {
const scheduler = new jasmineUnderTest.DelayedFunctionScheduler(),
const scheduler = new privateUnderTest.DelayedFunctionScheduler(),
fn = jasmine.createSpy('fn');
scheduler.scheduleFunction(fn, 0, ['foo', 'bar']);
@@ -63,7 +64,7 @@ describe('DelayedFunctionScheduler', function() {
});
it('scheduled fns can optionally reoccur', function() {
const scheduler = new jasmineUnderTest.DelayedFunctionScheduler(),
const scheduler = new privateUnderTest.DelayedFunctionScheduler(),
fn = jasmine.createSpy('fn');
scheduler.scheduleFunction(fn, 20, [], true);
@@ -84,18 +85,19 @@ describe('DelayedFunctionScheduler', function() {
});
it('increments scheduled fns ids unless one is passed', function() {
const scheduler = new jasmineUnderTest.DelayedFunctionScheduler();
const scheduler = new privateUnderTest.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() {
const scheduler = new jasmineUnderTest.DelayedFunctionScheduler(),
const scheduler = new privateUnderTest.DelayedFunctionScheduler(),
fn = jasmine.createSpy('fn'),
timeoutKey = scheduler.scheduleFunction(fn, 0);
@@ -109,7 +111,7 @@ describe('DelayedFunctionScheduler', function() {
});
it('executes recurring functions interleaved with regular functions in the correct order', function() {
const scheduler = new jasmineUnderTest.DelayedFunctionScheduler();
const scheduler = new privateUnderTest.DelayedFunctionScheduler();
const fn = jasmine.createSpy('fn');
let recurringCallCount = 0;
const recurring = jasmine.createSpy('recurring').and.callFake(function() {
@@ -130,7 +132,7 @@ describe('DelayedFunctionScheduler', function() {
});
it('schedules a function for later execution during a tick', function() {
const scheduler = new jasmineUnderTest.DelayedFunctionScheduler(),
const scheduler = new privateUnderTest.DelayedFunctionScheduler(),
fn = jasmine.createSpy('fn'),
fnDelay = 10;
@@ -146,7 +148,7 @@ describe('DelayedFunctionScheduler', function() {
});
it('#removeFunctionWithId removes a previously scheduled function with a given id during a tick', function() {
const scheduler = new jasmineUnderTest.DelayedFunctionScheduler(),
const scheduler = new privateUnderTest.DelayedFunctionScheduler(),
fn = jasmine.createSpy('fn'),
fnDelay = 10;
let timeoutKey;
@@ -164,7 +166,7 @@ describe('DelayedFunctionScheduler', function() {
});
it('executes recurring functions interleaved with regular functions and functions scheduled during a tick in the correct order', function() {
const scheduler = new jasmineUnderTest.DelayedFunctionScheduler();
const scheduler = new privateUnderTest.DelayedFunctionScheduler();
const fn = jasmine.createSpy('fn');
let recurringCallCount = 0;
const recurring = jasmine.createSpy('recurring').and.callFake(function() {
@@ -197,7 +199,7 @@ describe('DelayedFunctionScheduler', function() {
});
it('executes recurring functions after rescheduling them', function() {
const scheduler = new jasmineUnderTest.DelayedFunctionScheduler(),
const scheduler = new privateUnderTest.DelayedFunctionScheduler(),
recurring = function() {
expect(scheduler.scheduleFunction).toHaveBeenCalled();
};
@@ -210,7 +212,7 @@ describe('DelayedFunctionScheduler', function() {
});
it('removes functions during a tick that runs the function', function() {
const scheduler = new jasmineUnderTest.DelayedFunctionScheduler(),
const scheduler = new privateUnderTest.DelayedFunctionScheduler(),
spy = jasmine.createSpy('fn'),
spyAndRemove = jasmine.createSpy('fn'),
fnDelay = 10;
@@ -231,7 +233,7 @@ describe('DelayedFunctionScheduler', function() {
});
it('removes functions during the first tick that runs the function', function() {
const scheduler = new jasmineUnderTest.DelayedFunctionScheduler(),
const scheduler = new privateUnderTest.DelayedFunctionScheduler(),
fn = jasmine.createSpy('fn'),
fnDelay = 10;
let timeoutKey;
@@ -250,7 +252,7 @@ describe('DelayedFunctionScheduler', function() {
});
it("does not remove a function that hasn't been added yet", function() {
const scheduler = new jasmineUnderTest.DelayedFunctionScheduler(),
const scheduler = new privateUnderTest.DelayedFunctionScheduler(),
fn = jasmine.createSpy('fn'),
fnDelay = 10;
@@ -264,8 +266,44 @@ describe('DelayedFunctionScheduler', function() {
expect(fn).toHaveBeenCalled();
});
it('runs the next scheduled funtion', function() {
const scheduler = new privateUnderTest.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 privateUnderTest.DelayedFunctionScheduler();
const fn1 = jasmine.createSpy('fn');
const fn2 = jasmine.createSpy('fn2');
const tickSpy = jasmine.createSpy('tick');
scheduler.scheduleFunction(fn1, 10, [], false, 'foo1');
scheduler.scheduleFunction(fn2, 10, [], false, 'foo2');
scheduler.runNextQueuedFunction(tickSpy);
expect(fn1).toHaveBeenCalled();
expect(fn2).not.toHaveBeenCalled();
expect(tickSpy).toHaveBeenCalledWith(10);
tickSpy.calls.reset();
scheduler.runNextQueuedFunction(tickSpy);
expect(fn2).toHaveBeenCalled();
expect(tickSpy).toHaveBeenCalledWith(0);
});
it('updates the mockDate per scheduled time', function() {
const scheduler = new jasmineUnderTest.DelayedFunctionScheduler(),
const scheduler = new privateUnderTest.DelayedFunctionScheduler(),
tickDate = jasmine.createSpy('tickDate');
scheduler.scheduleFunction(function() {});
@@ -277,6 +315,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 privateUnderTest.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

@@ -7,7 +7,7 @@ describe('Deprecator', function() {
it('logs the mesage without context when the runnable is the top suite', function() {
const runnable = { addDeprecationWarning: function() {} };
const deprecator = new jasmineUnderTest.Deprecator(runnable);
const deprecator = new privateUnderTest.Deprecator(runnable);
deprecator.verboseDeprecations(true);
deprecator.addDeprecationWarning(runnable, 'the message', {
@@ -25,7 +25,7 @@ describe('Deprecator', function() {
},
children: []
};
const deprecator = new jasmineUnderTest.Deprecator({});
const deprecator = new privateUnderTest.Deprecator({});
deprecator.verboseDeprecations(true);
deprecator.addDeprecationWarning(runnable, 'the message', {
@@ -44,7 +44,7 @@ describe('Deprecator', function() {
return 'the spec';
}
};
const deprecator = new jasmineUnderTest.Deprecator({});
const deprecator = new privateUnderTest.Deprecator({});
deprecator.verboseDeprecations(true);
deprecator.addDeprecationWarning(runnable, 'the message', {
@@ -61,7 +61,7 @@ describe('Deprecator', function() {
'addDeprecationWarning',
'getFullName'
]);
const deprecator = new jasmineUnderTest.Deprecator(topSuite);
const deprecator = new privateUnderTest.Deprecator(topSuite);
const runnable = jasmine.createSpyObj('spec', [
'addDeprecationWarning',
'getFullName'
@@ -105,7 +105,7 @@ describe('Deprecator', function() {
});
it('emits the deprecation only once when verboseDeprecations is not set', function() {
const deprecator = new jasmineUnderTest.Deprecator({});
const deprecator = new privateUnderTest.Deprecator({});
const runnable1 = jasmine.createSpyObj('runnable1', [
'addDeprecationWarning',
'getFullName'
@@ -124,7 +124,7 @@ describe('Deprecator', function() {
});
it('emits the deprecation only once when verboseDeprecations is false', function() {
const deprecator = new jasmineUnderTest.Deprecator({});
const deprecator = new privateUnderTest.Deprecator({});
const runnable1 = jasmine.createSpyObj('runnable1', [
'addDeprecationWarning',
'getFullName'
@@ -144,7 +144,7 @@ describe('Deprecator', function() {
});
it('emits the deprecation for each call when verboseDeprecations is true', function() {
const deprecator = new jasmineUnderTest.Deprecator({});
const deprecator = new privateUnderTest.Deprecator({});
const runnable1 = jasmine.createSpyObj('runnable1', [
'addDeprecationWarning',
'getFullName'
@@ -164,7 +164,7 @@ describe('Deprecator', function() {
});
it('includes a note about verboseDeprecations', function() {
const deprecator = new jasmineUnderTest.Deprecator({});
const deprecator = new privateUnderTest.Deprecator({});
const runnable = jasmine.createSpyObj('runnable', [
'addDeprecationWarning',
'getFullName'
@@ -183,7 +183,7 @@ describe('Deprecator', function() {
});
it('omits the note about verboseDeprecations when verboseDeprecations is true', function() {
const deprecator = new jasmineUnderTest.Deprecator({});
const deprecator = new privateUnderTest.Deprecator({});
const runnable = jasmine.createSpyObj('runnable', [
'addDeprecationWarning',
'getFullName'
@@ -207,7 +207,7 @@ 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() {
const deprecator = new jasmineUnderTest.Deprecator({});
const deprecator = new privateUnderTest.Deprecator({});
const runnable = jasmine.createSpyObj('runnable', [
'addDeprecationWarning',
'getFullName'
@@ -238,7 +238,7 @@ describe('Deprecator', function() {
});
it('reports the deprecation every time, regardless of config.verboseDeprecations', function() {
const deprecator = new jasmineUnderTest.Deprecator({});
const deprecator = new privateUnderTest.Deprecator({});
const runnable = jasmine.createSpyObj('runnable', [
'addDeprecationWarning',
'getFullName'
@@ -259,7 +259,7 @@ describe('Deprecator', function() {
});
it('omits the note about verboseDeprecations', function() {
const deprecator = new jasmineUnderTest.Deprecator({});
const deprecator = new privateUnderTest.Deprecator({});
const runnable = jasmine.createSpyObj('runnable', [
'addDeprecationWarning',
'getFullName'
@@ -293,7 +293,7 @@ describe('Deprecator', function() {
}
function testStackTrace(options) {
const deprecator = new jasmineUnderTest.Deprecator({});
const deprecator = new privateUnderTest.Deprecator({});
const runnable = jasmine.createSpyObj('runnable', [
'addDeprecationWarning',
'getFullName'
@@ -311,7 +311,7 @@ describe('Deprecator', function() {
}
function testNoStackTrace(options) {
const deprecator = new jasmineUnderTest.Deprecator({});
const deprecator = new privateUnderTest.Deprecator({});
const runnable = jasmine.createSpyObj('runnable', [
'addDeprecationWarning',
'getFullName'

View File

@@ -2,7 +2,7 @@
describe('Env', function() {
let env;
beforeEach(function() {
env = new jasmineUnderTest.Env();
env = new privateUnderTest.Env();
});
afterEach(function() {
@@ -13,14 +13,14 @@ describe('Env', function() {
it('throws the Pending Spec exception', function() {
expect(function() {
env.pending();
}).toThrow(jasmineUnderTest.Spec.pendingSpecExceptionMessage);
}).toThrow(privateUnderTest.Spec.pendingSpecExceptionMessage);
});
it('throws the Pending Spec exception with a custom message', function() {
expect(function() {
env.pending('custom message');
}).toThrow(
jasmineUnderTest.Spec.pendingSpecExceptionMessage + 'custom message'
privateUnderTest.Spec.pendingSpecExceptionMessage + 'custom message'
);
});
});
@@ -38,24 +38,24 @@ describe('Env', function() {
});
const suite = env.topSuite();
expect(suite).not.toBeInstanceOf(jasmineUnderTest.Suite);
expect(suite).not.toBeInstanceOf(privateUnderTest.Suite);
expect(suite.description).toEqual('Jasmine__TopLevel__Suite');
expect(suite.getFullName()).toEqual('');
expect(suite.children.length).toEqual(2);
expect(suite.children[0]).not.toBeInstanceOf(jasmineUnderTest.Spec);
expect(suite.children[0]).not.toBeInstanceOf(privateUnderTest.Spec);
expect(suite.children[0].description).toEqual('a top level spec');
expect(suite.children[0].getFullName()).toEqual('a top level spec');
expect(suite.children[0].children).toBeFalsy();
expect(suite.children[1]).not.toBeInstanceOf(jasmineUnderTest.Suite);
expect(suite.children[1]).not.toBeInstanceOf(privateUnderTest.Suite);
expect(suite.children[1].description).toEqual('a suite');
expect(suite.children[1].getFullName()).toEqual('a suite');
expect(suite.children[1].parentSuite).toBe(suite);
expect(suite.children[1].children.length).toEqual(2);
expect(suite.children[1].children[0]).not.toBeInstanceOf(
jasmineUnderTest.Spec
privateUnderTest.Spec
);
expect(suite.children[1].children[0].description).toEqual('a spec');
expect(suite.children[1].children[0].getFullName()).toEqual(
@@ -80,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() {
@@ -89,9 +102,9 @@ describe('Env', function() {
it('can configure specs to throw errors on expectation failures', function() {
env.configure({ stopSpecOnExpectationFailure: true });
spyOn(jasmineUnderTest, 'Spec').and.callThrough();
spyOn(privateUnderTest, 'Spec').and.callThrough();
env.it('foo', function() {});
expect(jasmineUnderTest.Spec).toHaveBeenCalledWith(
expect(privateUnderTest.Spec).toHaveBeenCalledWith(
jasmine.objectContaining({
throwOnExpectationFailure: true
})
@@ -101,9 +114,9 @@ describe('Env', function() {
it('can configure suites to throw errors on expectation failures', function() {
env.configure({ stopSpecOnExpectationFailure: true });
spyOn(jasmineUnderTest, 'Suite');
spyOn(privateUnderTest, 'Suite');
env.describe('foo', function() {});
expect(jasmineUnderTest.Suite).toHaveBeenCalledWith(
expect(privateUnderTest.Suite).toHaveBeenCalledWith(
jasmine.objectContaining({
throwOnExpectationFailure: true
})
@@ -137,9 +150,9 @@ describe('Env', function() {
});
it('defaults to multiple failures for specs', function() {
spyOn(jasmineUnderTest, 'Spec').and.callThrough();
spyOn(privateUnderTest, 'Spec').and.callThrough();
env.it('bar', function() {});
expect(jasmineUnderTest.Spec).toHaveBeenCalledWith(
expect(privateUnderTest.Spec).toHaveBeenCalledWith(
jasmine.objectContaining({
throwOnExpectationFailure: false
})
@@ -147,9 +160,9 @@ describe('Env', function() {
});
it('defaults to multiple failures for suites', function() {
spyOn(jasmineUnderTest, 'Suite');
spyOn(privateUnderTest, 'Suite');
env.describe('foo', function() {});
expect(jasmineUnderTest.Suite).toHaveBeenCalledWith(
expect(privateUnderTest.Suite).toHaveBeenCalledWith(
jasmine.objectContaining({
throwOnExpectationFailure: false
})
@@ -192,7 +205,6 @@ describe('Env', function() {
it('throws an error when given arguments', function() {
expect(function() {
// eslint-disable-next-line no-unused-vars
env.describe('done method', function(done) {});
}).toThrowError('describe does not expect any arguments');
});
@@ -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() {
@@ -316,7 +337,7 @@ describe('Env', function() {
it('calls spec.exclude with "Temporarily disabled with xit"', function() {
const excludeSpy = jasmine.createSpy();
spyOn(jasmineUnderTest.SuiteBuilder.prototype, 'it_').and.returnValue({
spyOn(privateUnderTest.SuiteBuilder.prototype, 'it_').and.returnValue({
exclude: excludeSpy
});
env.xit('foo', function() {});
@@ -325,9 +346,9 @@ describe('Env', function() {
it('calls spec.pend with "Temporarily disabled with xit"', function() {
const pendSpy = jasmine.createSpy();
const realExclude = jasmineUnderTest.Spec.prototype.exclude;
const realExclude = privateUnderTest.Spec.prototype.exclude;
spyOn(jasmineUnderTest.SuiteBuilder.prototype, 'it_').and.returnValue({
spyOn(privateUnderTest.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(1);
env.setParallelLoadingState('specs');
check(2);
function check(disambiguator) {
let done = false;
env.describe('a suite ' + disambiguator, 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,6 +573,47 @@ 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(1);
env.setParallelLoadingState('specs');
check(2);
function check(disambiguator) {
let done = false;
env.describe('a suite ' + disambiguator, 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() {
@@ -471,9 +625,12 @@ describe('Env', function() {
'popListener',
'removeOverrideListener'
]);
spyOn(jasmineUnderTest, 'GlobalErrors').and.returnValue(globalErrors);
env.cleanup_();
env = new jasmineUnderTest.Env();
env = new privateUnderTest.Env({
GlobalErrors: function() {
return globalErrors;
}
});
expect(globalErrors.install).toHaveBeenCalled();
});
});
@@ -487,9 +644,13 @@ describe('Env', function() {
'popListener',
'removeOverrideListener'
]);
spyOn(jasmineUnderTest, 'GlobalErrors').and.returnValue(globalErrors);
env.cleanup_();
env = new jasmineUnderTest.Env({ suppressLoadErrors: true });
env = new privateUnderTest.Env({
suppressLoadErrors: true,
GlobalErrors: function() {
return globalErrors;
}
});
expect(globalErrors.install).not.toHaveBeenCalled();
env.execute();
expect(globalErrors.install).toHaveBeenCalled();
@@ -500,12 +661,12 @@ describe('Env', function() {
function customEqualityTester() {}
function customObjectFormatter() {}
function prettyPrinter() {}
const RealSpec = jasmineUnderTest.Spec;
const RealSpec = privateUnderTest.Spec;
let specInstance;
let expectationFactory;
spyOn(jasmineUnderTest, 'MatchersUtil');
spyOn(jasmineUnderTest, 'makePrettyPrinter').and.returnValue(prettyPrinter);
spyOn(jasmineUnderTest, 'Spec').and.callFake(function(options) {
spyOn(privateUnderTest, 'MatchersUtil');
spyOn(privateUnderTest, 'makePrettyPrinter').and.returnValue(prettyPrinter);
spyOn(privateUnderTest, 'Spec').and.callFake(function(options) {
expectationFactory = options.expectationFactory;
specInstance = new RealSpec(options);
return specInstance;
@@ -518,10 +679,10 @@ describe('Env', function() {
});
await env.execute();
expect(jasmineUnderTest.makePrettyPrinter).toHaveBeenCalledWith([
expect(privateUnderTest.makePrettyPrinter).toHaveBeenCalledWith([
customObjectFormatter
]);
expect(jasmineUnderTest.MatchersUtil).toHaveBeenCalledWith({
expect(privateUnderTest.MatchersUtil).toHaveBeenCalledWith({
customTesters: [customEqualityTester],
pp: prettyPrinter
});
@@ -531,12 +692,12 @@ describe('Env', function() {
function customEqualityTester() {}
function customObjectFormatter() {}
function prettyPrinter() {}
const RealSpec = jasmineUnderTest.Spec;
const RealSpec = privateUnderTest.Spec;
let specInstance;
let asyncExpectationFactory;
spyOn(jasmineUnderTest, 'MatchersUtil');
spyOn(jasmineUnderTest, 'makePrettyPrinter').and.returnValue(prettyPrinter);
spyOn(jasmineUnderTest, 'Spec').and.callFake(function(options) {
spyOn(privateUnderTest, 'MatchersUtil');
spyOn(privateUnderTest, 'makePrettyPrinter').and.returnValue(prettyPrinter);
spyOn(privateUnderTest, 'Spec').and.callFake(function(options) {
asyncExpectationFactory = options.asyncExpectationFactory;
specInstance = new RealSpec(options);
return specInstance;
@@ -550,10 +711,10 @@ describe('Env', function() {
await env.execute();
expect(jasmineUnderTest.makePrettyPrinter).toHaveBeenCalledWith([
expect(privateUnderTest.makePrettyPrinter).toHaveBeenCalledWith([
customObjectFormatter
]);
expect(jasmineUnderTest.MatchersUtil).toHaveBeenCalledWith({
expect(privateUnderTest.MatchersUtil).toHaveBeenCalledWith({
customTesters: [customEqualityTester],
pp: prettyPrinter
});
@@ -568,7 +729,7 @@ describe('Env', function() {
env.it('has a spec');
});
expect(suiteThis).not.toBeInstanceOf(jasmineUnderTest.Suite);
expect(suiteThis).not.toBeInstanceOf(privateUnderTest.Suite);
});
describe('#execute', function() {
@@ -577,7 +738,7 @@ describe('Env', function() {
});
it('should reset the topSuite when run twice', function() {
spyOn(jasmineUnderTest.Suite.prototype, 'reset');
spyOn(privateUnderTest.Suite.prototype, 'reset');
return env
.execute() // 1
.then(function() {
@@ -585,13 +746,39 @@ describe('Env', function() {
})
.then(function() {
expect(
jasmineUnderTest.Suite.prototype.reset
privateUnderTest.Suite.prototype.reset
).toHaveBeenCalledOnceWith();
const id = jasmineUnderTest.Suite.prototype.reset.calls.thisFor(0).id;
const id = privateUnderTest.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(privateUnderTest.Suite.prototype, 'reset');
await env.execute();
expect(privateUnderTest.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() {
@@ -608,4 +795,46 @@ describe('Env', function() {
).toBeRejectedWithError(msg);
});
});
describe('#addReporter', function() {
it('throws when called in parallel mode', function() {
env.setParallelLoadingState('helpers');
expect(function() {
env.addReporter({});
}).toThrowError('Reporters cannot be added via Env in parallel mode');
env.setParallelLoadingState('specs');
expect(function() {
env.addReporter({});
}).toThrowError('Reporters cannot be added via Env in parallel mode');
});
});
describe('#clearReporters', function() {
it('throws when called in parallel mode', function() {
env.setParallelLoadingState('helpers');
expect(function() {
env.clearReporters();
}).toThrowError('Reporters cannot be removed via Env in parallel mode');
env.setParallelLoadingState('specs');
expect(function() {
env.clearReporters();
}).toThrowError('Reporters cannot be removed via Env in parallel mode');
});
});
describe('#configure', function() {
it('throws when called in parallel mode', function() {
env.setParallelLoadingState('helpers');
expect(function() {
env.configure({});
}).toThrowError('Jasmine cannot be configured via Env in parallel mode');
env.setParallelLoadingState('specs');
expect(function() {
env.configure({});
}).toThrowError('Jasmine cannot be configured via Env in parallel mode');
});
});
});

View File

@@ -7,7 +7,7 @@ describe('ExceptionFormatter', function() {
message: 'you got your foo in my bar',
name: 'A Classic Mistake'
},
exceptionFormatter = new jasmineUnderTest.ExceptionFormatter(),
exceptionFormatter = new privateUnderTest.ExceptionFormatter(),
message = exceptionFormatter.message(sampleFirefoxException);
expect(message).toEqual(
@@ -22,7 +22,7 @@ describe('ExceptionFormatter', function() {
message: 'you got your foo in my bar',
name: 'A Classic Mistake'
},
exceptionFormatter = new jasmineUnderTest.ExceptionFormatter(),
exceptionFormatter = new privateUnderTest.ExceptionFormatter(),
message = exceptionFormatter.message(sampleWebkitException);
expect(message).toEqual(
@@ -35,7 +35,7 @@ describe('ExceptionFormatter', function() {
message: 'you got your foo in my bar',
name: 'A Classic Mistake'
},
exceptionFormatter = new jasmineUnderTest.ExceptionFormatter(),
exceptionFormatter = new privateUnderTest.ExceptionFormatter(),
message = exceptionFormatter.message(sampleV8);
expect(message).toEqual('A Classic Mistake: you got your foo in my bar');
@@ -44,7 +44,7 @@ describe('ExceptionFormatter', function() {
it('formats unnamed exceptions with message', function() {
const unnamedError = { message: 'This is an unnamed error message.' };
const exceptionFormatter = new jasmineUnderTest.ExceptionFormatter(),
const exceptionFormatter = new privateUnderTest.ExceptionFormatter(),
message = exceptionFormatter.message(unnamedError);
expect(message).toEqual('This is an unnamed error message.');
@@ -57,7 +57,7 @@ describe('ExceptionFormatter', function() {
};
const emptyError = new EmptyError();
const exceptionFormatter = new jasmineUnderTest.ExceptionFormatter(),
const exceptionFormatter = new privateUnderTest.ExceptionFormatter(),
message = exceptionFormatter.message(emptyError);
expect(message).toEqual('[EmptyError] thrown');
@@ -65,7 +65,7 @@ describe('ExceptionFormatter', function() {
it("formats thrown exceptions that aren't errors", function() {
const thrown = 'crazy error',
exceptionFormatter = new jasmineUnderTest.ExceptionFormatter(),
exceptionFormatter = new privateUnderTest.ExceptionFormatter(),
message = exceptionFormatter.message(thrown);
expect(message).toEqual('crazy error thrown');
@@ -76,7 +76,7 @@ describe('ExceptionFormatter', function() {
it('formats stack traces', function() {
const error = new Error('an error');
expect(new jasmineUnderTest.ExceptionFormatter().stack(error)).toMatch(
expect(new privateUnderTest.ExceptionFormatter().stack(error)).toMatch(
/ExceptionFormatterSpec\.js.*\d+/
);
});
@@ -96,7 +96,7 @@ describe('ExceptionFormatter', function() {
' at fn3 (C:\\__jasmine__\\lib\\jasmine-core\\jasmine.js:7575:25)\n' +
' at fn4 (node:internal/timers:462:21)\n'
};
const subject = new jasmineUnderTest.ExceptionFormatter({
const subject = new privateUnderTest.ExceptionFormatter({
jasmineFile: 'C:\\__jasmine__\\lib\\jasmine-core\\jasmine.js'
});
const result = subject.stack(error);
@@ -122,7 +122,7 @@ 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'
};
const subject = new jasmineUnderTest.ExceptionFormatter({
const subject = new privateUnderTest.ExceptionFormatter({
jasmineFile: 'http://localhost:8888/__jasmine__/jasmine.js'
});
const result = subject.stack(error);
@@ -142,7 +142,26 @@ describe('ExceptionFormatter', function() {
'fn2@http://localhost:8888/__jasmine__/jasmine.js:4320:27\n' +
'http://localhost:8888/__spec__/core/UtilSpec.js:115:28'
};
const subject = new jasmineUnderTest.ExceptionFormatter({
const subject = new privateUnderTest.ExceptionFormatter({
jasmineFile: 'http://localhost:8888/__jasmine__/jasmine.js'
});
const result = subject.stack(error);
expect(result).toEqual(
'http://localhost:8888/__spec__/core/UtilSpec.js:115:28\n' +
'<Jasmine>\n' +
'http://localhost:8888/__spec__/core/UtilSpec.js:115:28'
);
});
it('filters Jasmine stack frames 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 privateUnderTest.ExceptionFormatter({
jasmineFile: 'http://localhost:8888/__jasmine__/jasmine.js'
});
const result = subject.stack(error);
@@ -155,10 +174,12 @@ describe('ExceptionFormatter', function() {
it('filters Jasmine stack frames in this environment', function() {
const error = new Error('an error');
const subject = new jasmineUnderTest.ExceptionFormatter({
jasmineFile: jasmine.util.jasmineFile()
const subject = new privateUnderTest.ExceptionFormatter({
jasmineFile: jasmine.private.util.jasmineFile()
});
const result = subject.stack(error);
jasmine.debugLog('Original stack trace: ' + error.stack);
jasmine.debugLog('Filtered stack trace: ' + result);
const lines = result.split('\n');
if (lines[0].match(/an error/)) {
@@ -181,8 +202,8 @@ describe('ExceptionFormatter', function() {
if (error.stack.indexOf(msg) === -1) {
pending("Stack traces don't have messages in this environment");
}
const subject = new jasmineUnderTest.ExceptionFormatter({
jasmineFile: jasmine.util.jasmineFile()
const subject = new privateUnderTest.ExceptionFormatter({
jasmineFile: jasmine.private.util.jasmineFile()
});
const result = subject.stack(error);
const lines = result.split('\n');
@@ -194,18 +215,31 @@ describe('ExceptionFormatter', function() {
});
it('returns null if no Error provided', function() {
expect(new jasmineUnderTest.ExceptionFormatter().stack()).toBeNull();
expect(new privateUnderTest.ExceptionFormatter().stack()).toBeNull();
});
it('includes error properties in stack', function() {
it("includes the error's own properties in stack", function() {
const error = new Error('an error');
error.someProperty = 'hello there';
const result = new jasmineUnderTest.ExceptionFormatter().stack(error);
const result = new privateUnderTest.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 privateUnderTest.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 = {
@@ -217,7 +251,7 @@ 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'
};
const subject = new jasmineUnderTest.ExceptionFormatter({
const subject = new privateUnderTest.ExceptionFormatter({
jasmineFile: 'http://localhost:8888/__jasmine__/jasmine.js'
});
const result = subject.stack(error, { omitMessage: true });
@@ -236,7 +270,7 @@ describe('ExceptionFormatter', function() {
'fn2@http://localhost:8888/__jasmine__/jasmine.js:4320:27\n' +
'http://localhost:8888/__spec__/core/UtilSpec.js:115:28'
};
const subject = new jasmineUnderTest.ExceptionFormatter({
const subject = new privateUnderTest.ExceptionFormatter({
jasmineFile: 'http://localhost:8888/__jasmine__/jasmine.js'
});
const result = subject.stack(error, { omitMessage: true });
@@ -249,12 +283,68 @@ describe('ExceptionFormatter', function() {
it('ensures that stack traces do not include the message in this environment', function() {
const error = new Error('an error');
const subject = new jasmineUnderTest.ExceptionFormatter({
jasmineFile: jasmine.util.jasmineFile()
const subject = new privateUnderTest.ExceptionFormatter({
jasmineFile: jasmine.private.util.jasmineFile()
});
const result = subject.stack(error, { omitMessage: true });
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 privateUnderTest.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 privateUnderTest.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

@@ -2,7 +2,7 @@ describe('Exceptions:', function() {
let env;
beforeEach(function() {
env = new jasmineUnderTest.Env();
env = new privateUnderTest.Env();
});
afterEach(function() {

View File

@@ -3,7 +3,7 @@ describe('ExpectationFilterChain', function() {
it('returns a new filter chain with the added filter', function() {
const first = jasmine.createSpy('first'),
second = jasmine.createSpy('second'),
orig = new jasmineUnderTest.ExpectationFilterChain({
orig = new privateUnderTest.ExpectationFilterChain({
modifyFailureMessage: first
}),
added = orig.addFilter({ selectComparisonFunc: second });
@@ -15,7 +15,7 @@ describe('ExpectationFilterChain', function() {
});
it('does not modify the original filter chain', function() {
const orig = new jasmineUnderTest.ExpectationFilterChain({}),
const orig = new privateUnderTest.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() {
const chain = new jasmineUnderTest.ExpectationFilterChain();
const chain = new privateUnderTest.ExpectationFilterChain();
chain.addFilter({});
expect(chain.selectComparisonFunc()).toBeUndefined();
});
@@ -38,7 +38,7 @@ describe('ExpectationFilterChain', function() {
it('calls the first filter that has #selectComparisonFunc', function() {
const first = jasmine.createSpy('first').and.returnValue('first'),
second = jasmine.createSpy('second').and.returnValue('second'),
chain = new jasmineUnderTest.ExpectationFilterChain()
chain = new privateUnderTest.ExpectationFilterChain()
.addFilter({ selectComparisonFunc: first })
.addFilter({ selectComparisonFunc: second }),
matcher = {},
@@ -54,7 +54,7 @@ describe('ExpectationFilterChain', function() {
describe('#buildFailureMessage', function() {
describe('When no filters have #buildFailureMessage', function() {
it('returns undefined', function() {
const chain = new jasmineUnderTest.ExpectationFilterChain();
const chain = new privateUnderTest.ExpectationFilterChain();
chain.addFilter({});
expect(chain.buildFailureMessage()).toBeUndefined();
});
@@ -64,7 +64,7 @@ describe('ExpectationFilterChain', function() {
it('calls the first filter that has #buildFailureMessage', function() {
const first = jasmine.createSpy('first').and.returnValue('first'),
second = jasmine.createSpy('second').and.returnValue('second'),
chain = new jasmineUnderTest.ExpectationFilterChain()
chain = new privateUnderTest.ExpectationFilterChain()
.addFilter({ buildFailureMessage: first })
.addFilter({ buildFailureMessage: second }),
matcherResult = { pass: false },
@@ -94,7 +94,7 @@ describe('ExpectationFilterChain', function() {
describe('#modifyFailureMessage', function() {
describe('When no filters have #modifyFailureMessage', function() {
it('returns the original message', function() {
const chain = new jasmineUnderTest.ExpectationFilterChain();
const chain = new privateUnderTest.ExpectationFilterChain();
chain.addFilter({});
expect(chain.modifyFailureMessage('msg')).toEqual('msg');
});
@@ -104,7 +104,7 @@ describe('ExpectationFilterChain', function() {
it('calls the first filter that has #modifyFailureMessage', function() {
const first = jasmine.createSpy('first').and.returnValue('first'),
second = jasmine.createSpy('second').and.returnValue('second'),
chain = new jasmineUnderTest.ExpectationFilterChain()
chain = new privateUnderTest.ExpectationFilterChain()
.addFilter({ modifyFailureMessage: first })
.addFilter({ modifyFailureMessage: second }),
result = chain.modifyFailureMessage('original');

View File

@@ -4,7 +4,7 @@ describe('Expectation', function() {
toFoo: function() {},
toBar: function() {}
},
expectation = jasmineUnderTest.Expectation.factory({
expectation = privateUnderTest.Expectation.factory({
customMatchers: matchers
});
@@ -17,9 +17,9 @@ describe('Expectation', function() {
toQuux: function() {}
};
jasmineUnderTest.Expectation.addCoreMatchers(coreMatchers);
privateUnderTest.Expectation.addCoreMatchers(coreMatchers);
const expectation = jasmineUnderTest.Expectation.factory({});
const expectation = privateUnderTest.Expectation.factory({});
expect(expectation.toQuux).toBeDefined();
});
@@ -39,7 +39,7 @@ describe('Expectation', function() {
},
addExpectationResult = jasmine.createSpy('addExpectationResult');
const expectation = jasmineUnderTest.Expectation.factory({
const expectation = privateUnderTest.Expectation.factory({
matchersUtil: matchersUtil,
customMatchers: matchers,
actual: 'an actual',
@@ -67,7 +67,7 @@ describe('Expectation', function() {
},
addExpectationResult = jasmine.createSpy('addExpectationResult');
const expectation = jasmineUnderTest.Expectation.factory({
const expectation = privateUnderTest.Expectation.factory({
matchersUtil: matchersUtil,
customMatchers: matchers,
actual: 'an actual',
@@ -94,7 +94,7 @@ describe('Expectation', function() {
},
addExpectationResult = jasmine.createSpy('addExpectationResult');
const expectation = jasmineUnderTest.Expectation.factory({
const expectation = privateUnderTest.Expectation.factory({
customMatchers: matchers,
matchersUtil: matchersUtil,
actual: 'an actual',
@@ -108,8 +108,6 @@ describe('Expectation', function() {
passed: true,
message: '',
error: undefined,
expected: 'hello',
actual: 'an actual',
errorForStack: undefined
});
});
@@ -131,7 +129,7 @@ describe('Expectation', function() {
},
addExpectationResult = jasmine.createSpy('addExpectationResult');
const expectation = jasmineUnderTest.Expectation.factory({
const expectation = privateUnderTest.Expectation.factory({
customMatchers: matchers,
matchersUtil: matchersUtil,
actual: 'an actual',
@@ -143,8 +141,6 @@ describe('Expectation', function() {
expect(addExpectationResult).toHaveBeenCalledWith(false, {
matcherName: 'toFoo',
passed: false,
expected: 'hello',
actual: 'an actual',
message: '',
error: undefined,
errorForStack: undefined
@@ -166,7 +162,7 @@ describe('Expectation', function() {
},
addExpectationResult = jasmine.createSpy('addExpectationResult');
const expectation = jasmineUnderTest.Expectation.factory({
const expectation = privateUnderTest.Expectation.factory({
actual: 'an actual',
customMatchers: matchers,
addExpectationResult: addExpectationResult
@@ -177,8 +173,6 @@ describe('Expectation', function() {
expect(addExpectationResult).toHaveBeenCalledWith(false, {
matcherName: 'toFoo',
passed: false,
expected: 'hello',
actual: 'an actual',
message: 'I am a custom message',
error: undefined,
errorForStack: undefined
@@ -202,7 +196,7 @@ describe('Expectation', function() {
},
addExpectationResult = jasmine.createSpy('addExpectationResult');
const expectation = jasmineUnderTest.Expectation.factory({
const expectation = privateUnderTest.Expectation.factory({
customMatchers: matchers,
actual: 'an actual',
addExpectationResult: addExpectationResult
@@ -213,8 +207,6 @@ describe('Expectation', function() {
expect(addExpectationResult).toHaveBeenCalledWith(false, {
matcherName: 'toFoo',
passed: false,
expected: 'hello',
actual: 'an actual',
message: 'I am a custom message',
error: undefined,
errorForStack: undefined
@@ -231,10 +223,9 @@ describe('Expectation', function() {
};
}
},
addExpectationResult = jasmine.createSpy('addExpectationResult'),
actual = 'an actual';
addExpectationResult = jasmine.createSpy('addExpectationResult');
const expectation = jasmineUnderTest.Expectation.factory({
const expectation = privateUnderTest.Expectation.factory({
customMatchers: matchers,
actual: 'an actual',
addExpectationResult: addExpectationResult
@@ -247,8 +238,6 @@ describe('Expectation', function() {
passed: true,
message: '',
error: undefined,
expected: 'hello',
actual: actual,
errorForStack: undefined
});
});
@@ -268,10 +257,9 @@ describe('Expectation', function() {
return 'default message';
}
},
addExpectationResult = jasmine.createSpy('addExpectationResult'),
actual = 'an actual';
addExpectationResult = jasmine.createSpy('addExpectationResult');
const expectation = jasmineUnderTest.Expectation.factory({
const expectation = privateUnderTest.Expectation.factory({
customMatchers: matchers,
actual: 'an actual',
matchersUtil: matchersUtil,
@@ -283,8 +271,6 @@ describe('Expectation', function() {
expect(addExpectationResult).toHaveBeenCalledWith(false, {
matcherName: 'toFoo',
passed: false,
expected: 'hello',
actual: actual,
message: 'default message',
error: undefined,
errorForStack: undefined
@@ -304,10 +290,9 @@ describe('Expectation', function() {
};
}
},
addExpectationResult = jasmine.createSpy('addExpectationResult'),
actual = 'an actual';
addExpectationResult = jasmine.createSpy('addExpectationResult');
const expectation = jasmineUnderTest.Expectation.factory({
const expectation = privateUnderTest.Expectation.factory({
customMatchers: matchers,
actual: 'an actual',
addExpectationResult: addExpectationResult
@@ -318,8 +303,6 @@ describe('Expectation', function() {
expect(addExpectationResult).toHaveBeenCalledWith(false, {
matcherName: 'toFoo',
passed: false,
expected: 'hello',
actual: actual,
message: 'I am a custom message',
error: undefined,
errorForStack: undefined
@@ -339,10 +322,9 @@ describe('Expectation', function() {
};
}
},
addExpectationResult = jasmine.createSpy('addExpectationResult'),
actual = 'an actual';
addExpectationResult = jasmine.createSpy('addExpectationResult');
const expectation = jasmineUnderTest.Expectation.factory({
const expectation = privateUnderTest.Expectation.factory({
customMatchers: matchers,
actual: 'an actual',
addExpectationResult: addExpectationResult
@@ -353,8 +335,6 @@ describe('Expectation', function() {
expect(addExpectationResult).toHaveBeenCalledWith(true, {
matcherName: 'toFoo',
passed: true,
expected: 'hello',
actual: actual,
message: '',
error: undefined,
errorForStack: undefined
@@ -377,10 +357,9 @@ describe('Expectation', function() {
};
}
},
addExpectationResult = jasmine.createSpy('addExpectationResult'),
actual = 'an actual';
addExpectationResult = jasmine.createSpy('addExpectationResult');
const expectation = jasmineUnderTest.Expectation.factory({
const expectation = privateUnderTest.Expectation.factory({
customMatchers: matchers,
actual: 'an actual',
addExpectationResult: addExpectationResult
@@ -391,8 +370,6 @@ describe('Expectation', function() {
expect(addExpectationResult).toHaveBeenCalledWith(false, {
matcherName: 'toFoo',
passed: false,
expected: 'hello',
actual: actual,
message: "I'm a custom message",
error: undefined,
errorForStack: undefined
@@ -416,7 +393,7 @@ describe('Expectation', function() {
},
addExpectationResult = jasmine.createSpy('addExpectationResult');
const expectation = jasmineUnderTest.Expectation.factory({
const expectation = privateUnderTest.Expectation.factory({
actual: 'an actual',
customMatchers: matchers,
addExpectationResult: addExpectationResult
@@ -427,8 +404,6 @@ describe('Expectation', function() {
expect(addExpectationResult).toHaveBeenCalledWith(false, {
matcherName: 'toFoo',
passed: false,
expected: 'hello',
actual: 'an actual',
message: 'I am a custom message',
error: customError,
errorForStack: undefined
@@ -452,7 +427,7 @@ describe('Expectation', function() {
},
addExpectationResult = jasmine.createSpy('addExpectationResult');
const expectation = jasmineUnderTest.Expectation.factory({
const expectation = privateUnderTest.Expectation.factory({
actual: 'an actual',
customMatchers: matchers,
addExpectationResult: addExpectationResult
@@ -463,8 +438,6 @@ describe('Expectation', function() {
expect(addExpectationResult).toHaveBeenCalledWith(false, {
matcherName: 'toFoo',
passed: false,
expected: 'hello',
actual: 'an actual',
message: 'I am a custom message',
error: customError,
errorForStack: undefined
@@ -490,7 +463,7 @@ describe('Expectation', function() {
},
addExpectationResult = jasmine.createSpy('addExpectationResult');
const expectation = jasmineUnderTest.Expectation.factory({
const expectation = privateUnderTest.Expectation.factory({
actual: 'an actual',
customMatchers: matchers,
addExpectationResult: addExpectationResult
@@ -501,8 +474,6 @@ describe('Expectation', function() {
expect(addExpectationResult).toHaveBeenCalledWith(false, {
matcherName: 'toFoo',
passed: false,
expected: 'hello',
actual: 'an actual',
message: 'I am a custom message',
error: customError,
errorForStack: undefined
@@ -526,7 +497,7 @@ describe('Expectation', function() {
}
},
addExpectationResult = jasmine.createSpy('addExpectationResult'),
expectation = jasmineUnderTest.Expectation.factory({
expectation = privateUnderTest.Expectation.factory({
customMatchers: matchers,
matchersUtil: matchersUtil,
actual: 'an actual',
@@ -554,7 +525,7 @@ describe('Expectation', function() {
}
},
addExpectationResult = jasmine.createSpy('addExpectationResult'),
expectation = jasmineUnderTest.Expectation.factory({
expectation = privateUnderTest.Expectation.factory({
customMatchers: matchers,
actual: 'an actual',
addExpectationResult: addExpectationResult
@@ -581,7 +552,7 @@ describe('Expectation', function() {
}
},
addExpectationResult = jasmine.createSpy('addExpectationResult'),
expectation = jasmineUnderTest.Expectation.factory({
expectation = privateUnderTest.Expectation.factory({
customMatchers: matchers,
actual: 'an actual',
addExpectationResult: addExpectationResult
@@ -611,7 +582,7 @@ describe('Expectation', function() {
}
},
addExpectationResult = jasmine.createSpy('addExpectationResult'),
expectation = jasmineUnderTest.Expectation.factory({
expectation = privateUnderTest.Expectation.factory({
customMatchers: matchers,
actual: 'an actual',
addExpectationResult: addExpectationResult
@@ -638,10 +609,10 @@ describe('Expectation', function() {
}
},
addExpectationResult = jasmine.createSpy('addExpectationResult'),
pp = jasmineUnderTest.makePrettyPrinter(),
expectation = jasmineUnderTest.Expectation.factory({
pp = privateUnderTest.makePrettyPrinter(),
expectation = privateUnderTest.Expectation.factory({
customMatchers: matchers,
matchersUtil: new jasmineUnderTest.MatchersUtil({ pp: pp }),
matchersUtil: new privateUnderTest.MatchersUtil({ pp: pp }),
actual: 'an actual',
addExpectationResult: addExpectationResult
});
@@ -674,7 +645,7 @@ describe('Expectation', function() {
}
},
addExpectationResult = jasmine.createSpy('addExpectationResult'),
expectation = jasmineUnderTest.Expectation.factory({
expectation = privateUnderTest.Expectation.factory({
actual: 'an actual',
customMatchers: matchers,
addExpectationResult: addExpectationResult

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() {
const reporter = new jasmineUnderTest.JsApiReporter({});
const reporter = new privateUnderTest.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() {
const reporter = new jasmineUnderTest.JsApiReporter({});
const reporter = new privateUnderTest.JsApiReporter({});
expect(reporter.started).toBe(false);
expect(reporter.finished).toBe(false);
@@ -24,13 +24,13 @@ describe('JsApiReporter', function() {
});
it("defaults to 'loaded' status", function() {
const reporter = new jasmineUnderTest.JsApiReporter({});
const reporter = new privateUnderTest.JsApiReporter({});
expect(reporter.status()).toEqual('loaded');
});
it("reports 'started' when Jasmine has started", function() {
const reporter = new jasmineUnderTest.JsApiReporter({});
const reporter = new privateUnderTest.JsApiReporter({});
reporter.jasmineStarted();
@@ -38,7 +38,7 @@ describe('JsApiReporter', function() {
});
it("reports 'done' when Jasmine is done", function() {
const reporter = new jasmineUnderTest.JsApiReporter({});
const reporter = new privateUnderTest.JsApiReporter({});
reporter.jasmineDone({});
@@ -46,7 +46,7 @@ describe('JsApiReporter', function() {
});
it('tracks a suite', function() {
const reporter = new jasmineUnderTest.JsApiReporter({});
const reporter = new privateUnderTest.JsApiReporter({});
reporter.suiteStarted({
id: 123,
@@ -71,7 +71,7 @@ describe('JsApiReporter', function() {
describe('#specResults', function() {
let reporter, specResult1, specResult2;
beforeEach(function() {
reporter = new jasmineUnderTest.JsApiReporter({});
reporter = new privateUnderTest.JsApiReporter({});
specResult1 = {
id: 1,
description: 'A spec'
@@ -101,7 +101,7 @@ describe('JsApiReporter', function() {
describe('#suiteResults', function() {
let reporter, suiteStarted1, suiteResult1, suiteResult2;
beforeEach(function() {
reporter = new jasmineUnderTest.JsApiReporter({});
reporter = new privateUnderTest.JsApiReporter({});
suiteStarted1 = {
id: 1
};
@@ -138,7 +138,7 @@ describe('JsApiReporter', function() {
describe('#executionTime', function() {
it('should start the timer when jasmine starts', function() {
const timerSpy = jasmine.createSpyObj('timer', ['start', 'elapsed']),
reporter = new jasmineUnderTest.JsApiReporter({
reporter = new privateUnderTest.JsApiReporter({
timer: timerSpy
});
@@ -148,7 +148,7 @@ describe('JsApiReporter', function() {
it('should return the time it took the specs to run, in ms', function() {
const timerSpy = jasmine.createSpyObj('timer', ['start', 'elapsed']),
reporter = new jasmineUnderTest.JsApiReporter({
reporter = new privateUnderTest.JsApiReporter({
timer: timerSpy
});
@@ -160,7 +160,7 @@ describe('JsApiReporter', function() {
describe("when the specs haven't finished being run", function() {
it('should return undefined', function() {
const timerSpy = jasmine.createSpyObj('timer', ['start', 'elapsed']),
reporter = new jasmineUnderTest.JsApiReporter({
reporter = new privateUnderTest.JsApiReporter({
timer: timerSpy
});
@@ -171,7 +171,7 @@ describe('JsApiReporter', function() {
describe('#runDetails', function() {
it('should have details about the run', function() {
const reporter = new jasmineUnderTest.JsApiReporter({});
const reporter = new privateUnderTest.JsApiReporter({});
reporter.jasmineDone({ some: { run: 'details' } });
expect(reporter.runDetails).toEqual({ some: { run: 'details' } });
});

View File

@@ -1,7 +1,7 @@
describe('FakeDate', function() {
it('does not fail if no global date is found', function() {
const fakeGlobal = {},
mockDate = new jasmineUnderTest.MockDate(fakeGlobal);
mockDate = new privateUnderTest.MockDate(fakeGlobal);
expect(function() {
mockDate.install();
@@ -19,7 +19,7 @@ describe('FakeDate', function() {
};
}),
fakeGlobal = { Date: globalDate },
mockDate = new jasmineUnderTest.MockDate(fakeGlobal);
mockDate = new privateUnderTest.MockDate(fakeGlobal);
expect(fakeGlobal.Date).toEqual(globalDate);
mockDate.install();
@@ -36,7 +36,7 @@ describe('FakeDate', function() {
};
}),
fakeGlobal = { Date: globalDate },
mockDate = new jasmineUnderTest.MockDate(fakeGlobal);
mockDate = new privateUnderTest.MockDate(fakeGlobal);
mockDate.install();
mockDate.uninstall();
@@ -55,7 +55,7 @@ describe('FakeDate', function() {
};
}),
fakeGlobal = { Date: globalDate },
mockDate = new jasmineUnderTest.MockDate(fakeGlobal);
mockDate = new privateUnderTest.MockDate(fakeGlobal);
mockDate.install();
@@ -66,7 +66,7 @@ describe('FakeDate', function() {
it('can accept a date as time base when installing', function() {
const fakeGlobal = { Date: Date },
mockDate = new jasmineUnderTest.MockDate(fakeGlobal),
mockDate = new privateUnderTest.MockDate(fakeGlobal),
baseDate = new Date();
spyOn(baseDate, 'getTime').and.returnValue(123);
@@ -77,7 +77,7 @@ describe('FakeDate', function() {
it('makes real dates', function() {
const fakeGlobal = { Date: Date },
mockDate = new jasmineUnderTest.MockDate(fakeGlobal);
mockDate = new privateUnderTest.MockDate(fakeGlobal);
mockDate.install();
expect(new fakeGlobal.Date()).toEqual(jasmine.any(Date));
@@ -97,7 +97,7 @@ describe('FakeDate', function() {
fakeGlobal = { Date: globalDate };
globalDate.now = function() {};
const mockDate = new jasmineUnderTest.MockDate(fakeGlobal);
const mockDate = new privateUnderTest.MockDate(fakeGlobal);
mockDate.install();
@@ -117,7 +117,7 @@ describe('FakeDate', function() {
fakeGlobal = { Date: globalDate };
globalDate.now = function() {};
const mockDate = new jasmineUnderTest.MockDate(fakeGlobal);
const mockDate = new privateUnderTest.MockDate(fakeGlobal);
mockDate.install();
@@ -143,7 +143,7 @@ describe('FakeDate', function() {
fakeGlobal = { Date: globalDate };
globalDate.now = function() {};
const mockDate = new jasmineUnderTest.MockDate(fakeGlobal);
const mockDate = new privateUnderTest.MockDate(fakeGlobal);
mockDate.install();
@@ -156,7 +156,7 @@ describe('FakeDate', function() {
it('allows creation of a Date in a different time than the mocked time', function() {
const fakeGlobal = { Date: Date },
mockDate = new jasmineUnderTest.MockDate(fakeGlobal);
mockDate = new privateUnderTest.MockDate(fakeGlobal);
mockDate.install();
@@ -168,7 +168,7 @@ describe('FakeDate', function() {
it("allows creation of a Date that isn't fully specified", function() {
const fakeGlobal = { Date: Date },
mockDate = new jasmineUnderTest.MockDate(fakeGlobal);
mockDate = new privateUnderTest.MockDate(fakeGlobal);
mockDate.install();
@@ -178,7 +178,7 @@ describe('FakeDate', function() {
it('allows creation of a Date with millis', function() {
const fakeGlobal = { Date: Date },
mockDate = new jasmineUnderTest.MockDate(fakeGlobal),
mockDate = new privateUnderTest.MockDate(fakeGlobal),
now = new Date(2014, 3, 15).getTime();
mockDate.install();
@@ -189,7 +189,7 @@ describe('FakeDate', function() {
it('copies all Date properties to the mocked date', function() {
const fakeGlobal = { Date: Date },
mockDate = new jasmineUnderTest.MockDate(fakeGlobal);
mockDate = new privateUnderTest.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,12 +1,12 @@
describe('PrettyPrinter', function() {
it('should wrap strings in single quotes', function() {
const pp = jasmineUnderTest.makePrettyPrinter();
const pp = privateUnderTest.makePrettyPrinter();
expect(pp('some string')).toEqual("'some string'");
expect(pp("som' string")).toEqual("'som' string'");
});
it('stringifies empty string primitives and objects recognizably', function() {
const pp = jasmineUnderTest.makePrettyPrinter();
const pp = privateUnderTest.makePrettyPrinter();
expect(pp(new String(''))).toEqual(pp(''));
expect(pp(new String(''))).toEqual("''");
expect(pp([new String('')])).toEqual(pp(['']));
@@ -14,7 +14,7 @@ describe('PrettyPrinter', function() {
});
it('should stringify primitives properly', function() {
const pp = jasmineUnderTest.makePrettyPrinter();
const pp = privateUnderTest.makePrettyPrinter();
expect(pp(true)).toEqual('true');
expect(pp(false)).toEqual('false');
expect(pp(null)).toEqual('null');
@@ -29,7 +29,7 @@ describe('PrettyPrinter', function() {
const set = new Set();
set.add(1);
set.add(2);
const pp = jasmineUnderTest.makePrettyPrinter();
const pp = privateUnderTest.makePrettyPrinter();
expect(pp(set)).toEqual('Set( 1, 2 )');
});
@@ -42,7 +42,7 @@ describe('PrettyPrinter', function() {
set.add('a');
set.add('b');
set.add('c');
const pp = jasmineUnderTest.makePrettyPrinter();
const pp = privateUnderTest.makePrettyPrinter();
expect(pp(set)).toEqual("Set( 'a', 'b', ... )");
} finally {
jasmineUnderTest.MAX_PRETTY_PRINT_ARRAY_LENGTH = originalMaxSize;
@@ -54,7 +54,7 @@ describe('PrettyPrinter', function() {
it('should stringify maps properly', function() {
const map = new Map();
map.set(1, 2);
const pp = jasmineUnderTest.makePrettyPrinter();
const pp = privateUnderTest.makePrettyPrinter();
expect(pp(map)).toEqual('Map( [ 1, 2 ] )');
});
@@ -67,7 +67,7 @@ describe('PrettyPrinter', function() {
map.set('a', 1);
map.set('b', 2);
map.set('c', 3);
const pp = jasmineUnderTest.makePrettyPrinter();
const pp = privateUnderTest.makePrettyPrinter();
expect(pp(map)).toEqual("Map( [ 'a', 1 ], [ 'b', 2 ], ... )");
} finally {
jasmineUnderTest.MAX_PRETTY_PRINT_ARRAY_LENGTH = originalMaxSize;
@@ -77,7 +77,7 @@ describe('PrettyPrinter', function() {
describe('stringify arrays', function() {
it('should stringify arrays properly', function() {
const pp = jasmineUnderTest.makePrettyPrinter();
const pp = privateUnderTest.makePrettyPrinter();
expect(pp([1, 2])).toEqual('[ 1, 2 ]');
expect(pp([1, 'foo', {}, jasmine.undefined, null])).toEqual(
"[ 1, 'foo', Object({ }), undefined, null ]"
@@ -85,14 +85,14 @@ describe('PrettyPrinter', function() {
});
it('includes symbols', function() {
const pp = jasmineUnderTest.makePrettyPrinter();
const pp = privateUnderTest.makePrettyPrinter();
expect(pp([1, Symbol('foo'), 2])).toEqual('[ 1, Symbol(foo), 2 ]');
});
it('should truncate arrays that are longer than jasmineUnderTest.MAX_PRETTY_PRINT_ARRAY_LENGTH', function() {
const originalMaxLength = jasmineUnderTest.MAX_PRETTY_PRINT_ARRAY_LENGTH;
const array = [1, 2, 3];
const pp = jasmineUnderTest.makePrettyPrinter();
const pp = privateUnderTest.makePrettyPrinter();
try {
jasmineUnderTest.MAX_PRETTY_PRINT_ARRAY_LENGTH = 2;
@@ -103,7 +103,7 @@ describe('PrettyPrinter', function() {
});
it('should stringify arrays with properties properly', function() {
const pp = jasmineUnderTest.makePrettyPrinter();
const pp = privateUnderTest.makePrettyPrinter();
const arr = [1, 2];
arr.foo = 'bar';
arr.baz = {};
@@ -111,7 +111,7 @@ describe('PrettyPrinter', function() {
});
it('should stringify empty arrays with properties properly', function() {
const pp = jasmineUnderTest.makePrettyPrinter();
const pp = privateUnderTest.makePrettyPrinter();
const empty = [];
empty.foo = 'bar';
empty.baz = {};
@@ -119,7 +119,7 @@ describe('PrettyPrinter', function() {
});
it('should stringify long arrays with properties properly', function() {
const pp = jasmineUnderTest.makePrettyPrinter();
const pp = privateUnderTest.makePrettyPrinter();
const originalMaxLength = jasmineUnderTest.MAX_PRETTY_PRINT_ARRAY_LENGTH;
const long = [1, 2, 3];
long.foo = 'bar';
@@ -136,7 +136,7 @@ describe('PrettyPrinter', function() {
});
it('should indicate circular array references', function() {
const pp = jasmineUnderTest.makePrettyPrinter();
const pp = privateUnderTest.makePrettyPrinter();
const array1 = [1, 2];
const array2 = [array1];
array1.push(array2);
@@ -144,14 +144,14 @@ describe('PrettyPrinter', function() {
});
it('should not indicate circular references incorrectly', function() {
const pp = jasmineUnderTest.makePrettyPrinter();
const pp = privateUnderTest.makePrettyPrinter();
const array = [[1]];
expect(pp(array)).toEqual('[ [ 1 ] ]');
});
});
it('should stringify objects properly', function() {
const pp = jasmineUnderTest.makePrettyPrinter();
const pp = privateUnderTest.makePrettyPrinter();
expect(pp({ foo: 'bar' })).toEqual("Object({ foo: 'bar' })");
expect(
pp({
@@ -164,19 +164,19 @@ 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 pp = privateUnderTest.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 pp = privateUnderTest.makePrettyPrinter();
const symObj = {};
const strObj = {};
const k = 'foo';
@@ -188,12 +188,12 @@ describe('PrettyPrinter', function() {
});
it('should stringify objects that almost look like DOM nodes', function() {
const pp = jasmineUnderTest.makePrettyPrinter();
const pp = privateUnderTest.makePrettyPrinter();
expect(pp({ nodeType: 1 })).toEqual('Object({ nodeType: 1 })');
});
it('should truncate objects with too many keys', function() {
const pp = jasmineUnderTest.makePrettyPrinter();
const pp = privateUnderTest.makePrettyPrinter();
const originalMaxLength = jasmineUnderTest.MAX_PRETTY_PRINT_ARRAY_LENGTH;
const long = { a: 1, b: 2, c: 3 };
@@ -217,7 +217,7 @@ describe('PrettyPrinter', function() {
}
it('should truncate outputs that are too long', function() {
const pp = jasmineUnderTest.makePrettyPrinter();
const pp = privateUnderTest.makePrettyPrinter();
const big = [{ a: 1, b: 'a long string' }, {}];
withMaxChars(34, function() {
@@ -246,7 +246,7 @@ describe('PrettyPrinter', function() {
.createSpy('d jasmineToString')
.and.returnValue('')
},
pp = jasmineUnderTest.makePrettyPrinter();
pp = privateUnderTest.makePrettyPrinter();
withMaxChars(30, function() {
pp([{ a: a, b: b, c: c }, d]);
@@ -256,13 +256,13 @@ describe('PrettyPrinter', function() {
});
it("should print 'null' as the constructor of an object with its own constructor property", function() {
const pp = jasmineUnderTest.makePrettyPrinter();
const pp = privateUnderTest.makePrettyPrinter();
expect(pp({ constructor: function() {} })).toContain('null({');
expect(pp({ constructor: 'foo' })).toContain('null({');
});
it('should not include inherited properties when stringifying an object', function() {
const pp = jasmineUnderTest.makePrettyPrinter();
const pp = privateUnderTest.makePrettyPrinter();
const SomeClass = function SomeClass() {};
SomeClass.prototype.foo = 'inherited foo';
const instance = new SomeClass();
@@ -271,7 +271,7 @@ describe('PrettyPrinter', function() {
});
it('should not recurse objects and arrays more deeply than jasmineUnderTest.MAX_PRETTY_PRINT_DEPTH', function() {
const pp = jasmineUnderTest.makePrettyPrinter();
const pp = privateUnderTest.makePrettyPrinter();
const originalMaxDepth = jasmineUnderTest.MAX_PRETTY_PRINT_DEPTH;
const nestedObject = { level1: { level2: { level3: { level4: 'leaf' } } } };
const nestedArray = [1, [2, [3, [4, 'leaf']]]];
@@ -300,7 +300,7 @@ describe('PrettyPrinter', function() {
});
it('should stringify immutable circular objects', function() {
const pp = jasmineUnderTest.makePrettyPrinter();
const pp = privateUnderTest.makePrettyPrinter();
let frozenObject = { foo: { bar: 'baz' } };
frozenObject.circular = frozenObject;
frozenObject = Object.freeze(frozenObject);
@@ -310,12 +310,12 @@ describe('PrettyPrinter', function() {
});
it('should stringify RegExp objects properly', function() {
const pp = jasmineUnderTest.makePrettyPrinter();
const pp = privateUnderTest.makePrettyPrinter();
expect(pp(/x|y|z/)).toEqual('/x|y|z/');
});
it('should indicate circular object references', function() {
const pp = jasmineUnderTest.makePrettyPrinter();
const pp = privateUnderTest.makePrettyPrinter();
const sampleValue = { foo: 'hello' };
sampleValue.nested = sampleValue;
expect(pp(sampleValue)).toEqual(
@@ -324,7 +324,7 @@ describe('PrettyPrinter', function() {
});
it('should use the return value of getters', function() {
const pp = jasmineUnderTest.makePrettyPrinter();
const pp = privateUnderTest.makePrettyPrinter();
const sampleValue = {
id: 1,
get calculatedValue() {
@@ -337,19 +337,19 @@ describe('PrettyPrinter', function() {
});
it('should not do HTML escaping of strings', function() {
const pp = jasmineUnderTest.makePrettyPrinter();
const pp = privateUnderTest.makePrettyPrinter();
expect(pp('some <b>html string</b> &', false)).toEqual(
"'some <b>html string</b> &'"
);
});
it('should abbreviate the global (usually window) object', function() {
const pp = jasmineUnderTest.makePrettyPrinter();
const pp = privateUnderTest.makePrettyPrinter();
expect(pp(jasmine.getGlobal())).toEqual('<global>');
});
it('should stringify Date objects properly', function() {
const pp = jasmineUnderTest.makePrettyPrinter();
const pp = privateUnderTest.makePrettyPrinter();
const now = new Date();
expect(pp(now)).toEqual('Date(' + now.toString() + ')');
});
@@ -358,8 +358,8 @@ describe('PrettyPrinter', function() {
let env, pp;
beforeEach(function() {
env = new jasmineUnderTest.Env();
pp = jasmineUnderTest.makePrettyPrinter();
env = new privateUnderTest.Env();
pp = privateUnderTest.makePrettyPrinter();
});
afterEach(function() {
@@ -371,12 +371,12 @@ describe('PrettyPrinter', function() {
someFunction: function() {}
};
const spyRegistry = new jasmineUnderTest.SpyRegistry({
const spyRegistry = new privateUnderTest.SpyRegistry({
currentSpies: function() {
return [];
},
createSpy: function(name, originalFn) {
return jasmineUnderTest.Spy(name, originalFn);
return privateUnderTest.Spy(name, originalFn);
}
});
@@ -390,15 +390,15 @@ describe('PrettyPrinter', function() {
const TestObject = {
someFunction: function() {}
},
env = new jasmineUnderTest.Env(),
pp = jasmineUnderTest.makePrettyPrinter();
env = new privateUnderTest.Env(),
pp = privateUnderTest.makePrettyPrinter();
const spyRegistry = new jasmineUnderTest.SpyRegistry({
const spyRegistry = new privateUnderTest.SpyRegistry({
currentSpies: function() {
return [];
},
createSpy: function(name, originalFn) {
return jasmineUnderTest.Spy(name, originalFn);
return privateUnderTest.Spy(name, originalFn);
}
});
@@ -410,7 +410,7 @@ describe('PrettyPrinter', function() {
});
it('should stringify objects that implement jasmineToString', function() {
const pp = jasmineUnderTest.makePrettyPrinter();
const pp = privateUnderTest.makePrettyPrinter();
const obj = {
jasmineToString: function() {
return 'strung';
@@ -421,7 +421,7 @@ describe('PrettyPrinter', function() {
});
it('should pass itself to jasmineToString', function() {
const pp = jasmineUnderTest.makePrettyPrinter([]);
const pp = privateUnderTest.makePrettyPrinter([]);
const obj = {
jasmineToString: jasmine.createSpy('jasmineToString').and.returnValue('')
};
@@ -431,7 +431,7 @@ describe('PrettyPrinter', function() {
});
it('should stringify objects that implement custom toString', function() {
const pp = jasmineUnderTest.makePrettyPrinter();
const pp = privateUnderTest.makePrettyPrinter();
const obj = {
toString: function() {
return 'my toString';
@@ -450,12 +450,12 @@ 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() {
const pp = jasmineUnderTest.makePrettyPrinter();
const pp = privateUnderTest.makePrettyPrinter();
const obj = {
toString: 'foo'
};
@@ -464,7 +464,7 @@ describe('PrettyPrinter', function() {
});
it('should stringify objects from anonymous constructors with custom toString', function() {
const pp = jasmineUnderTest.makePrettyPrinter();
const pp = privateUnderTest.makePrettyPrinter();
const MyAnonymousConstructor = (function() {
return function() {};
})();
@@ -477,8 +477,19 @@ describe('PrettyPrinter', function() {
expect(pp(a)).toEqual('<anonymous>({ })');
});
it('stringifies functions with names', function() {
const pp = privateUnderTest.makePrettyPrinter();
expect(pp(foo)).toEqual("Function 'foo'");
function foo() {}
});
it('stringifies functions without names', function() {
const pp = privateUnderTest.makePrettyPrinter();
expect(pp(function() {})).toEqual('Function');
});
it('should handle objects with null prototype', function() {
const pp = jasmineUnderTest.makePrettyPrinter();
const pp = privateUnderTest.makePrettyPrinter();
const obj = Object.create(null);
obj.foo = 'bar';
@@ -486,7 +497,7 @@ describe('PrettyPrinter', function() {
});
it('should gracefully handle objects with invalid toString implementations', function() {
const pp = jasmineUnderTest.makePrettyPrinter();
const pp = privateUnderTest.makePrettyPrinter();
const obj = {
foo: {
toString: function() {
@@ -534,7 +545,7 @@ describe('PrettyPrinter', function() {
return '3rd: ' + obj.foo;
}
],
pp = jasmineUnderTest.makePrettyPrinter(customObjectFormatters),
pp = privateUnderTest.makePrettyPrinter(customObjectFormatters),
obj = { foo: 'bar' };
expect(pp(obj)).toEqual('2nd: bar');
@@ -546,7 +557,7 @@ describe('PrettyPrinter', function() {
return undefined;
}
],
pp = jasmineUnderTest.makePrettyPrinter(customObjectFormatters),
pp = privateUnderTest.makePrettyPrinter(customObjectFormatters),
obj = { foo: 'bar' };
expect(pp(obj)).toEqual("Object({ foo: 'bar' })");
@@ -566,7 +577,7 @@ describe('PrettyPrinter', function() {
return '3rd: ' + obj.foo;
}
],
pp = jasmineUnderTest.makePrettyPrinter(customObjectFormatters),
pp = privateUnderTest.makePrettyPrinter(customObjectFormatters),
obj = { foo: 'bar' };
expect(pp.customFormat_(obj)).toEqual('2nd: bar');
@@ -578,7 +589,7 @@ describe('PrettyPrinter', function() {
return undefined;
}
],
pp = jasmineUnderTest.makePrettyPrinter(customObjectFormatters),
pp = privateUnderTest.makePrettyPrinter(customObjectFormatters),
obj = { foo: 'bar' };
expect(pp.customFormat_(obj)).toBeUndefined();

View File

@@ -1,9 +1,25 @@
describe('QueueRunner', function() {
it('validates that queueableFns are truthy', function() {
expect(function() {
new privateUnderTest.QueueRunner({
queueableFns: [undefined]
});
}).toThrowError('Received a falsy queueableFn');
});
it('validates that queueableFns have fn properties', function() {
expect(function() {
new privateUnderTest.QueueRunner({
queueableFns: [{ fn: undefined }]
});
}).toThrowError('Received a queueableFn with no fn');
});
it("runs all the functions it's passed", function() {
const calls = [],
queueableFn1 = { fn: jasmine.createSpy('fn1') },
queueableFn2 = { fn: jasmine.createSpy('fn2') },
queueRunner = new jasmineUnderTest.QueueRunner({
queueRunner = new privateUnderTest.QueueRunner({
queueableFns: [queueableFn1, queueableFn2]
});
queueableFn1.fn.and.callFake(function() {
@@ -28,14 +44,14 @@ describe('QueueRunner', function() {
done();
}
},
queueRunner = new jasmineUnderTest.QueueRunner({
queueRunner = new privateUnderTest.QueueRunner({
queueableFns: [queueableFn1, queueableFn2, queueableFn3]
});
queueRunner.execute();
const context = queueableFn1.fn.calls.first().object;
expect(context).toEqual(new jasmineUnderTest.UserContext());
expect(context).toEqual(new privateUnderTest.UserContext());
expect(queueableFn2.fn.calls.first().object).toBe(context);
expect(asyncContext).toBe(context);
});
@@ -75,7 +91,7 @@ describe('QueueRunner', function() {
setTimeout(done, 100);
}
},
queueRunner = new jasmineUnderTest.QueueRunner({
queueRunner = new privateUnderTest.QueueRunner({
queueableFns: [queueableFn1, queueableFn2, queueableFn3],
onComplete: onComplete
});
@@ -113,7 +129,7 @@ describe('QueueRunner', function() {
},
queueableFn2 = { fn: jasmine.createSpy('fn2') },
failFn = jasmine.createSpy('fail'),
queueRunner = new jasmineUnderTest.QueueRunner({
queueRunner = new privateUnderTest.QueueRunner({
queueableFns: [queueableFn1, queueableFn2],
fail: failFn
});
@@ -141,7 +157,7 @@ describe('QueueRunner', function() {
},
queueableFn2 = { fn: jasmine.createSpy('fn2') },
failFn = jasmine.createSpy('fail'),
queueRunner = new jasmineUnderTest.QueueRunner({
queueRunner = new privateUnderTest.QueueRunner({
queueableFns: [queueableFn1, queueableFn2],
fail: failFn
});
@@ -173,7 +189,7 @@ describe('QueueRunner', function() {
},
queueableFn2 = { fn: jasmine.createSpy('fn2') },
failFn = jasmine.createSpy('fail'),
queueRunner = new jasmineUnderTest.QueueRunner({
queueRunner = new privateUnderTest.QueueRunner({
queueableFns: [queueableFn1, queueableFn2],
fail: failFn,
onComplete: function() {
@@ -185,43 +201,20 @@ describe('QueueRunner', function() {
queueRunner.execute();
});
it('does not log a deprecation', function(done) {
const err = new Error('foo'),
queueableFn1 = {
fn: function() {
return Promise.resolve(err);
}
},
deprecated = jasmine.createSpy('deprecated'),
queueRunner = new jasmineUnderTest.QueueRunner({
queueableFns: [queueableFn1],
deprecated: deprecated,
onComplete: function() {
expect(deprecated).not.toHaveBeenCalled();
done();
}
});
queueRunner.execute();
});
});
describe('and the argument is not an Error', function() {
it('does not log a deprecation or report a failure', function(done) {
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({
queueRunner = new privateUnderTest.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() {
const err = new jasmineUnderTest.StopExecutionError('foo'),
const err = new privateUnderTest.StopExecutionError('foo'),
queueableFn1 = {
fn: function(done) {
setTimeout(function() {
@@ -244,7 +237,7 @@ describe('QueueRunner', function() {
},
queueableFn2 = { fn: jasmine.createSpy('fn2') },
failFn = jasmine.createSpy('fail'),
queueRunner = new jasmineUnderTest.QueueRunner({
queueRunner = new privateUnderTest.QueueRunner({
queueableFns: [queueableFn1, queueableFn2],
fail: failFn
});
@@ -262,12 +255,11 @@ describe('QueueRunner', function() {
it("sets a timeout if requested for asynchronous functions so they don't go on forever", function() {
const timeout = 3,
// eslint-disable-next-line no-unused-vars
beforeFn = { fn: function(done) {}, type: 'before', timeout: timeout },
queueableFn = { fn: jasmine.createSpy('fn'), type: 'queueable' },
onComplete = jasmine.createSpy('onComplete'),
onException = jasmine.createSpy('onException'),
queueRunner = new jasmineUnderTest.QueueRunner({
queueRunner = new privateUnderTest.QueueRunner({
queueableFns: [beforeFn, queueableFn],
onComplete: onComplete,
onException: onException
@@ -295,7 +287,7 @@ describe('QueueRunner', function() {
};
const onComplete = jasmine.createSpy('onComplete');
const onMultipleDone = jasmine.createSpy('onMultipleDone');
const queueRunner = new jasmineUnderTest.QueueRunner({
const queueRunner = new privateUnderTest.QueueRunner({
queueableFns: [queueableFn],
onComplete: onComplete,
onMultipleDone: onMultipleDone
@@ -310,12 +302,11 @@ describe('QueueRunner', function() {
});
it('by default does not set a timeout for asynchronous functions', function() {
// eslint-disable-next-line no-unused-vars
const beforeFn = { fn: function(done) {} },
queueableFn = { fn: jasmine.createSpy('fn') },
onComplete = jasmine.createSpy('onComplete'),
onException = jasmine.createSpy('onException'),
queueRunner = new jasmineUnderTest.QueueRunner({
queueRunner = new privateUnderTest.QueueRunner({
queueableFns: [beforeFn, queueableFn],
onComplete: onComplete,
onException: onException
@@ -333,14 +324,13 @@ describe('QueueRunner', function() {
it('clears the timeout when an async function throws an exception, to prevent additional exception reporting', function() {
const queueableFn = {
// eslint-disable-next-line no-unused-vars
fn: function(done) {
throw new Error('error!');
}
},
onComplete = jasmine.createSpy('onComplete'),
onException = jasmine.createSpy('onException'),
queueRunner = new jasmineUnderTest.QueueRunner({
queueRunner = new privateUnderTest.QueueRunner({
queueableFns: [queueableFn],
onComplete: onComplete,
onException: onException
@@ -363,7 +353,7 @@ describe('QueueRunner', function() {
},
onComplete = jasmine.createSpy('onComplete'),
onException = jasmine.createSpy('onException'),
queueRunner = new jasmineUnderTest.QueueRunner({
queueRunner = new privateUnderTest.QueueRunner({
queueableFns: [queueableFn],
onComplete: onComplete,
onException: onException
@@ -387,7 +377,7 @@ describe('QueueRunner', function() {
},
nextQueueableFn = { fn: jasmine.createSpy('nextFn') },
onMultipleDone = jasmine.createSpy('onMultipleDone'),
queueRunner = new jasmineUnderTest.QueueRunner({
queueRunner = new privateUnderTest.QueueRunner({
queueableFns: [queueableFn, nextQueueableFn],
onMultipleDone: onMultipleDone
});
@@ -406,17 +396,12 @@ describe('QueueRunner', function() {
}
},
nextQueueableFn = { fn: jasmine.createSpy('nextFn') },
deprecated = jasmine.createSpy('deprecated'),
queueRunner = new jasmineUnderTest.QueueRunner({
deprecated: deprecated,
queueRunner = new privateUnderTest.QueueRunner({
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() {
@@ -427,7 +412,7 @@ describe('QueueRunner', function() {
doneReturn = done();
}
};
const queueRunner = new jasmineUnderTest.QueueRunner({
const queueRunner = new privateUnderTest.QueueRunner({
queueableFns: [queueableFn]
});
@@ -437,7 +422,6 @@ describe('QueueRunner', function() {
it('continues running functions when an exception is thrown in async code without timing out', function() {
const queueableFn = {
// eslint-disable-next-line no-unused-vars
fn: function(done) {
throwAsync();
},
@@ -449,7 +433,7 @@ describe('QueueRunner', function() {
pushListener: jasmine.createSpy('pushListener'),
popListener: jasmine.createSpy('popListener')
},
queueRunner = new jasmineUnderTest.QueueRunner({
queueRunner = new privateUnderTest.QueueRunner({
queueableFns: [queueableFn, nextQueueableFn],
onException: onException,
globalErrors: globalErrors
@@ -487,6 +471,31 @@ 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 privateUnderTest.QueueRunner({
queueableFns: [queueableFn],
onException: onException,
globalErrors: globalErrors
});
queueRunner.execute();
expect(onException).toHaveBeenCalledWith('nope');
});
it('handles exceptions thrown while waiting for the stack to clear', function() {
const queueableFn = {
fn: function(done) {
@@ -504,7 +513,7 @@ describe('QueueRunner', function() {
},
clearStack = jasmine.createSpy('clearStack'),
onException = jasmine.createSpy('onException'),
queueRunner = new jasmineUnderTest.QueueRunner({
queueRunner = new privateUnderTest.QueueRunner({
queueableFns: [queueableFn],
globalErrors: globalErrors,
clearStack: clearStack,
@@ -520,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 privateUnderTest.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() {
@@ -560,7 +603,7 @@ describe('QueueRunner', function() {
return p2;
}
},
queueRunner = new jasmineUnderTest.QueueRunner({
queueRunner = new privateUnderTest.QueueRunner({
queueableFns: [queueableFn1, queueableFn2],
onComplete: onComplete
});
@@ -591,7 +634,7 @@ describe('QueueRunner', function() {
},
queueableFn2 = { fn: jasmine.createSpy('fn2') },
onExceptionCallback = jasmine.createSpy('on exception callback'),
queueRunner = new jasmineUnderTest.QueueRunner({
queueRunner = new privateUnderTest.QueueRunner({
queueableFns: [queueableFn1, queueableFn2],
onException: onExceptionCallback
});
@@ -609,13 +652,12 @@ describe('QueueRunner', function() {
it('issues an error if the function also takes a parameter', function() {
const queueableFn = {
// eslint-disable-next-line no-unused-vars
fn: function(done) {
return new StubPromise();
}
},
onException = jasmine.createSpy('onException'),
queueRunner = new jasmineUnderTest.QueueRunner({
queueRunner = new privateUnderTest.QueueRunner({
queueableFns: [queueableFn],
onException: onException
});
@@ -623,19 +665,20 @@ 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() {
// eslint-disable-next-line no-unused-vars
async function fn(done) {}
const onException = jasmine.createSpy('onException'),
queueRunner = new jasmineUnderTest.QueueRunner({
queueRunner = new privateUnderTest.QueueRunner({
queueableFns: [{ fn: fn }],
onException: onException
});
@@ -643,40 +686,29 @@ 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() {
it('passes final errors to exception handlers', function() {
const error = new Error('fake error'),
onExceptionCallback = jasmine.createSpy('on exception callback'),
queueRunner = new jasmineUnderTest.QueueRunner({
queueRunner = new privateUnderTest.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() {
const error = new Error('fake error'),
onExceptionCallback = jasmine.createSpy('on exception callback'),
queueRunner = new jasmineUnderTest.QueueRunner({
onException: onExceptionCallback
});
queueRunner.execute();
queueRunner.handleFinalError(error.message);
expect(onExceptionCallback).toHaveBeenCalledWith(error.message);
});
it('calls exception handlers when an exception is thrown in a fn', function() {
const queueableFn = {
type: 'queueable',
@@ -685,7 +717,7 @@ describe('QueueRunner', function() {
}
},
onExceptionCallback = jasmine.createSpy('on exception callback'),
queueRunner = new jasmineUnderTest.QueueRunner({
queueRunner = new privateUnderTest.QueueRunner({
queueableFns: [queueableFn],
onException: onExceptionCallback
});
@@ -697,13 +729,12 @@ describe('QueueRunner', function() {
it('continues running the functions even after an exception is thrown in an async spec', function() {
const queueableFn = {
// eslint-disable-next-line no-unused-vars
fn: function(done) {
throw new Error('error');
}
},
nextQueueableFn = { fn: jasmine.createSpy('nextFunction') },
queueRunner = new jasmineUnderTest.QueueRunner({
queueRunner = new privateUnderTest.QueueRunner({
queueableFns: [queueableFn, nextQueueableFn]
});
@@ -716,7 +747,7 @@ describe('QueueRunner', function() {
const SkipPolicy = jasmine.createSpy('SkipPolicy ctor');
const queueableFns = [{ fn: () => {} }, { fn: () => {} }];
new jasmineUnderTest.QueueRunner({
new privateUnderTest.QueueRunner({
queueableFns,
SkipPolicy
});
@@ -738,7 +769,7 @@ describe('QueueRunner', function() {
skipPolicy.skipTo.and.callFake(function(lastRanIx) {
return lastRanIx === 0 ? 2 : lastRanIx + 1;
});
const queueRunner = new jasmineUnderTest.QueueRunner({
const queueRunner = new privateUnderTest.QueueRunner({
queueableFns,
SkipPolicy: function() {
return skipPolicy;
@@ -759,7 +790,7 @@ describe('QueueRunner', function() {
it('throws if the skip policy returns the current fn', function() {
const skipPolicy = { skipTo: i => i };
const queueableFns = [{ fn: () => {} }];
const queueRunner = new jasmineUnderTest.QueueRunner({
const queueRunner = new privateUnderTest.QueueRunner({
queueableFns,
SkipPolicy: function() {
return skipPolicy;
@@ -785,17 +816,17 @@ describe('QueueRunner', function() {
type: 'specCleanup'
},
onComplete = jasmine.createSpy('onComplete'),
queueRunner = new jasmineUnderTest.QueueRunner({
queueRunner = new privateUnderTest.QueueRunner({
queueableFns: [queueableFn, nextQueueableFn, cleanupFn],
onComplete: onComplete,
SkipPolicy: jasmineUnderTest.CompleteOnFirstErrorSkipPolicy
SkipPolicy: privateUnderTest.CompleteOnFirstErrorSkipPolicy
});
queueRunner.execute();
expect(nextQueueableFn.fn).not.toHaveBeenCalled();
expect(cleanupFn.fn).toHaveBeenCalled();
expect(onComplete).toHaveBeenCalledWith(
jasmine.any(jasmineUnderTest.StopExecutionError)
jasmine.any(privateUnderTest.StopExecutionError)
);
});
@@ -811,9 +842,9 @@ describe('QueueRunner', function() {
fn: jasmine.createSpy('cleanupFn2'),
type: 'afterEach'
},
queueRunner = new jasmineUnderTest.QueueRunner({
queueRunner = new privateUnderTest.QueueRunner({
queueableFns: [queueableFn, cleanupFn1, cleanupFn2],
SkipPolicy: jasmineUnderTest.CompleteOnFirstErrorSkipPolicy
SkipPolicy: privateUnderTest.CompleteOnFirstErrorSkipPolicy
});
queueRunner.execute();
@@ -842,7 +873,7 @@ describe('QueueRunner', function() {
fn: jasmine.createSpy('cleanup'),
type: 'specCleanup'
};
const queueRunner = new jasmineUnderTest.QueueRunner({
const queueRunner = new privateUnderTest.QueueRunner({
globalErrors: {
pushListener: function(f) {
errorListeners.push(f);
@@ -852,7 +883,7 @@ describe('QueueRunner', function() {
}
},
queueableFns: [queueableFn, nextQueueableFn, cleanupFn],
SkipPolicy: jasmineUnderTest.CompleteOnFirstErrorSkipPolicy
SkipPolicy: privateUnderTest.CompleteOnFirstErrorSkipPolicy
});
queueRunner.execute();
@@ -871,9 +902,9 @@ describe('QueueRunner', function() {
},
nextQueueableFn = { fn: jasmine.createSpy('nextFunction') },
cleanupFn = { fn: jasmine.createSpy('cleanup'), type: 'specCleanup' },
queueRunner = new jasmineUnderTest.QueueRunner({
queueRunner = new privateUnderTest.QueueRunner({
queueableFns: [queueableFn, nextQueueableFn, cleanupFn],
SkipPolicy: jasmineUnderTest.CompleteOnFirstErrorSkipPolicy
SkipPolicy: privateUnderTest.CompleteOnFirstErrorSkipPolicy
});
queueRunner.execute();
@@ -893,9 +924,9 @@ describe('QueueRunner', function() {
fn: jasmine.createSpy('cleanup'),
type: 'specCleanup'
},
queueRunner = new jasmineUnderTest.QueueRunner({
queueRunner = new privateUnderTest.QueueRunner({
queueableFns: [queueableFn, nextQueueableFn, cleanupFn],
SkipPolicy: jasmineUnderTest.CompleteOnFirstErrorSkipPolicy
SkipPolicy: privateUnderTest.CompleteOnFirstErrorSkipPolicy
});
queueRunner.execute();
@@ -909,7 +940,7 @@ describe('QueueRunner', function() {
it('calls a provided complete callback when done', function() {
const queueableFn = { fn: jasmine.createSpy('fn') },
completeCallback = jasmine.createSpy('completeCallback'),
queueRunner = new jasmineUnderTest.QueueRunner({
queueRunner = new privateUnderTest.QueueRunner({
queueableFns: [queueableFn],
onComplete: completeCallback
});
@@ -937,7 +968,7 @@ describe('QueueRunner', function() {
afterFn = { fn: jasmine.createSpy('afterFn') },
completeCallback = jasmine.createSpy('completeCallback'),
clearStack = jasmine.createSpy('clearStack'),
queueRunner = new jasmineUnderTest.QueueRunner({
queueRunner = new privateUnderTest.QueueRunner({
queueableFns: [asyncFn, afterFn],
clearStack: clearStack,
onComplete: completeCallback
@@ -961,7 +992,7 @@ describe('QueueRunner', function() {
const fn = jasmine.createSpy('fn1');
this.fn = fn;
this.queueRunner = new jasmineUnderTest.QueueRunner({
this.queueRunner = new privateUnderTest.QueueRunner({
queueableFns: [{ fn: fn }]
});
});
@@ -974,7 +1005,7 @@ describe('QueueRunner', function() {
this.queueRunner.execute();
expect(context.constructor).toBe(jasmineUnderTest.UserContext);
expect(context.constructor).toBe(privateUnderTest.UserContext);
});
});
@@ -984,8 +1015,8 @@ describe('QueueRunner', function() {
let context;
this.fn = fn;
this.context = context = new jasmineUnderTest.UserContext();
this.queueRunner = new jasmineUnderTest.QueueRunner({
this.context = context = new privateUnderTest.UserContext();
this.queueRunner = new privateUnderTest.QueueRunner({
queueableFns: [{ fn: fn }],
userContext: context
});

View File

@@ -1,6 +1,6 @@
describe('ReportDispatcher', function() {
it('builds an interface of requested methods', function() {
const dispatcher = new jasmineUnderTest.ReportDispatcher([
const dispatcher = new privateUnderTest.ReportDispatcher([
'foo',
'bar',
'baz'
@@ -12,21 +12,20 @@ describe('ReportDispatcher', function() {
});
it('dispatches requested methods to added reporters', function() {
const queueRunnerFactory = jasmine.createSpy('queueRunner'),
dispatcher = new jasmineUnderTest.ReportDispatcher(
const runQueue = jasmine.createSpy('runQueue'),
dispatcher = new privateUnderTest.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({ an: 'event' });
expect(queueRunnerFactory).toHaveBeenCalledWith(
expect(runQueue).toHaveBeenCalledWith(
jasmine.objectContaining({
queueableFns: [
{ fn: jasmine.any(Function) },
@@ -36,20 +35,20 @@ describe('ReportDispatcher', function() {
})
);
let 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).toHaveBeenCalledWith({ an: 'event' });
expect(reporter.foo.calls.mostRecent().object).toBe(reporter);
fns[1].fn();
expect(anotherReporter.foo).toHaveBeenCalledWith(123, 456);
expect(anotherReporter.foo).toHaveBeenCalledWith({ an: 'event' });
expect(anotherReporter.foo.calls.mostRecent().object).toBe(anotherReporter);
queueRunnerFactory.calls.reset();
runQueue.calls.reset();
dispatcher.bar('a', 'b', completeCallback);
dispatcher.bar({ another: 'event' });
expect(queueRunnerFactory).toHaveBeenCalledWith(
expect(runQueue).toHaveBeenCalledWith(
jasmine.objectContaining({
queueableFns: [
{ fn: jasmine.any(Function) },
@@ -59,26 +58,56 @@ 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');
expect(reporter.bar).toHaveBeenCalledWith({ another: 'event' });
fns[1].fn();
expect(anotherReporter.bar).toHaveBeenCalledWith('a', 'b');
expect(anotherReporter.bar).toHaveBeenCalledWith({ another: 'event' });
});
it('passes each reporter a separate deep copy of the event', function() {
const runQueue = jasmine.createSpy('runQueue');
const dispatcher = new privateUnderTest.ReportDispatcher(
['foo', 'bar'],
runQueue
);
const reporter = jasmine.createSpyObj('reporter', ['foo']);
const anotherReporter = jasmine.createSpyObj('anotherReporter', ['foo']);
const event = {
child: {
grandchild: 'something'
}
};
dispatcher.addReporter(reporter);
dispatcher.addReporter(anotherReporter);
dispatcher.foo(event);
for (const fn of runQueue.calls.mostRecent().args[0].queueableFns) {
fn.fn();
}
expect(reporter.foo).toHaveBeenCalledWith(event);
expect(anotherReporter.foo).toHaveBeenCalledWith(event);
const receivedEvents = [reporter, anotherReporter].map(function(reporter) {
return reporter.foo.calls.mostRecent().args[0];
});
expect(receivedEvents[0]).not.toBe(event);
expect(receivedEvents[0]).not.toBe(receivedEvents[1]);
expect(receivedEvents[0].child).not.toBe(event.child);
expect(receivedEvents[0].child).not.toBe(receivedEvents[1].child);
});
it("does not dispatch to a reporter if the reporter doesn't accept the method", function() {
const queueRunnerFactory = jasmine.createSpy('queueRunner'),
dispatcher = new jasmineUnderTest.ReportDispatcher(
['foo'],
queueRunnerFactory
),
const runQueue = jasmine.createSpy('runQueue'),
dispatcher = new privateUnderTest.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,93 +115,90 @@ describe('ReportDispatcher', function() {
});
it("allows providing a fallback reporter in case there's no other reporter", function() {
const queueRunnerFactory = jasmine.createSpy('queueRunner'),
dispatcher = new jasmineUnderTest.ReportDispatcher(
const runQueue = jasmine.createSpy('runQueue'),
dispatcher = new privateUnderTest.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({ an: 'event' });
expect(queueRunnerFactory).toHaveBeenCalledWith(
expect(runQueue).toHaveBeenCalledWith(
jasmine.objectContaining({
queueableFns: [{ fn: jasmine.any(Function) }],
isReporter: true
})
);
const 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(reporter.foo).toHaveBeenCalledWith({ an: 'event' });
});
it('does not call fallback reporting methods when another reporter is provided', function() {
const queueRunnerFactory = jasmine.createSpy('queueRunner'),
dispatcher = new jasmineUnderTest.ReportDispatcher(
const runQueue = jasmine.createSpy('runQueue'),
dispatcher = new privateUnderTest.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({ an: 'event' });
expect(queueRunnerFactory).toHaveBeenCalledWith(
expect(runQueue).toHaveBeenCalledWith(
jasmine.objectContaining({
queueableFns: [{ fn: jasmine.any(Function) }],
isReporter: true
})
);
const 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);
expect(reporter.foo).toHaveBeenCalledWith({ an: 'event' });
expect(fallbackReporter.foo).not.toHaveBeenCalled();
});
it('allows registered reporters to be cleared', function() {
const queueRunnerFactory = jasmine.createSpy('queueRunner'),
dispatcher = new jasmineUnderTest.ReportDispatcher(
const runQueue = jasmine.createSpy('runQueue'),
dispatcher = new privateUnderTest.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({ an: 'event' });
expect(runQueue).toHaveBeenCalledWith(
jasmine.objectContaining({
queueableFns: [{ fn: jasmine.any(Function) }],
isReporter: true
})
);
let fns = queueRunnerFactory.calls.mostRecent().args[0].queueableFns;
let fns = runQueue.calls.mostRecent().args[0].queueableFns;
fns[0].fn();
expect(reporter1.foo).toHaveBeenCalledWith(123);
expect(reporter1.foo).toHaveBeenCalledWith({ an: 'event' });
dispatcher.clearReporters();
dispatcher.addReporter(reporter2);
dispatcher.bar(456, completeCallback);
dispatcher.bar({ another: 'event' });
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);
expect(reporter2.bar).toHaveBeenCalledWith({ another: 'event' });
});
});

View File

@@ -38,7 +38,7 @@ describe('RunableResources', function() {
describe('#addCustomMatchers', function() {
it("adds all properties to the current runable's matchers", function() {
const currentRunableId = 1;
const runableResources = new jasmineUnderTest.RunableResources({
const runableResources = new privateUnderTest.RunableResources({
globalErrors: stubGlobalErrors(),
getCurrentRunableId: () => currentRunableId
});
@@ -70,7 +70,7 @@ describe('RunableResources', function() {
describe('#addCustomAsyncMatchers', function() {
it("adds all properties to the current runable's matchers", function() {
const currentRunableId = 1;
const runableResources = new jasmineUnderTest.RunableResources({
const runableResources = new privateUnderTest.RunableResources({
globalErrors: stubGlobalErrors(),
getCurrentRunableId: () => currentRunableId
});
@@ -95,7 +95,7 @@ describe('RunableResources', function() {
describe('#defaultSpyStrategy', function() {
it('returns undefined for a newly initialized resource', function() {
let currentRunableId = 1;
const runableResources = new jasmineUnderTest.RunableResources({
const runableResources = new privateUnderTest.RunableResources({
globalErrors: stubGlobalErrors(),
getCurrentRunableId: () => currentRunableId
});
@@ -106,7 +106,7 @@ describe('RunableResources', function() {
it('returns the value previously set by #setDefaultSpyStrategy', function() {
let currentRunableId = 1;
const runableResources = new jasmineUnderTest.RunableResources({
const runableResources = new privateUnderTest.RunableResources({
globalErrors: stubGlobalErrors(),
getCurrentRunableId: () => currentRunableId
});
@@ -119,7 +119,7 @@ describe('RunableResources', function() {
it('is per-runable', function() {
let currentRunableId = 1;
const runableResources = new jasmineUnderTest.RunableResources({
const runableResources = new privateUnderTest.RunableResources({
globalErrors: stubGlobalErrors(),
getCurrentRunableId: () => currentRunableId
});
@@ -132,7 +132,7 @@ describe('RunableResources', function() {
});
it('does not require a current runable', function() {
const runableResources = new jasmineUnderTest.RunableResources({
const runableResources = new privateUnderTest.RunableResources({
globalErrors: stubGlobalErrors(),
getCurrentRunableId: () => null
});
@@ -141,7 +141,7 @@ describe('RunableResources', function() {
it("inherits the parent runable's value", function() {
let currentRunableId = 1;
const runableResources = new jasmineUnderTest.RunableResources({
const runableResources = new privateUnderTest.RunableResources({
globalErrors: stubGlobalErrors(),
getCurrentRunableId: () => currentRunableId
});
@@ -157,7 +157,7 @@ describe('RunableResources', function() {
describe('#setDefaultSpyStrategy', function() {
it('throws a user-facing error when there is no current runable', function() {
const runableResources = new jasmineUnderTest.RunableResources({
const runableResources = new privateUnderTest.RunableResources({
globalErrors: stubGlobalErrors(),
getCurrentRunableId: () => null
});
@@ -171,21 +171,21 @@ describe('RunableResources', function() {
describe('#makePrettyPrinter', function() {
it('returns a pretty printer configured with the current customObjectFormatters', function() {
const runableResources = new jasmineUnderTest.RunableResources({
const runableResources = new privateUnderTest.RunableResources({
globalErrors: stubGlobalErrors(),
getCurrentRunableId: () => 1
});
runableResources.initForRunable(1);
function cof() {}
runableResources.customObjectFormatters().push(cof);
spyOn(jasmineUnderTest, 'makePrettyPrinter').and.callThrough();
spyOn(privateUnderTest, 'makePrettyPrinter').and.callThrough();
const pp = runableResources.makePrettyPrinter();
expect(jasmineUnderTest.makePrettyPrinter).toHaveBeenCalledOnceWith([
expect(privateUnderTest.makePrettyPrinter).toHaveBeenCalledOnceWith([
cof
]);
expect(pp).toBe(
jasmineUnderTest.makePrettyPrinter.calls.first().returnValue
privateUnderTest.makePrettyPrinter.calls.first().returnValue
);
});
});
@@ -193,7 +193,7 @@ describe('RunableResources', function() {
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({
const runableResources = new privateUnderTest.RunableResources({
globalErrors: stubGlobalErrors(),
getCurrentRunableId: () => 1
});
@@ -204,26 +204,26 @@ describe('RunableResources', function() {
runableResources.customEqualityTesters().push(ceq);
const expectedPP = {};
const expectedMatchersUtil = {};
spyOn(jasmineUnderTest, 'makePrettyPrinter').and.returnValue(
spyOn(privateUnderTest, 'makePrettyPrinter').and.returnValue(
expectedPP
);
spyOn(jasmineUnderTest, 'MatchersUtil').and.returnValue(
spyOn(privateUnderTest, 'MatchersUtil').and.returnValue(
expectedMatchersUtil
);
const matchersUtil = runableResources.makeMatchersUtil();
expect(matchersUtil).toBe(expectedMatchersUtil);
expect(jasmineUnderTest.makePrettyPrinter).toHaveBeenCalledOnceWith([
expect(privateUnderTest.makePrettyPrinter).toHaveBeenCalledOnceWith([
cof
]);
// We need === equality on the pp passed to MatchersUtil
expect(jasmineUnderTest.MatchersUtil).toHaveBeenCalledOnceWith(
expect(privateUnderTest.MatchersUtil).toHaveBeenCalledOnceWith(
jasmine.objectContaining({
customTesters: [ceq]
})
);
expect(jasmineUnderTest.MatchersUtil.calls.argsFor(0)[0].pp).toBe(
expect(privateUnderTest.MatchersUtil.calls.argsFor(0)[0].pp).toBe(
expectedPP
);
});
@@ -231,12 +231,12 @@ describe('RunableResources', function() {
describe('When there is no current runable', function() {
it('returns a MatchersUtil configured with defaults', function() {
const runableResources = new jasmineUnderTest.RunableResources({
const runableResources = new privateUnderTest.RunableResources({
globalErrors: stubGlobalErrors(),
getCurrentRunableId: () => null
});
const expectedMatchersUtil = {};
spyOn(jasmineUnderTest, 'MatchersUtil').and.returnValue(
spyOn(privateUnderTest, 'MatchersUtil').and.returnValue(
expectedMatchersUtil
);
@@ -244,12 +244,12 @@ describe('RunableResources', function() {
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(privateUnderTest.MatchersUtil).toHaveBeenCalledTimes(1);
expect(privateUnderTest.MatchersUtil.calls.argsFor(0)[0].pp).toBe(
privateUnderTest.basicPrettyPrinter
);
expect(
jasmineUnderTest.MatchersUtil.calls.argsFor(0)[0].customTesters
privateUnderTest.MatchersUtil.calls.argsFor(0)[0].customTesters
).toBeUndefined();
});
});
@@ -258,11 +258,11 @@ describe('RunableResources', function() {
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({
const runableResources = new privateUnderTest.RunableResources({
globalErrors: stubGlobalErrors(),
getCurrentRunableId: () => null
});
spyOn(jasmineUnderTest, 'Spy');
spyOn(privateUnderTest, 'Spy');
const matchersUtil = {};
spyOn(runableResources, 'makeMatchersUtil').and.returnValue(
matchersUtil
@@ -270,7 +270,7 @@ describe('RunableResources', function() {
runableResources.spyFactory.createSpy('foo');
expect(jasmineUnderTest.Spy).toHaveBeenCalledWith(
expect(privateUnderTest.Spy).toHaveBeenCalledWith(
'foo',
is(matchersUtil),
jasmine.objectContaining({
@@ -283,7 +283,7 @@ describe('RunableResources', function() {
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({
const runableResources = new privateUnderTest.RunableResources({
globalErrors: stubGlobalErrors(),
getCurrentRunableId: () => 1
});
@@ -292,7 +292,7 @@ describe('RunableResources', function() {
function defaultStrategy() {}
runableResources.customSpyStrategies().foo = customStrategy;
runableResources.setDefaultSpyStrategy(defaultStrategy);
spyOn(jasmineUnderTest, 'Spy');
spyOn(privateUnderTest, 'Spy');
const matchersUtil = {};
spyOn(runableResources, 'makeMatchersUtil').and.returnValue(
matchersUtil
@@ -300,7 +300,7 @@ describe('RunableResources', function() {
runableResources.spyFactory.createSpy('foo');
expect(jasmineUnderTest.Spy).toHaveBeenCalledWith(
expect(privateUnderTest.Spy).toHaveBeenCalledWith(
'foo',
is(matchersUtil),
jasmine.objectContaining({
@@ -325,7 +325,7 @@ describe('RunableResources', function() {
describe('.spyRegistry', function() {
it("writes to the current runable's spies", function() {
const runableResources = new jasmineUnderTest.RunableResources({
const runableResources = new privateUnderTest.RunableResources({
globalErrors: stubGlobalErrors(),
getCurrentRunableId: () => 1
});
@@ -348,7 +348,7 @@ describe('RunableResources', function() {
describe('#clearForRunable', function() {
it('removes resources for the specified runable', function() {
const runableResources = new jasmineUnderTest.RunableResources({
const runableResources = new privateUnderTest.RunableResources({
globalErrors: stubGlobalErrors(),
getCurrentRunableId: () => 1
});
@@ -363,7 +363,7 @@ describe('RunableResources', function() {
});
it('clears spies', function() {
const runableResources = new jasmineUnderTest.RunableResources({
const runableResources = new privateUnderTest.RunableResources({
globalErrors: stubGlobalErrors(),
getCurrentRunableId: () => 1
});
@@ -381,7 +381,7 @@ describe('RunableResources', function() {
const globalErrors = jasmine.createSpyObj('globalErrors', [
'removeOverrideListener'
]);
const runableResources = new jasmineUnderTest.RunableResources({
const runableResources = new privateUnderTest.RunableResources({
getCurrentRunableId: () => 1,
globalErrors
});
@@ -392,7 +392,7 @@ describe('RunableResources', function() {
});
it('does not remove resources for other runables', function() {
const runableResources = new jasmineUnderTest.RunableResources({
const runableResources = new privateUnderTest.RunableResources({
globalErrors: stubGlobalErrors(),
getCurrentRunableId: () => 1
});
@@ -411,7 +411,7 @@ describe('RunableResources', function() {
) {
it('is initially empty', function() {
const currentRunableId = 1;
const runableResources = new jasmineUnderTest.RunableResources({
const runableResources = new privateUnderTest.RunableResources({
globalErrors: stubGlobalErrors(),
getCurrentRunableId: () => currentRunableId
});
@@ -422,7 +422,7 @@ describe('RunableResources', function() {
it('is mutable', function() {
const currentRunableId = 1;
const runableResources = new jasmineUnderTest.RunableResources({
const runableResources = new privateUnderTest.RunableResources({
globalErrors: stubGlobalErrors(),
getCurrentRunableId: () => currentRunableId
});
@@ -434,7 +434,7 @@ describe('RunableResources', function() {
it('is per-runable', function() {
let currentRunableId = 1;
const runableResources = new jasmineUnderTest.RunableResources({
const runableResources = new privateUnderTest.RunableResources({
globalErrors: stubGlobalErrors(),
getCurrentRunableId: () => currentRunableId
});
@@ -446,7 +446,7 @@ describe('RunableResources', function() {
});
it('throws a user-facing error when there is no current runable', function() {
const runableResources = new jasmineUnderTest.RunableResources({
const runableResources = new privateUnderTest.RunableResources({
globalErrors: stubGlobalErrors(),
getCurrentRunableId: () => null
});
@@ -458,7 +458,7 @@ describe('RunableResources', function() {
if (inherits) {
it('inherits from the parent runable', function() {
let currentRunableId = 1;
const runableResources = new jasmineUnderTest.RunableResources({
const runableResources = new privateUnderTest.RunableResources({
globalErrors: stubGlobalErrors(),
getCurrentRunableId: () => currentRunableId
});
@@ -480,7 +480,7 @@ describe('RunableResources', function() {
function behavesLikeAPerRunableMutableObject(methodName, errorMsg) {
it('is initially empty', function() {
const currentRunableId = 1;
const runableResources = new jasmineUnderTest.RunableResources({
const runableResources = new privateUnderTest.RunableResources({
globalErrors: stubGlobalErrors(),
getCurrentRunableId: () => currentRunableId
});
@@ -491,7 +491,7 @@ describe('RunableResources', function() {
it('is mutable', function() {
const currentRunableId = 1;
const runableResources = new jasmineUnderTest.RunableResources({
const runableResources = new privateUnderTest.RunableResources({
globalErrors: stubGlobalErrors(),
getCurrentRunableId: () => currentRunableId
});
@@ -503,7 +503,7 @@ describe('RunableResources', function() {
it('is per-runable', function() {
let currentRunableId = 1;
const runableResources = new jasmineUnderTest.RunableResources({
const runableResources = new privateUnderTest.RunableResources({
globalErrors: stubGlobalErrors(),
getCurrentRunableId: () => currentRunableId
});
@@ -515,7 +515,7 @@ describe('RunableResources', function() {
});
it('throws a user-facing error when there is no current runable', function() {
const runableResources = new jasmineUnderTest.RunableResources({
const runableResources = new privateUnderTest.RunableResources({
globalErrors: stubGlobalErrors(),
getCurrentRunableId: () => null
});
@@ -526,7 +526,7 @@ describe('RunableResources', function() {
it('inherits from the parent runable', function() {
let currentRunableId = 1;
const runableResources = new jasmineUnderTest.RunableResources({
const runableResources = new privateUnderTest.RunableResources({
globalErrors: stubGlobalErrors(),
getCurrentRunableId: () => currentRunableId
});

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

@@ -0,0 +1,499 @@
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',
privateUnderTest.reporterEvents
);
for (const k of privateUnderTest.reporterEvents) {
reportDispatcher[k].and.returnValue(Promise.resolve());
}
// Reasonable defaults, may be overridden in some cases
failSpecWithNoExpectations = false;
detectLateRejectionHandling = false;
spyOn(privateUnderTest.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: privateUnderTest.TreeProcessor,
runableResources: {
initForRunable: () => {},
clearForRunable: () => {}
},
reportDispatcher,
globalErrors,
runQueue
};
return new privateUnderTest.Runner({
...defaultOptions,
topSuite
});
}
function arrayNotContaining(item) {
return {
asymmetricMatch(other, matchersUtil) {
if (!jasmine.private.isArray(other)) {
return false;
}
for (const x of other) {
if (matchersUtil.equals(x, item)) {
return false;
}
}
return true;
}
};
}
// Precondition: privateUnderTest.TreeRunner.prototype._executeSpec is a spy
function verifyAndFinishSpec(spec, queueableFn, shouldBeExcluded) {
const ex = privateUnderTest.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: privateUnderTest.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: privateUnderTest.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: privateUnderTest.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: privateUnderTest.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(
privateUnderTest.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(
privateUnderTest.TreeRunner.prototype._executeSpec
).not.toHaveBeenCalledWith(specs[0], jasmine.anything());
expect(
privateUnderTest.TreeRunner.prototype._executeSpec
).toHaveBeenCalledWith(specs[1], 'done');
queueableFns[1].fn('done');
expect(
privateUnderTest.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(
privateUnderTest.TreeRunner.prototype._executeSpec
).not.toHaveBeenCalledWith(nonSpecified, jasmine.anything());
expect(
privateUnderTest.TreeRunner.prototype._executeSpec
).toHaveBeenCalledWith(specified, 'done');
queueableFns[1].fn('done');
expect(
privateUnderTest.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(
privateUnderTest.TreeRunner.prototype._executeSpec
).toHaveBeenCalledWith(nonSpecifiedSpec, 'done');
queueableFns[1].fn('done');
expect(
privateUnderTest.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(
privateUnderTest.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(
privateUnderTest.TreeRunner.prototype._executeSpec
).toHaveBeenCalledWith(spec2, 'done');
childFns[2].fn('done');
expect(
privateUnderTest.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(
privateUnderTest.TreeRunner.prototype._executeSpec
).toHaveBeenCalledWith(specs[i], 'done');
}
await expectAsync(promise).toBePending();
});
});
});

View File

@@ -2,7 +2,7 @@ describe('SkipAfterBeforeAllErrorPolicy', function() {
describe('#skipTo', function() {
describe('When nothing has errored', function() {
it('does not skip anything', function() {
const policy = new jasmineUnderTest.SkipAfterBeforeAllErrorPolicy(
const policy = new privateUnderTest.SkipAfterBeforeAllErrorPolicy(
arrayOfArbitraryFns(4)
);
@@ -15,7 +15,7 @@ describe('SkipAfterBeforeAllErrorPolicy', function() {
describe('When anything but a beforeAll has errored', function() {
it('does not skip anything', function() {
const policy = new jasmineUnderTest.SkipAfterBeforeAllErrorPolicy(
const policy = new privateUnderTest.SkipAfterBeforeAllErrorPolicy(
arrayOfArbitraryFns(4)
);
@@ -40,7 +40,7 @@ describe('SkipAfterBeforeAllErrorPolicy', function() {
{ type: 'afterAll', fn: () => {} },
{ type: 'afterAll', fn: () => {} }
];
const policy = new jasmineUnderTest.SkipAfterBeforeAllErrorPolicy(fns);
const policy = new privateUnderTest.SkipAfterBeforeAllErrorPolicy(fns);
policy.fnErrored(0);
expect(policy.skipTo(0)).toEqual(3);
@@ -54,7 +54,7 @@ describe('SkipAfterBeforeAllErrorPolicy', function() {
it("sets the suite's hadBeforeAllFailure property to true", function() {
const suite = {};
const fns = [{ type: 'beforeAll', fn: () => {}, suite }];
const policy = new jasmineUnderTest.SkipAfterBeforeAllErrorPolicy(fns);
const policy = new privateUnderTest.SkipAfterBeforeAllErrorPolicy(fns);
policy.fnErrored(0);
@@ -65,7 +65,7 @@ describe('SkipAfterBeforeAllErrorPolicy', function() {
describe('When the fn is not a beforeAll', function() {
it('does not try to access the suite, which is probably not there', function() {
const fns = [{ fn: () => {} /* no suite */ }];
const policy = new jasmineUnderTest.SkipAfterBeforeAllErrorPolicy(fns);
const policy = new privateUnderTest.SkipAfterBeforeAllErrorPolicy(fns);
expect(() => policy.fnErrored(0)).not.toThrow();
});

File diff suppressed because it is too large Load Diff

View File

@@ -1,11 +1,11 @@
describe('SpyRegistry', function() {
function createSpy(name, originalFn) {
return jasmineUnderTest.Spy(name, originalFn);
return privateUnderTest.Spy(name, originalFn);
}
describe('#spyOn', function() {
it('checks for the existence of the object', function() {
const spyRegistry = new jasmineUnderTest.SpyRegistry({
const spyRegistry = new privateUnderTest.SpyRegistry({
createSpy: createSpy
});
expect(function() {
@@ -14,53 +14,53 @@ describe('SpyRegistry', function() {
});
it('checks that a method name was passed', function() {
const spyRegistry = new jasmineUnderTest.SpyRegistry(),
subject = {};
const spyRegistry = new privateUnderTest.SpyRegistry(),
target = {};
expect(function() {
spyRegistry.spyOn(subject);
spyRegistry.spyOn(target);
}).toThrowError(/No method name supplied/);
});
it('checks that the object is not `null`', function() {
const spyRegistry = new jasmineUnderTest.SpyRegistry();
const spyRegistry = new privateUnderTest.SpyRegistry();
expect(function() {
spyRegistry.spyOn(null, 'pants');
}).toThrowError(/could not find an object/);
});
it('checks that the method name is not `null`', function() {
const spyRegistry = new jasmineUnderTest.SpyRegistry(),
subject = {};
const spyRegistry = new privateUnderTest.SpyRegistry(),
target = {};
expect(function() {
spyRegistry.spyOn(subject, null);
spyRegistry.spyOn(target, null);
}).toThrowError(/No method name supplied/);
});
it('checks for the existence of the method', function() {
const spyRegistry = new jasmineUnderTest.SpyRegistry(),
subject = {};
const spyRegistry = new privateUnderTest.SpyRegistry(),
target = {};
expect(function() {
spyRegistry.spyOn(subject, 'pants');
spyRegistry.spyOn(target, 'pants');
}).toThrowError(/method does not exist/);
});
it('checks if it has already been spied upon', function() {
const spies = [],
spyRegistry = new jasmineUnderTest.SpyRegistry({
spyRegistry = new privateUnderTest.SpyRegistry({
currentSpies: function() {
return spies;
},
createSpy: createSpy
}),
subject = { spiedFunc: function() {} };
target = { spiedFunc: function() {} };
spyRegistry.spyOn(subject, 'spiedFunc');
spyRegistry.spyOn(target, 'spiedFunc');
expect(function() {
spyRegistry.spyOn(subject, 'spiedFunc');
spyRegistry.spyOn(target, 'spiedFunc');
}).toThrowError(/has already been spied upon/);
});
@@ -78,72 +78,108 @@ describe('SpyRegistry', function() {
});
const spies = [],
spyRegistry = new jasmineUnderTest.SpyRegistry({
spyRegistry = new privateUnderTest.SpyRegistry({
currentSpies: function() {
return spies;
}
}),
subject = { spiedFunc: scope.myFunc };
target = { spiedFunc: scope.myFunc };
expect(function() {
spyRegistry.spyOn(scope, 'myFunc');
}).toThrowError(/is not declared writable or has no setter/);
expect(function() {
spyRegistry.spyOn(subject, 'spiedFunc');
spyRegistry.spyOn(target, 'spiedFunc');
}).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 privateUnderTest.SpyRegistry({
createSpy: createSpy
});
expect(function() {
spyRegistry.spyOn(scope, 'myFunc');
}).toThrowError(
"<spyOn> : Can't spy on myFunc because assigning to it had no effect"
);
});
it('overrides the method on the object and returns the spy', function() {
const originalFunctionWasCalled = false,
spyRegistry = new jasmineUnderTest.SpyRegistry({
createSpy: createSpy
}),
subject = {
spiedFunc: function() {
originalFunctionWasCalled = true;
}
};
let originalFunctionWasCalled = false;
const spyRegistry = new privateUnderTest.SpyRegistry({
createSpy: createSpy
});
const target = {
spiedFunc: function() {
originalFunctionWasCalled = true;
}
};
const spy = spyRegistry.spyOn(subject, 'spiedFunc');
const spy = spyRegistry.spyOn(target, 'spiedFunc');
expect(subject.spiedFunc).toEqual(spy);
subject.spiedFunc();
expect(target.spiedFunc).toEqual(spy);
target.spiedFunc();
expect(originalFunctionWasCalled).toBe(false);
});
it('throws if the method is a mock clock method', function() {
const spyRegistry = new privateUnderTest.SpyRegistry({
createSpy: createSpy
});
const target = { spiedFunc: function() {} };
target.spiedFunc[privateUnderTest.Clock.IsMockClockTimingFn] = true;
expect(function() {
spyRegistry.spyOn(target, 'spiedFunc');
}).toThrowError("Mock clock timing functions can't be spied on");
});
});
describe('#spyOnProperty', function() {
it('checks for the existence of the object', function() {
const spyRegistry = new jasmineUnderTest.SpyRegistry();
const spyRegistry = new privateUnderTest.SpyRegistry();
expect(function() {
spyRegistry.spyOnProperty(void 0, 'pants');
}).toThrowError(/could not find an object/);
});
it('checks that a property name was passed', function() {
const spyRegistry = new jasmineUnderTest.SpyRegistry(),
subject = {};
const spyRegistry = new privateUnderTest.SpyRegistry(),
target = {};
expect(function() {
spyRegistry.spyOnProperty(subject);
spyRegistry.spyOnProperty(target);
}).toThrowError(/No property name supplied/);
});
it('checks for the existence of the method', function() {
const spyRegistry = new jasmineUnderTest.SpyRegistry(),
subject = {};
const spyRegistry = new privateUnderTest.SpyRegistry(),
target = {};
expect(function() {
spyRegistry.spyOnProperty(subject, 'pants');
spyRegistry.spyOnProperty(target, 'pants');
}).toThrowError(/property does not exist/);
});
it('checks for the existence of access type', function() {
const spyRegistry = new jasmineUnderTest.SpyRegistry(),
subject = {};
const spyRegistry = new privateUnderTest.SpyRegistry(),
target = {};
Object.defineProperty(subject, 'pants', {
Object.defineProperty(target, 'pants', {
get: function() {
return 1;
},
@@ -151,65 +187,65 @@ describe('SpyRegistry', function() {
});
expect(function() {
spyRegistry.spyOnProperty(subject, 'pants', 'set');
spyRegistry.spyOnProperty(target, 'pants', 'set');
}).toThrowError(/does not have access type/);
});
it('checks if it can be spied upon', function() {
const subject = {};
const target = {};
Object.defineProperty(subject, 'myProp', {
Object.defineProperty(target, 'myProp', {
get: function() {}
});
Object.defineProperty(subject, 'spiedProp', {
Object.defineProperty(target, 'spiedProp', {
get: function() {},
configurable: true
});
const spyRegistry = new jasmineUnderTest.SpyRegistry();
const spyRegistry = new privateUnderTest.SpyRegistry();
expect(function() {
spyRegistry.spyOnProperty(subject, 'myProp');
spyRegistry.spyOnProperty(target, 'myProp');
}).toThrowError(/is not declared configurable/);
expect(function() {
spyRegistry.spyOnProperty(subject, 'spiedProp');
spyRegistry.spyOnProperty(target, 'spiedProp');
}).not.toThrowError(/is not declared configurable/);
});
it('overrides the property getter on the object and returns the spy', function() {
const spyRegistry = new jasmineUnderTest.SpyRegistry({
const spyRegistry = new privateUnderTest.SpyRegistry({
createSpy: createSpy
}),
subject = {},
target = {},
returnValue = 1;
Object.defineProperty(subject, 'spiedProperty', {
Object.defineProperty(target, 'spiedProperty', {
get: function() {
return returnValue;
},
configurable: true
});
expect(subject.spiedProperty).toEqual(returnValue);
expect(target.spiedProperty).toEqual(returnValue);
const spy = spyRegistry.spyOnProperty(subject, 'spiedProperty');
const getter = Object.getOwnPropertyDescriptor(subject, 'spiedProperty')
const spy = spyRegistry.spyOnProperty(target, 'spiedProperty');
const getter = Object.getOwnPropertyDescriptor(target, 'spiedProperty')
.get;
expect(getter).toEqual(spy);
expect(subject.spiedProperty).toBeUndefined();
expect(target.spiedProperty).toBeUndefined();
});
it('overrides the property setter on the object and returns the spy', function() {
const spyRegistry = new jasmineUnderTest.SpyRegistry({
const spyRegistry = new privateUnderTest.SpyRegistry({
createSpy: createSpy
}),
subject = {},
target = {},
returnValue = 1;
Object.defineProperty(subject, 'spiedProperty', {
Object.defineProperty(target, 'spiedProperty', {
get: function() {
return returnValue;
},
@@ -217,53 +253,53 @@ describe('SpyRegistry', function() {
configurable: true
});
const spy = spyRegistry.spyOnProperty(subject, 'spiedProperty', 'set');
const setter = Object.getOwnPropertyDescriptor(subject, 'spiedProperty')
const spy = spyRegistry.spyOnProperty(target, 'spiedProperty', 'set');
const setter = Object.getOwnPropertyDescriptor(target, 'spiedProperty')
.set;
expect(subject.spiedProperty).toEqual(returnValue);
expect(target.spiedProperty).toEqual(returnValue);
expect(setter).toEqual(spy);
});
describe('when the property is already spied upon', function() {
it('throws an error if respy is not allowed', function() {
const spyRegistry = new jasmineUnderTest.SpyRegistry({
const spyRegistry = new privateUnderTest.SpyRegistry({
createSpy: createSpy
}),
subject = {};
target = {};
Object.defineProperty(subject, 'spiedProp', {
Object.defineProperty(target, 'spiedProp', {
get: function() {
return 1;
},
configurable: true
});
spyRegistry.spyOnProperty(subject, 'spiedProp');
spyRegistry.spyOnProperty(target, 'spiedProp');
expect(function() {
spyRegistry.spyOnProperty(subject, 'spiedProp');
spyRegistry.spyOnProperty(target, 'spiedProp');
}).toThrowError(/spiedProp#get has already been spied upon/);
});
it('returns the original spy if respy is allowed', function() {
const spyRegistry = new jasmineUnderTest.SpyRegistry({
const spyRegistry = new privateUnderTest.SpyRegistry({
createSpy: createSpy
}),
subject = {};
target = {};
spyRegistry.allowRespy(true);
Object.defineProperty(subject, 'spiedProp', {
Object.defineProperty(target, 'spiedProp', {
get: function() {
return 1;
},
configurable: true
});
const originalSpy = spyRegistry.spyOnProperty(subject, 'spiedProp');
const originalSpy = spyRegistry.spyOnProperty(target, 'spiedProp');
expect(spyRegistry.spyOnProperty(subject, 'spiedProp')).toBe(
expect(spyRegistry.spyOnProperty(target, 'spiedProp')).toBe(
originalSpy
);
});
@@ -272,14 +308,14 @@ describe('SpyRegistry', function() {
describe('#spyOnAllFunctions', function() {
it('checks for the existence of the object', function() {
const spyRegistry = new jasmineUnderTest.SpyRegistry();
const spyRegistry = new privateUnderTest.SpyRegistry();
expect(function() {
spyRegistry.spyOnAllFunctions(void 0);
}).toThrowError(/spyOnAllFunctions could not find an object to spy upon/);
});
it('overrides all writable and configurable functions of the object and its parents', function() {
const spyRegistry = new jasmineUnderTest.SpyRegistry({
const spyRegistry = new privateUnderTest.SpyRegistry({
createSpy: function() {
return 'I am a spy';
}
@@ -298,21 +334,21 @@ describe('SpyRegistry', function() {
const parent = {
parentSpied1: noop1
};
const subject = Object.create(parent);
Object.defineProperty(subject, 'spied1', {
const target = Object.create(parent);
Object.defineProperty(target, 'spied1', {
value: noop1,
writable: true,
configurable: true,
enumerable: true
});
Object.defineProperty(subject, 'spied2', {
Object.defineProperty(target, 'spied2', {
value: noop2,
writable: true,
configurable: true,
enumerable: true
});
let _spied3 = noop3;
Object.defineProperty(subject, 'spied3', {
Object.defineProperty(target, 'spied3', {
configurable: true,
set: function(val) {
_spied3 = val;
@@ -322,20 +358,20 @@ describe('SpyRegistry', function() {
},
enumerable: true
});
subject.spied4 = noop4;
Object.defineProperty(subject, 'notSpied2', {
target.spied4 = noop4;
Object.defineProperty(target, 'notSpied2', {
value: noop2,
writable: false,
configurable: true,
enumerable: true
});
Object.defineProperty(subject, 'notSpied3', {
Object.defineProperty(target, 'notSpied3', {
value: noop3,
writable: true,
configurable: false,
enumerable: true
});
Object.defineProperty(subject, 'notSpied4', {
Object.defineProperty(target, 'notSpied4', {
configurable: false,
set: function() {
/**/
@@ -345,31 +381,31 @@ describe('SpyRegistry', function() {
},
enumerable: true
});
Object.defineProperty(subject, 'notSpied5', {
Object.defineProperty(target, 'notSpied5', {
value: noop5,
writable: true,
configurable: true,
enumerable: false
});
subject.notSpied6 = 6;
target.notSpied6 = 6;
const spiedObject = spyRegistry.spyOnAllFunctions(subject);
const spiedObject = spyRegistry.spyOnAllFunctions(target);
expect(subject.parentSpied1).toBe('I am a spy');
expect(subject.notSpied2).toBe(noop2);
expect(subject.notSpied3).toBe(noop3);
expect(subject.notSpied4).toBe(noop4);
expect(subject.notSpied5).toBe(noop5);
expect(subject.notSpied6).toBe(6);
expect(subject.spied1).toBe('I am a spy');
expect(subject.spied2).toBe('I am a spy');
expect(subject.spied3).toBe('I am a spy');
expect(subject.spied4).toBe('I am a spy');
expect(spiedObject).toBe(subject);
expect(target.parentSpied1).toBe('I am a spy');
expect(target.notSpied2).toBe(noop2);
expect(target.notSpied3).toBe(noop3);
expect(target.notSpied4).toBe(noop4);
expect(target.notSpied5).toBe(noop5);
expect(target.notSpied6).toBe(6);
expect(target.spied1).toBe('I am a spy');
expect(target.spied2).toBe('I am a spy');
expect(target.spied3).toBe('I am a spy');
expect(target.spied4).toBe('I am a spy');
expect(spiedObject).toBe(target);
});
it('overrides prototype methods on the object', function() {
const spyRegistry = new jasmineUnderTest.SpyRegistry({
const spyRegistry = new privateUnderTest.SpyRegistry({
createSpy: function() {
return 'I am a spy';
}
@@ -383,145 +419,145 @@ describe('SpyRegistry', function() {
};
MyClass.prototype.spied2 = noop2;
const subject = new MyClass();
spyRegistry.spyOnAllFunctions(subject);
const target = new MyClass();
spyRegistry.spyOnAllFunctions(target);
expect(subject.spied1).toBe('I am a spy');
expect(subject.spied2).toBe('I am a spy');
expect(target.spied1).toBe('I am a spy');
expect(target.spied2).toBe('I am a spy');
expect(MyClass.prototype.spied2).toBe(noop2);
});
it('does not override non-enumerable properties (like Object.prototype methods)', function() {
const spyRegistry = new jasmineUnderTest.SpyRegistry({
const spyRegistry = new privateUnderTest.SpyRegistry({
createSpy: function() {
return 'I am a spy';
}
});
const subject = {
const target = {
spied1: function() {}
};
spyRegistry.spyOnAllFunctions(subject);
spyRegistry.spyOnAllFunctions(target);
expect(subject.spied1).toBe('I am a spy');
expect(subject.toString).not.toBe('I am a spy');
expect(subject.hasOwnProperty).not.toBe('I am a spy');
expect(target.spied1).toBe('I am a spy');
expect(target.toString).not.toBe('I am a spy');
expect(target.hasOwnProperty).not.toBe('I am a spy');
});
describe('when includeNonEnumerable is true', function() {
it('does not override Object.prototype methods', function() {
const spyRegistry = new jasmineUnderTest.SpyRegistry({
const spyRegistry = new privateUnderTest.SpyRegistry({
createSpy: function() {
return 'I am a spy';
}
});
const subject = {
const target = {
spied1: function() {}
};
spyRegistry.spyOnAllFunctions(subject, true);
spyRegistry.spyOnAllFunctions(target, true);
expect(subject.spied1).toBe('I am a spy');
expect(subject.toString).not.toBe('I am a spy');
expect(subject.hasOwnProperty).not.toBe('I am a spy');
expect(target.spied1).toBe('I am a spy');
expect(target.toString).not.toBe('I am a spy');
expect(target.hasOwnProperty).not.toBe('I am a spy');
});
it('overrides non-enumerable properties', function() {
const spyRegistry = new jasmineUnderTest.SpyRegistry({
const spyRegistry = new privateUnderTest.SpyRegistry({
createSpy: function() {
return 'I am a spy';
}
});
const subject = {
const target = {
spied1: function() {},
spied2: function() {}
};
Object.defineProperty(subject, 'spied2', {
Object.defineProperty(target, 'spied2', {
enumerable: false,
writable: true,
configurable: true
});
spyRegistry.spyOnAllFunctions(subject, true);
spyRegistry.spyOnAllFunctions(target, true);
expect(subject.spied1).toBe('I am a spy');
expect(subject.spied2).toBe('I am a spy');
expect(target.spied1).toBe('I am a spy');
expect(target.spied2).toBe('I am a spy');
});
it('should not spy on non-enumerable functions named constructor', function() {
const spyRegistry = new jasmineUnderTest.SpyRegistry({
const spyRegistry = new privateUnderTest.SpyRegistry({
createSpy: function() {
return 'I am a spy';
}
});
const subject = {
const target = {
constructor: function() {}
};
Object.defineProperty(subject, 'constructor', {
Object.defineProperty(target, 'constructor', {
enumerable: false,
writable: true,
configurable: true
});
spyRegistry.spyOnAllFunctions(subject, true);
spyRegistry.spyOnAllFunctions(target, true);
expect(subject.constructor).not.toBe('I am a spy');
expect(target.constructor).not.toBe('I am a spy');
});
it('should spy on enumerable functions named constructor', function() {
const spyRegistry = new jasmineUnderTest.SpyRegistry({
const spyRegistry = new privateUnderTest.SpyRegistry({
createSpy: function() {
return 'I am a spy';
}
});
const subject = {
const target = {
constructor: function() {}
};
spyRegistry.spyOnAllFunctions(subject, true);
spyRegistry.spyOnAllFunctions(target, true);
expect(subject.constructor).toBe('I am a spy');
expect(target.constructor).toBe('I am a spy');
});
it('should not throw an exception if we try and access strict mode restricted properties', function() {
const spyRegistry = new jasmineUnderTest.SpyRegistry({
const spyRegistry = new privateUnderTest.SpyRegistry({
createSpy: function() {
return 'I am a spy';
}
});
const subject = function() {};
const target = function() {};
const fn = function() {
spyRegistry.spyOnAllFunctions(subject, true);
spyRegistry.spyOnAllFunctions(target, true);
};
expect(fn).not.toThrow();
});
it('should not spy on properties which are more permissable further up the prototype chain', function() {
const spyRegistry = new jasmineUnderTest.SpyRegistry({
const spyRegistry = new privateUnderTest.SpyRegistry({
createSpy: function() {
return 'I am a spy';
}
});
const subjectParent = Object.defineProperty({}, 'sharedProp', {
const targetParent = Object.defineProperty({}, 'sharedProp', {
value: function() {},
writable: true,
configurable: true
});
const subject = Object.create(subjectParent);
const target = Object.create(targetParent);
Object.defineProperty(subject, 'sharedProp', {
Object.defineProperty(target, 'sharedProp', {
value: function() {}
});
const fn = function() {
spyRegistry.spyOnAllFunctions(subject, true);
spyRegistry.spyOnAllFunctions(target, true);
};
expect(fn).not.toThrow();
expect(subject).not.toBe('I am a spy');
expect(target).not.toBe('I am a spy');
});
});
});
@@ -529,90 +565,90 @@ describe('SpyRegistry', function() {
describe('#clearSpies', function() {
it('restores the original functions on the spied-upon objects', function() {
const spies = [],
spyRegistry = new jasmineUnderTest.SpyRegistry({
spyRegistry = new privateUnderTest.SpyRegistry({
currentSpies: function() {
return spies;
},
createSpy: createSpy
}),
originalFunction = function() {},
subject = { spiedFunc: originalFunction };
target = { spiedFunc: originalFunction };
spyRegistry.spyOn(subject, 'spiedFunc');
spyRegistry.spyOn(target, 'spiedFunc');
spyRegistry.clearSpies();
expect(subject.spiedFunc).toBe(originalFunction);
expect(target.spiedFunc).toBe(originalFunction);
});
it('restores the original functions, even when that spy has been replace and re-spied upon', function() {
const spies = [],
spyRegistry = new jasmineUnderTest.SpyRegistry({
spyRegistry = new privateUnderTest.SpyRegistry({
currentSpies: function() {
return spies;
},
createSpy: createSpy
}),
originalFunction = function() {},
subject = { spiedFunc: originalFunction };
target = { spiedFunc: originalFunction };
spyRegistry.spyOn(subject, 'spiedFunc');
spyRegistry.spyOn(target, 'spiedFunc');
// replace the original spy with some other function
subject.spiedFunc = function() {};
target.spiedFunc = function() {};
// spy on the function in that location again
spyRegistry.spyOn(subject, 'spiedFunc');
spyRegistry.spyOn(target, 'spiedFunc');
spyRegistry.clearSpies();
expect(subject.spiedFunc).toBe(originalFunction);
expect(target.spiedFunc).toBe(originalFunction);
});
it("does not add a property that the spied-upon object didn't originally have", function() {
const spies = [],
spyRegistry = new jasmineUnderTest.SpyRegistry({
spyRegistry = new privateUnderTest.SpyRegistry({
currentSpies: function() {
return spies;
},
createSpy: createSpy
}),
originalFunction = function() {},
subjectParent = { spiedFunc: originalFunction };
targetParent = { spiedFunc: originalFunction };
const subject = Object.create(subjectParent);
const target = Object.create(targetParent);
expect(subject.hasOwnProperty('spiedFunc')).toBe(false);
expect(target.hasOwnProperty('spiedFunc')).toBe(false);
spyRegistry.spyOn(subject, 'spiedFunc');
spyRegistry.spyOn(target, 'spiedFunc');
spyRegistry.clearSpies();
expect(subject.hasOwnProperty('spiedFunc')).toBe(false);
expect(subject.spiedFunc).toBe(originalFunction);
expect(target.hasOwnProperty('spiedFunc')).toBe(false);
expect(target.spiedFunc).toBe(originalFunction);
});
it("restores the original function when it's inherited and cannot be deleted", function() {
const spies = [],
spyRegistry = new jasmineUnderTest.SpyRegistry({
spyRegistry = new privateUnderTest.SpyRegistry({
currentSpies: function() {
return spies;
},
createSpy: createSpy
}),
originalFunction = function() {},
subjectParent = { spiedFunc: originalFunction };
targetParent = { spiedFunc: originalFunction };
const subject = Object.create(subjectParent);
const target = Object.create(targetParent);
spyRegistry.spyOn(subject, 'spiedFunc');
spyRegistry.spyOn(target, 'spiedFunc');
// simulate a spy that cannot be deleted
Object.defineProperty(subject, 'spiedFunc', {
Object.defineProperty(target, 'spiedFunc', {
configurable: false
});
spyRegistry.clearSpies();
expect(jasmineUnderTest.isSpy(subject.spiedFunc)).toBe(false);
expect(jasmineUnderTest.isSpy(target.spiedFunc)).toBe(false);
});
it('restores window.onerror by overwriting, not deleting', function() {
@@ -621,7 +657,7 @@ describe('SpyRegistry', function() {
const spies = [],
global = new FakeWindow(),
spyRegistry = new jasmineUnderTest.SpyRegistry({
spyRegistry = new privateUnderTest.SpyRegistry({
currentSpies: function() {
return spies;
},
@@ -639,55 +675,55 @@ describe('SpyRegistry', function() {
describe('spying on properties', function() {
it('restores the original properties on the spied-upon objects', function() {
const spies = [],
spyRegistry = new jasmineUnderTest.SpyRegistry({
spyRegistry = new privateUnderTest.SpyRegistry({
currentSpies: function() {
return spies;
},
createSpy: createSpy
}),
originalReturn = 1,
subject = {};
target = {};
Object.defineProperty(subject, 'spiedProp', {
Object.defineProperty(target, 'spiedProp', {
get: function() {
return originalReturn;
},
configurable: true
});
spyRegistry.spyOnProperty(subject, 'spiedProp');
spyRegistry.spyOnProperty(target, 'spiedProp');
spyRegistry.clearSpies();
expect(subject.spiedProp).toBe(originalReturn);
expect(target.spiedProp).toBe(originalReturn);
});
it("does not add a property that the spied-upon object didn't originally have", function() {
const spies = [],
spyRegistry = new jasmineUnderTest.SpyRegistry({
spyRegistry = new privateUnderTest.SpyRegistry({
currentSpies: function() {
return spies;
},
createSpy: createSpy
}),
originalReturn = 1,
subjectParent = {};
targetParent = {};
Object.defineProperty(subjectParent, 'spiedProp', {
Object.defineProperty(targetParent, 'spiedProp', {
get: function() {
return originalReturn;
},
configurable: true
});
const subject = Object.create(subjectParent);
const target = Object.create(targetParent);
expect(subject.hasOwnProperty('spiedProp')).toBe(false);
expect(target.hasOwnProperty('spiedProp')).toBe(false);
spyRegistry.spyOnProperty(subject, 'spiedProp');
spyRegistry.spyOnProperty(target, 'spiedProp');
spyRegistry.clearSpies();
expect(subject.hasOwnProperty('spiedProp')).toBe(false);
expect(subject.spiedProp).toBe(originalReturn);
expect(target.hasOwnProperty('spiedProp')).toBe(false);
expect(target.spiedProp).toBe(originalReturn);
});
});
});

View File

@@ -2,7 +2,7 @@ describe('Spies', function() {
let env;
beforeEach(function() {
env = new jasmineUnderTest.Env();
env = new privateUnderTest.Env();
});
afterEach(function() {
@@ -50,8 +50,8 @@ describe('Spies', function() {
TestClass.prototype.someFunction
);
expect(spy.and).toEqual(jasmine.any(jasmineUnderTest.SpyStrategy));
expect(spy.calls).toEqual(jasmine.any(jasmineUnderTest.CallTracker));
expect(spy.and).toEqual(jasmine.any(privateUnderTest.SpyStrategy));
expect(spy.calls).toEqual(jasmine.any(privateUnderTest.CallTracker));
});
it('tracks the argument of calls', function() {
@@ -97,17 +97,11 @@ describe('Spies', function() {
it('preserves arity of original function', function() {
const functions = [
function nullary() {},
// eslint-disable-next-line no-unused-vars
function unary(arg) {},
// eslint-disable-next-line no-unused-vars
function binary(arg1, arg2) {},
// eslint-disable-next-line no-unused-vars
function ternary(arg1, arg2, arg3) {},
// eslint-disable-next-line no-unused-vars
function quaternary(arg1, arg2, arg3, arg4) {},
// eslint-disable-next-line no-unused-vars
function quinary(arg1, arg2, arg3, arg4, arg5) {},
// eslint-disable-next-line no-unused-vars
function senary(arg1, arg2, arg3, arg4, arg5, arg6) {}
];
@@ -159,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'
);
});
@@ -167,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'
);
});
@@ -175,7 +169,7 @@ 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'
);
});
@@ -247,7 +241,7 @@ describe('Spies', function() {
});
it('uses the provided matchersUtil selecting a strategy', function() {
const matchersUtil = new jasmineUnderTest.MatchersUtil({
const matchersUtil = new privateUnderTest.MatchersUtil({
customTesters: [
function(a, b) {
if ((a === 'bar' && b === 'baz') || (a === 'baz' && b === 'bar')) {
@@ -256,7 +250,7 @@ describe('Spies', function() {
}
]
});
const spy = new jasmineUnderTest.Spy('aSpy', matchersUtil);
const spy = new privateUnderTest.Spy('aSpy', matchersUtil);
spy.and.returnValue('default strategy return value');
spy.withArgs('bar').and.returnValue('custom strategy return value');
expect(spy('foo')).toEqual('default strategy return value');

View File

@@ -1,19 +1,19 @@
describe('SpyStrategy', function() {
it('defaults its name to unknown', function() {
const spyStrategy = new jasmineUnderTest.SpyStrategy();
const spyStrategy = new privateUnderTest.SpyStrategy();
expect(spyStrategy.identity).toEqual('unknown');
});
it('takes a name', function() {
const spyStrategy = new jasmineUnderTest.SpyStrategy({ name: 'foo' });
const spyStrategy = new privateUnderTest.SpyStrategy({ name: 'foo' });
expect(spyStrategy.identity).toEqual('foo');
});
it('stubs an original function, if provided', function() {
const originalFn = jasmine.createSpy('original'),
spyStrategy = new jasmineUnderTest.SpyStrategy({ fn: originalFn });
spyStrategy = new privateUnderTest.SpyStrategy({ fn: originalFn });
spyStrategy.exec();
@@ -22,7 +22,7 @@ describe('SpyStrategy', function() {
it("allows an original function to be called, passed through the params and returns it's value", function() {
const originalFn = jasmine.createSpy('original').and.returnValue(42),
spyStrategy = new jasmineUnderTest.SpyStrategy({ fn: originalFn });
spyStrategy = new privateUnderTest.SpyStrategy({ fn: originalFn });
spyStrategy.callThrough();
const returnValue = spyStrategy.exec(null, ['foo']);
@@ -34,7 +34,7 @@ describe('SpyStrategy', function() {
it('can return a specified value when executed', function() {
const originalFn = jasmine.createSpy('original'),
spyStrategy = new jasmineUnderTest.SpyStrategy({ fn: originalFn });
spyStrategy = new privateUnderTest.SpyStrategy({ fn: originalFn });
spyStrategy.returnValue(17);
const returnValue = spyStrategy.exec();
@@ -45,7 +45,7 @@ describe('SpyStrategy', function() {
it('can return specified values in order specified when executed', function() {
const originalFn = jasmine.createSpy('original'),
spyStrategy = new jasmineUnderTest.SpyStrategy({ fn: originalFn });
spyStrategy = new privateUnderTest.SpyStrategy({ fn: originalFn });
spyStrategy.returnValues('value1', 'value2', 'value3');
@@ -58,7 +58,7 @@ describe('SpyStrategy', function() {
it('allows an exception to be thrown when executed', function() {
const originalFn = jasmine.createSpy('original'),
spyStrategy = new jasmineUnderTest.SpyStrategy({ fn: originalFn });
spyStrategy = new privateUnderTest.SpyStrategy({ fn: originalFn });
spyStrategy.throwError(new TypeError('bar'));
@@ -70,7 +70,7 @@ describe('SpyStrategy', function() {
it('allows a string to be thrown, wrapping it into an exception when executed', function() {
const originalFn = jasmine.createSpy('original'),
spyStrategy = new jasmineUnderTest.SpyStrategy({ fn: originalFn });
spyStrategy = new privateUnderTest.SpyStrategy({ fn: originalFn });
spyStrategy.throwError('bar');
@@ -82,7 +82,7 @@ describe('SpyStrategy', function() {
it('allows a non-Error to be thrown when executed', function() {
const originalFn = jasmine.createSpy('original'),
spyStrategy = new jasmineUnderTest.SpyStrategy({ fn: originalFn });
spyStrategy = new privateUnderTest.SpyStrategy({ fn: originalFn });
spyStrategy.throwError({ code: 'ESRCH' });
@@ -95,7 +95,7 @@ describe('SpyStrategy', function() {
it('allows a fake function to be called instead', function() {
const originalFn = jasmine.createSpy('original'),
fakeFn = jasmine.createSpy('fake').and.returnValue(67),
spyStrategy = new jasmineUnderTest.SpyStrategy({ fn: originalFn });
spyStrategy = new privateUnderTest.SpyStrategy({ fn: originalFn });
spyStrategy.callFake(fakeFn);
const returnValue = spyStrategy.exec();
@@ -109,7 +109,7 @@ describe('SpyStrategy', function() {
fakeFn = jasmine.createSpy('fake').and.callFake(async () => {
return 67;
}),
spyStrategy = new jasmineUnderTest.SpyStrategy({ fn: originalFn });
spyStrategy = new privateUnderTest.SpyStrategy({ fn: originalFn });
spyStrategy.callFake(fakeFn);
spyStrategy
@@ -128,7 +128,7 @@ describe('SpyStrategy', function() {
describe('#resolveTo', function() {
it('allows a resolved promise to be returned', function(done) {
const originalFn = jasmine.createSpy('original'),
spyStrategy = new jasmineUnderTest.SpyStrategy({
spyStrategy = new privateUnderTest.SpyStrategy({
fn: originalFn
});
@@ -144,7 +144,7 @@ describe('SpyStrategy', function() {
it('allows an empty resolved promise to be returned', function(done) {
const originalFn = jasmine.createSpy('original'),
spyStrategy = new jasmineUnderTest.SpyStrategy({
spyStrategy = new privateUnderTest.SpyStrategy({
fn: originalFn
});
@@ -162,7 +162,7 @@ describe('SpyStrategy', function() {
describe('#rejectWith', function() {
it('allows a rejected promise to be returned', function(done) {
const originalFn = jasmine.createSpy('original'),
spyStrategy = new jasmineUnderTest.SpyStrategy({
spyStrategy = new privateUnderTest.SpyStrategy({
fn: originalFn
});
@@ -179,7 +179,7 @@ describe('SpyStrategy', function() {
it('allows an empty rejected promise to be returned', function(done) {
const originalFn = jasmine.createSpy('original'),
spyStrategy = new jasmineUnderTest.SpyStrategy({
spyStrategy = new privateUnderTest.SpyStrategy({
fn: originalFn
});
@@ -196,7 +196,7 @@ describe('SpyStrategy', function() {
it('allows a non-Error to be rejected', function(done) {
const originalFn = jasmine.createSpy('original'),
spyStrategy = new jasmineUnderTest.SpyStrategy({
spyStrategy = new privateUnderTest.SpyStrategy({
fn: originalFn
});
@@ -220,7 +220,7 @@ describe('SpyStrategy', function() {
.createSpy('custom strategy')
.and.returnValue(plan),
originalFn = jasmine.createSpy('original'),
spyStrategy = new jasmineUnderTest.SpyStrategy({
spyStrategy = new privateUnderTest.SpyStrategy({
fn: originalFn,
customStrategies: {
doSomething: customStrategy
@@ -237,7 +237,7 @@ describe('SpyStrategy', function() {
it("throws an error if a custom strategy doesn't return a function", function() {
const originalFn = jasmine.createSpy('original'),
spyStrategy = new jasmineUnderTest.SpyStrategy({
spyStrategy = new privateUnderTest.SpyStrategy({
fn: originalFn,
customStrategies: {
doSomething: function() {
@@ -252,37 +252,32 @@ describe('SpyStrategy', function() {
});
it('does not allow custom strategies to overwrite existing methods', function() {
const spyStrategy = new jasmineUnderTest.SpyStrategy({
const spyStrategy = new privateUnderTest.SpyStrategy({
fn: function() {},
customStrategies: {
exec: function() {}
}
});
expect(spyStrategy.exec).toBe(jasmineUnderTest.SpyStrategy.prototype.exec);
expect(spyStrategy.exec).toBe(privateUnderTest.SpyStrategy.prototype.exec);
});
it('throws an error when a non-function is passed to callFake strategy', function() {
const originalFn = jasmine.createSpy('original'),
spyStrategy = new jasmineUnderTest.SpyStrategy({ fn: originalFn });
spyOn(jasmineUnderTest, 'isFunction_').and.returnValue(false);
spyOn(jasmineUnderTest, 'isAsyncFunction_').and.returnValue(false);
spyStrategy = new privateUnderTest.SpyStrategy({ fn: originalFn });
expect(function() {
spyStrategy.callFake(function() {});
}).toThrowError(/^Argument passed to callFake should be a function, got/);
expect(function() {
spyStrategy.callFake(function() {});
}).toThrowError(/^Argument passed to callFake should be a function, got/);
spyStrategy.callFake('not a function');
}).toThrowError(
'Argument passed to callFake should be a function, got not a function'
);
});
it('allows generator functions to be passed to callFake strategy', function() {
const generator = function*() {
yield 'ok';
},
spyStrategy = new jasmineUnderTest.SpyStrategy({ fn: function() {} });
spyStrategy = new privateUnderTest.SpyStrategy({ fn: function() {} });
spyStrategy.callFake(generator);
@@ -292,7 +287,7 @@ describe('SpyStrategy', function() {
it('allows a return to plan stubbing after another strategy', function() {
const originalFn = jasmine.createSpy('original'),
fakeFn = jasmine.createSpy('fake').and.returnValue(67),
spyStrategy = new jasmineUnderTest.SpyStrategy({ fn: originalFn });
spyStrategy = new privateUnderTest.SpyStrategy({ fn: originalFn });
spyStrategy.callFake(fakeFn);
let returnValue = spyStrategy.exec();
@@ -309,7 +304,7 @@ describe('SpyStrategy', function() {
it('returns the spy after changing the strategy', function() {
const spy = {},
spyFn = jasmine.createSpy('spyFn').and.returnValue(spy),
spyStrategy = new jasmineUnderTest.SpyStrategy({ getSpy: spyFn });
spyStrategy = new privateUnderTest.SpyStrategy({ getSpy: spyFn });
expect(spyStrategy.callThrough()).toBe(spy);
expect(spyStrategy.returnValue()).toBe(spy);

View File

@@ -8,7 +8,7 @@ describe('StackTrace', function() {
' at QueueRunner.run (http://localhost:8888/__jasmine__/jasmine.js:4320:20)'
};
const result = new jasmineUnderTest.StackTrace(error);
const result = new privateUnderTest.StackTrace(error);
expect(result.message).toEqual('Error: nope');
expect(result.style).toEqual('v8');
@@ -39,7 +39,7 @@ describe('StackTrace', function() {
' at QueueRunner.run (http://localhost:8888/__jasmine__/jasmine.js:4320:20)'
};
const result = new jasmineUnderTest.StackTrace(error);
const result = new privateUnderTest.StackTrace(error);
expect(result.message).toEqual('Error: line 1\nline 2');
const rawFrames = result.frames.map(function(f) {
@@ -51,6 +51,27 @@ describe('StackTrace', function() {
]);
});
it('understands Chrome/Edge style traces with messages containing blank lines', function() {
const error = {
message: 'line 1\n\nline 2',
stack:
'Error: line 1\n\nline 2\n' +
' at UserContext.<anonymous> (http://localhost:8888/__spec__/core/UtilSpec.js:115:19)\n' +
' at QueueRunner.run (http://localhost:8888/__jasmine__/jasmine.js:4320:20)'
};
const result = new privateUnderTest.StackTrace(error);
expect(result.message).toEqual('Error: line 1\n\nline 2');
const rawFrames = result.frames.map(function(f) {
return f.raw;
});
expect(rawFrames).toEqual([
' at UserContext.<anonymous> (http://localhost:8888/__spec__/core/UtilSpec.js:115:19)',
' at QueueRunner.run (http://localhost:8888/__jasmine__/jasmine.js:4320:20)'
]);
});
it('understands Node style traces', function() {
const error = {
message: 'nope',
@@ -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)'
};
const result = new jasmineUnderTest.StackTrace(error);
const result = new privateUnderTest.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() {
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'
};
const result = new jasmineUnderTest.StackTrace(error);
const result = new privateUnderTest.StackTrace(error);
expect(result.message).toBeFalsy();
expect(result.style).toEqual('webkit');
@@ -129,7 +150,7 @@ describe('StackTrace', function() {
'@http://localhost:8888/__spec__/core/FooSpec.js:164:24\n' +
'attempt@http://localhost:8888/__jasmine__/jasmine.js:8074:44\n'
};
const result = new jasmineUnderTest.StackTrace(error);
const result = new privateUnderTest.StackTrace(error);
expect(result.message).toBeFalsy();
expect(result.style).toEqual('webkit');
@@ -149,46 +170,16 @@ describe('StackTrace', function() {
]);
});
it('does not mistake gibberish for Safari/Firefox/Phantom-OS X style traces', function() {
it('does not mistake gibberish for Safari/Firefox style traces', function() {
const error = {
message: 'nope',
stack: 'randomcharsnotincludingwhitespace'
};
const result = new jasmineUnderTest.StackTrace(error);
const result = new privateUnderTest.StackTrace(error);
expect(result.style).toBeNull();
expect(result.frames).toEqual([{ raw: error.stack }]);
});
it('understands Phantom-Linux style traces', function() {
const error = {
message: 'nope',
stack:
' at UserContext.<anonymous> (http://localhost:8888/__spec__/core/UtilSpec.js:115:19)\n' +
' at QueueRunner.run (http://localhost:8888/__jasmine__/jasmine.js:4320:20)'
};
const result = new jasmineUnderTest.StackTrace(error);
expect(result.message).toBeFalsy();
expect(result.style).toEqual('v8');
expect(result.frames).toEqual([
{
raw:
' at UserContext.<anonymous> (http://localhost:8888/__spec__/core/UtilSpec.js:115:19)',
func: 'UserContext.<anonymous>',
file: 'http://localhost:8888/__spec__/core/UtilSpec.js',
line: 115
},
{
raw:
' at QueueRunner.run (http://localhost:8888/__jasmine__/jasmine.js:4320:20)',
func: 'QueueRunner.run',
file: 'http://localhost:8888/__jasmine__/jasmine.js',
line: 4320
}
]);
});
it('ignores blank lines', function() {
const error = {
message: 'nope',
@@ -196,7 +187,7 @@ describe('StackTrace', function() {
' at UserContext.<anonymous> (http://localhost:8888/__spec__/core/UtilSpec.js:115:19)\n'
};
const result = new jasmineUnderTest.StackTrace(error);
const result = new privateUnderTest.StackTrace(error);
expect(result.frames).toEqual([
{
@@ -218,7 +209,7 @@ describe('StackTrace', function() {
' at QueueRunner.run (http://localhost:8888/__jasmine__/jasmine.js:4320:20)'
};
const result = new jasmineUnderTest.StackTrace(error);
const result = new privateUnderTest.StackTrace(error);
expect(result.style).toEqual('v8');
expect(result.frames).toEqual([
{
@@ -241,7 +232,7 @@ describe('StackTrace', function() {
]);
});
it('consideres different types of errors', function() {
it('considers different types of errors', function() {
const error = {
message: 'nope',
stack:
@@ -250,7 +241,7 @@ describe('StackTrace', function() {
' at QueueRunner.run (http://localhost:8888/__jasmine__/jasmine.js:4320:20)'
};
const result = new jasmineUnderTest.StackTrace(error);
const result = new privateUnderTest.StackTrace(error);
expect(result.message).toEqual('TypeError: nope');
expect(result.frames).toEqual([
@@ -278,7 +269,7 @@ describe('StackTrace', function() {
' at QueueRunner.run (http://localhost:8888/__jasmine__/jasmine.js:4320:20)'
};
const result_no_error = new jasmineUnderTest.StackTrace(no_error);
const result_no_error = new privateUnderTest.StackTrace(no_error);
expect(result_no_error.message).not.toEqual(jasmine.anything());
});

View File

@@ -1,12 +1,12 @@
describe('SuiteBuilder', function() {
beforeEach(function() {
// Rethrow exceptions to ease debugging
spyOn(jasmineUnderTest.Suite.prototype, 'handleException').and.callFake(
spyOn(privateUnderTest.Suite.prototype, 'handleException').and.callFake(
function(e) {
throw e;
}
);
spyOn(jasmineUnderTest.Spec.prototype, 'handleException').and.callFake(
spyOn(privateUnderTest.Spec.prototype, 'handleException').and.callFake(
function(e) {
throw e;
}
@@ -15,9 +15,9 @@ describe('SuiteBuilder', function() {
it('creates the top suite', function() {
const env = { configuration: () => ({}) };
const suiteBuilder = new jasmineUnderTest.SuiteBuilder({ env });
const suiteBuilder = new privateUnderTest.SuiteBuilder({ env });
expect(suiteBuilder.topSuite).toBeInstanceOf(jasmineUnderTest.Suite);
expect(suiteBuilder.topSuite).toBeInstanceOf(privateUnderTest.Suite);
expect(suiteBuilder.topSuite.description).toEqual(
'Jasmine__TopLevel__Suite'
);
@@ -33,7 +33,7 @@ describe('SuiteBuilder', function() {
it('focuses the suite', function() {
const env = { configuration: () => ({}) };
const suiteBuilder = new jasmineUnderTest.SuiteBuilder({ env });
const suiteBuilder = new privateUnderTest.SuiteBuilder({ env });
const suite = suiteBuilder.fdescribe('a suite', function() {
suiteBuilder.it('a spec');
@@ -45,7 +45,7 @@ describe('SuiteBuilder', function() {
it('unfocuses any focused ancestor suite', function() {
const env = { configuration: () => ({}) };
const suiteBuilder = new jasmineUnderTest.SuiteBuilder({ env });
const suiteBuilder = new privateUnderTest.SuiteBuilder({ env });
const grandparent = suiteBuilder.fdescribe('a suite', function() {
suiteBuilder.describe('another suite', function() {
@@ -64,7 +64,7 @@ describe('SuiteBuilder', function() {
it('excludes the suite', function() {
const env = { configuration: () => ({}) };
const suiteBuilder = new jasmineUnderTest.SuiteBuilder({ env });
const suiteBuilder = new privateUnderTest.SuiteBuilder({ env });
const suite = suiteBuilder.xdescribe('a suite', function() {
suiteBuilder.it('a spec');
@@ -75,7 +75,7 @@ describe('SuiteBuilder', function() {
it('causes child suites to be marked excluded', function() {
const env = { configuration: () => ({}) };
const suiteBuilder = new jasmineUnderTest.SuiteBuilder({ env });
const suiteBuilder = new privateUnderTest.SuiteBuilder({ env });
let suite;
suiteBuilder.xdescribe('a suite', function() {
@@ -103,7 +103,7 @@ describe('SuiteBuilder', function() {
function definesSuites(fnName) {
it('links suites to their parents and children', function() {
const env = { configuration: () => ({}) };
const suiteBuilder = new jasmineUnderTest.SuiteBuilder({ env });
const suiteBuilder = new privateUnderTest.SuiteBuilder({ env });
let child;
const parent = suiteBuilder[fnName]('parent', function() {
@@ -120,7 +120,7 @@ describe('SuiteBuilder', function() {
it('gives each suite a unique ID', function() {
const env = { configuration: () => ({}) };
const suiteBuilder = new jasmineUnderTest.SuiteBuilder({ env });
const suiteBuilder = new privateUnderTest.SuiteBuilder({ env });
let child;
const parent = suiteBuilder[fnName]('parent', function() {
@@ -142,7 +142,7 @@ describe('SuiteBuilder', function() {
function definesSpecs(fnName) {
it('adds the spec to its suite', function() {
const env = { configuration: () => ({}) };
const suiteBuilder = new jasmineUnderTest.SuiteBuilder({ env });
const suiteBuilder = new privateUnderTest.SuiteBuilder({ env });
let spec;
const suite = suiteBuilder.describe('a suite', function() {
@@ -154,7 +154,7 @@ describe('SuiteBuilder', function() {
it('gives each spec a unique ID', function() {
const env = { configuration: () => ({}) };
const suiteBuilder = new jasmineUnderTest.SuiteBuilder({ env });
const suiteBuilder = new privateUnderTest.SuiteBuilder({ env });
const spec1 = suiteBuilder[fnName]('a spec', function() {});
const spec2 = suiteBuilder[fnName]('another spec', function() {});
@@ -163,6 +163,20 @@ describe('SuiteBuilder', function() {
expect(spec2.id).toMatch(/^spec[0-9]+$/);
expect(spec1.id).not.toEqual(spec2.id);
});
it('gives each spec a full path', function() {
const env = { configuration: () => ({}) };
const suiteBuilder = new privateUnderTest.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) {
@@ -175,4 +189,202 @@ describe('SuiteBuilder', function() {
}
};
}
describe('Duplicate name handling', function() {
describe('When forbidDuplicateNames is true', function() {
let env;
beforeEach(function() {
env = { configuration: () => ({ forbidDuplicateNames: true }) };
});
it('forbids duplicate spec names', function() {
const suiteBuilder = new privateUnderTest.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 privateUnderTest.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 privateUnderTest.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 privateUnderTest.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 privateUnderTest.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 privateUnderTest.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() {
privateUnderTest.Suite.prototype.handleException.and.callThrough();
const env = { configuration: () => ({}) };
const suiteBuilder = new privateUnderTest.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 privateUnderTest.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 privateUnderTest.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 privateUnderTest.SuiteBuilder({ env });
suiteBuilder.it('a spec');
suiteBuilder.parallelReset();
expect(suiteBuilder.totalSpecsDefined).toEqual(0);
});
it('resets focusedRunables', function() {
const env = { configuration: () => ({}) };
const suiteBuilder = new privateUnderTest.SuiteBuilder({ env });
suiteBuilder.fit('a spec', function() {});
suiteBuilder.parallelReset();
expect(suiteBuilder.focusedRunables).toEqual([]);
});
});
});

View File

@@ -2,7 +2,7 @@ describe('Suite', function() {
let env;
beforeEach(function() {
env = new jasmineUnderTest.Env();
env = new privateUnderTest.Env();
});
afterEach(function() {
@@ -10,7 +10,7 @@ describe('Suite', function() {
});
it('keeps its id', function() {
const suite = new jasmineUnderTest.Suite({
const suite = new privateUnderTest.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() {
const suite = new jasmineUnderTest.Suite({
const suite = new privateUnderTest.Suite({
env: env,
description: 'I am a suite'
});
@@ -29,12 +29,12 @@ describe('Suite', function() {
});
it('returns its full name when it has parent suites', function() {
const parentSuite = new jasmineUnderTest.Suite({
const parentSuite = new privateUnderTest.Suite({
env: env,
description: 'I am a parent suite',
parentSuite: jasmine.createSpy('pretend top level suite')
}),
suite = new jasmineUnderTest.Suite({
suite = new privateUnderTest.Suite({
env: env,
description: 'I am a suite',
parentSuite: parentSuite
@@ -44,7 +44,7 @@ describe('Suite', function() {
});
it('adds beforeEach functions in order of needed execution', function() {
const suite = new jasmineUnderTest.Suite({
const suite = new privateUnderTest.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() {
const suite = new jasmineUnderTest.Suite({
const suite = new privateUnderTest.Suite({
env: env,
description: 'I am a suite'
}),
@@ -78,7 +78,7 @@ describe('Suite', function() {
});
it('adds afterEach functions in order of needed execution', function() {
const suite = new jasmineUnderTest.Suite({
const suite = new privateUnderTest.Suite({
env: env,
description: 'I am a suite'
}),
@@ -95,7 +95,7 @@ describe('Suite', function() {
});
it('adds afterAll functions in order of needed execution', function() {
const suite = new jasmineUnderTest.Suite({
const suite = new privateUnderTest.Suite({
env: env,
description: 'I am a suite'
}),
@@ -112,51 +112,53 @@ describe('Suite', function() {
});
it('has a status of failed if any expectations have failed', function() {
const suite = new jasmineUnderTest.Suite({});
const suite = new privateUnderTest.Suite({});
suite.addExpectationResult(false, {});
expect(suite.status()).toBe('failed');
expect(suite.getResult().status).toBe('failed');
});
it('retrieves a result with updated status', function() {
const suite = new jasmineUnderTest.Suite({});
const suite = new privateUnderTest.Suite({});
expect(suite.getResult().status).toBe('passed');
});
it('retrieves a result with pending status', function() {
const suite = new jasmineUnderTest.Suite({});
const suite = new privateUnderTest.Suite({});
suite.pend();
expect(suite.getResult().status).toBe('pending');
});
it('throws an ExpectationFailed when receiving a failed expectation when throwOnExpectationFailure is set', function() {
const suite = new jasmineUnderTest.Suite({
const suite = new privateUnderTest.Suite({
throwOnExpectationFailure: true
});
expect(function() {
suite.addExpectationResult(false, { message: 'failed' });
}).toThrowError(jasmineUnderTest.errors.ExpectationFailed);
}).toThrowError(jasmineUnderTest.private.errors.ExpectationFailed);
expect(suite.status()).toBe('failed');
expect(suite.getResult().status).toBe('failed');
expect(suite.result.failedExpectations).toEqual([
jasmine.objectContaining({ message: 'failed' })
]);
});
it('does not add an additional failure when an expectation fails', function() {
const suite = new jasmineUnderTest.Suite({});
const suite = new privateUnderTest.Suite({});
suite.handleException(new jasmineUnderTest.errors.ExpectationFailed());
suite.handleException(
new jasmineUnderTest.private.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 suite = new privateUnderTest.Suite({ onLateError });
const data = {
matcherName: '',
passed: false,
@@ -178,7 +180,7 @@ describe('Suite', function() {
it('does not forward non-late expectation failures to onLateError', function() {
const onLateError = jasmine.createSpy('onLateError');
const suite = new jasmineUnderTest.Suite({
const suite = new privateUnderTest.Suite({
onLateError
});
const data = {
@@ -197,7 +199,7 @@ describe('Suite', function() {
it('forwards late handleException calls to onLateError', function() {
const onLateError = jasmine.createSpy('onLateError');
const suite = new jasmineUnderTest.Suite({
const suite = new privateUnderTest.Suite({
onLateError
});
const error = new Error('oops');
@@ -215,7 +217,7 @@ describe('Suite', function() {
it('does not forward non-late handleException calls to onLateError', function() {
const onLateError = jasmine.createSpy('onLateError');
const suite = new jasmineUnderTest.Suite({
const suite = new privateUnderTest.Suite({
onLateError
});
const error = new Error('oops');
@@ -227,7 +229,7 @@ describe('Suite', function() {
});
it('clears the reportedDone flag when reset', function() {
const suite = new jasmineUnderTest.Suite({
const suite = new privateUnderTest.Suite({
queueableFn: { fn: function() {} }
});
suite.reportedDone = true;
@@ -238,7 +240,7 @@ describe('Suite', function() {
});
it('calls timer to compute duration', function() {
const suite = new jasmineUnderTest.Suite({
const suite = new privateUnderTest.Suite({
env: env,
id: 456,
description: 'I am a suite',
@@ -251,19 +253,19 @@ describe('Suite', function() {
describe('#sharedUserContext', function() {
beforeEach(function() {
this.suite = new jasmineUnderTest.Suite({});
this.suite = new privateUnderTest.Suite({});
});
it('returns a UserContext', function() {
expect(this.suite.sharedUserContext().constructor).toBe(
jasmineUnderTest.UserContext
privateUnderTest.UserContext
);
});
});
describe('attr.autoCleanClosures', function() {
function arrangeSuite(attrs) {
const suite = new jasmineUnderTest.Suite(attrs);
const suite = new privateUnderTest.Suite(attrs);
suite.beforeAll(function() {});
suite.beforeEach(function() {});
suite.afterEach(function() {});
@@ -301,21 +303,21 @@ describe('Suite', function() {
describe('#reset', function() {
it('should reset the "pending" status', function() {
const suite = new jasmineUnderTest.Suite({});
const suite = new privateUnderTest.Suite({});
suite.pend();
suite.reset();
expect(suite.getResult().status).toBe('passed');
});
it('should not reset the "pending" status when the suite was excluded', function() {
const suite = new jasmineUnderTest.Suite({});
const suite = new privateUnderTest.Suite({});
suite.exclude();
suite.reset();
expect(suite.getResult().status).toBe('pending');
});
it('should also reset the children', function() {
const suite = new jasmineUnderTest.Suite({});
const suite = new privateUnderTest.Suite({});
const child1 = jasmine.createSpyObj(['reset']);
const child2 = jasmine.createSpyObj(['reset']);
suite.addChild(child1);
@@ -328,7 +330,7 @@ describe('Suite', function() {
});
it('should reset the failedExpectations', function() {
const suite = new jasmineUnderTest.Suite({});
const suite = new privateUnderTest.Suite({});
suite.handleException(new Error());
suite.reset();
@@ -342,7 +344,7 @@ describe('Suite', function() {
describe('#onMultipleDone', function() {
it('reports a special error when it is the top suite', function() {
const onLateError = jasmine.createSpy('onLateError');
const suite = new jasmineUnderTest.Suite({
const suite = new privateUnderTest.Suite({
onLateError,
parentSuite: null
});
@@ -359,7 +361,7 @@ describe('Suite', function() {
it('reports an error including the suite name when it is a normal suite', function() {
const onLateError = jasmine.createSpy('onLateError');
const suite = new jasmineUnderTest.Suite({
const suite = new privateUnderTest.Suite({
onLateError,
description: 'the suite',
parentSuite: {
@@ -378,4 +380,49 @@ describe('Suite', function() {
);
});
});
describe('#hasChildWithDescription', function() {
it('returns true if there is a child with the given description', function() {
const subject = new privateUnderTest.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 privateUnderTest.Suite({});
subject.addChild({ description: 'a different spec' });
expect(subject.hasChildWithDescription('a spec')).toBeFalse();
});
it('does not recurse into child suites', function() {
const subject = new privateUnderTest.Suite({});
const childSuite = new privateUnderTest.Suite({});
subject.addChild(childSuite);
const description = 'a spec';
childSuite.addChild(description);
expect(subject.hasChildWithDescription('a spec')).toBeFalse();
});
});
describe('#setSuiteProperty', function() {
it('throws if the key is not structured-cloneable', function() {
const suite = new privateUnderTest.Suite({});
expect(function() {
suite.setSuiteProperty(new Promise(() => {}), '');
}).toThrowError("Key can't be cloned");
});
it('throws if the value is not structured-cloneable', function() {
const suite = new privateUnderTest.Suite({});
expect(function() {
suite.setSuiteProperty('k', new Promise(() => {}));
}).toThrowError("Value can't be cloned");
});
});
});

File diff suppressed because it is too large Load Diff

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

@@ -0,0 +1,719 @@
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 privateUnderTest.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.startedEvent()
);
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 privateUnderTest.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.doneEvent());
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 privateUnderTest.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 privateUnderTest.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.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 privateUnderTest.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.getResult().status).toEqual('pending');
expect(spec.getResult().pendingReason).toEqual('some reason');
});
it('passes failSpecWithNoExp to Spec#executionFinished', async function() {
const spec = new privateUnderTest.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('Suite execution', function() {
it('reports the duration of the suite', async function() {
const timer = jasmine.createSpyObj('timer', ['start', 'elapsed']);
const topSuite = new privateUnderTest.Suite({ id: 'topSuite' });
const suite = new privateUnderTest.Suite({
id: 'suite1',
parentSuite: topSuite,
timer
});
topSuite.addChild(suite);
const executionTree = {
topSuite,
childrenOfTopSuite() {
return [{ suite }];
},
childrenOfSuite() {
return [];
},
isExcluded() {
return false;
}
};
const runQueue = jasmine.createSpy('runQueue');
const reportDispatcher = mockReportDispatcher();
const subject = new privateUnderTest.TreeRunner({
executionTree,
runQueue,
globalErrors: mockGlobalErrors(),
runableResources: mockRunableResources(),
reportDispatcher,
currentRunableTracker: new privateUnderTest.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 privateUnderTest.Suite({ id: 'topSuite' });
const failingSuite = new privateUnderTest.Suite({
id: 'failingSuite',
parentSuite: topSuite
});
const passingSuite = new privateUnderTest.Suite({
id: 'passingSuite',
parentSuite: topSuite
});
const executionTree = {
topSuite,
childrenOfTopSuite() {
return [{ suite: failingSuite }, { suite: passingSuite }];
},
childrenOfSuite() {
return [];
},
isExcluded() {
return false;
}
};
const runQueue = jasmine.createSpy('runQueue');
const reportDispatcher = mockReportDispatcher();
const subject = new privateUnderTest.TreeRunner({
executionTree,
runQueue,
globalErrors: mockGlobalErrors(),
runableResources: mockRunableResources(),
reportDispatcher,
currentRunableTracker: new privateUnderTest.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 privateUnderTest.Suite({ id: 'topSuite' });
const suite = new privateUnderTest.Suite({
id: 'suite',
parentSuite: topSuite
});
suite.beforeAll({ fn() {} });
const spec = new privateUnderTest.Spec({
id: 'spec',
parentSuite: suite,
queueableFn: { fn() {} }
});
suite.addChild(spec);
topSuite.addChild(suite);
const executionTree = {
topSuite,
childrenOfTopSuite() {
return [{ suite }];
},
childrenOfSuite() {
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 privateUnderTest.TreeRunner({
executionTree,
runQueue,
globalErrors: mockGlobalErrors(),
runableResources: mockRunableResources(),
reportDispatcher,
currentRunableTracker: new privateUnderTest.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 privateUnderTest.Suite({ id: 'topSuite' });
const suite = new privateUnderTest.Suite({
id: 'suite',
parentSuite: topSuite
});
const spec = new privateUnderTest.Spec({
id: 'spec',
parentSuite: suite,
queueableFn: { fn() {} }
});
const executionTree = {
topSuite,
childrenOfTopSuite() {
return [{ suite }];
},
childrenOfSuite() {
return [{ spec }];
},
isExcluded() {
return false;
}
};
const runQueue = jasmine.createSpy('runQueue');
const reportDispatcher = mockReportDispatcher();
const subject = new privateUnderTest.TreeRunner({
executionTree,
runQueue,
globalErrors: mockGlobalErrors(),
runableResources: mockRunableResources(),
reportDispatcher,
currentRunableTracker: new privateUnderTest.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();
});
});
it('does not remove before and after fns from the top suite', async function() {
const topSuite = new privateUnderTest.Suite({ id: 'topSuite' });
spyOn(topSuite, 'cleanupBeforeAfter');
const executionTree = {
topSuite,
childrenOfTopSuite() {
return [];
},
isExcluded() {
return false;
}
};
const runQueue = jasmine.createSpy('runQueue');
const subject = new privateUnderTest.TreeRunner({
executionTree,
runQueue,
globalErrors: mockGlobalErrors(),
runableResources: mockRunableResources(),
reportDispatcher: mockReportDispatcher(),
currentRunableTracker: new privateUnderTest.CurrentRunableTracker(),
getConfig() {
return {};
}
});
const executePromise = subject.execute();
expect(runQueue).toHaveBeenCalledTimes(1);
const topSuiteRunQueueOpts = runQueue.calls.mostRecent().args[0];
runQueue.calls.reset();
for (const qfn of topSuiteRunQueueOpts.queueableFns) {
qfn.fn();
}
topSuiteRunQueueOpts.onComplete();
await expectAsync(executePromise).toBeResolved();
expect(topSuite.cleanupBeforeAfter).not.toHaveBeenCalled();
});
describe('Late promise rejection handling', function() {
it('works for specs 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 privateUnderTest.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
);
});
it('works for beforeAll when the detectLateRejectionHandling param is true', async function() {
const topSuite = new privateUnderTest.Suite({ id: 'topSuite' });
const suite = new privateUnderTest.Suite({
id: 'suite',
parentSuite: topSuite
});
suite.beforeAll(function() {});
const spec = new privateUnderTest.Spec({
queueableFn: { fn() {} },
parentSuite: suite
});
const executionTree = {
topSuite,
childrenOfTopSuite() {
return [{ suite }];
},
childrenOfSuite() {
return [{ spec }];
},
isExcluded() {
return false;
}
};
const runQueue = jasmine.createSpy('runQueue');
const reportDispatcher = mockReportDispatcher();
const globalErrors = mockGlobalErrors();
const setTimeout = jasmine.createSpy('setTimeout');
const subject = new privateUnderTest.TreeRunner({
executionTree,
runQueue,
globalErrors,
runableResources: mockRunableResources(),
reportDispatcher,
setTimeout,
currentRunableTracker: new privateUnderTest.CurrentRunableTracker(),
getConfig() {
return { detectLateRejectionHandling: true };
},
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];
expect(suiteRunQueueOpts.queueableFns).toEqual([
{ fn: jasmine.any(Function) }, // onStart
jasmine.objectContaining({ type: 'beforeAll' }),
{ fn: jasmine.any(Function) }, // detect late rejection handling
{ fn: jasmine.any(Function) } // spec
]);
suiteRunQueueOpts.queueableFns[0].fn();
const done = jasmine.createSpy('done');
suiteRunQueueOpts.queueableFns[2].fn(done);
expect(globalErrors.reportUnhandledRejections).not.toHaveBeenCalled();
expect(setTimeout).toHaveBeenCalledOnceWith(jasmine.any(Function));
setTimeout.calls.argsFor(0)[0]();
expect(globalErrors.reportUnhandledRejections).toHaveBeenCalledBefore(
done
);
await expectAsync(executePromise).toBePending();
});
it('works for afterAll when the detectLateRejectionHandling param is true', async function() {
const topSuite = new privateUnderTest.Suite({ id: 'topSuite' });
const suite = new privateUnderTest.Suite({
id: 'suite',
parentSuite: topSuite
});
suite.afterAll(function() {});
const spec = new privateUnderTest.Spec({
queueableFn: { fn() {} },
parentSuite: suite
});
const executionTree = {
topSuite,
childrenOfTopSuite() {
return [{ suite }];
},
childrenOfSuite() {
return [{ spec }];
},
isExcluded() {
return false;
}
};
const runQueue = jasmine.createSpy('runQueue');
const reportDispatcher = mockReportDispatcher();
const globalErrors = mockGlobalErrors();
const setTimeout = jasmine.createSpy('setTimeout');
const subject = new privateUnderTest.TreeRunner({
executionTree,
runQueue,
globalErrors,
runableResources: mockRunableResources(),
reportDispatcher,
setTimeout,
currentRunableTracker: new privateUnderTest.CurrentRunableTracker(),
getConfig() {
return { detectLateRejectionHandling: true };
},
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];
expect(suiteRunQueueOpts.queueableFns).toEqual([
{ fn: jasmine.any(Function) }, // onStart
{ fn: jasmine.any(Function) }, // spec
jasmine.objectContaining({ type: 'afterAll' }),
{ fn: jasmine.any(Function) } // detect late rejection handling
]);
suiteRunQueueOpts.queueableFns[0].fn();
const done = jasmine.createSpy('done');
suiteRunQueueOpts.queueableFns[3].fn(done);
expect(globalErrors.reportUnhandledRejections).not.toHaveBeenCalled();
expect(setTimeout).toHaveBeenCalledOnceWith(jasmine.any(Function));
setTimeout.calls.argsFor(0)[0]();
expect(globalErrors.reportUnhandledRejections).toHaveBeenCalledBefore(
done
);
await expectAsync(executePromise).toBePending();
});
});
function runSingleSpecSuite(spec, optionalConfig) {
const topSuiteId = 'suite1';
spec.parentSuiteId = topSuiteId;
const topSuite = new privateUnderTest.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 privateUnderTest.CurrentRunableTracker();
const subject = new privateUnderTest.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
};
}
function mockReportDispatcher() {
const reportDispatcher = jasmine.createSpyObj(
'reportDispatcher',
privateUnderTest.reporterEvents
);
for (const k of privateUnderTest.reporterEvents) {
reportDispatcher[k].and.returnValue(Promise.resolve());
}
return reportDispatcher;
}
function mockRunableResources() {
return jasmine.createSpyObj('runableResources', [
'initForRunable',
'clearForRunable'
]);
}
function mockGlobalErrors() {
return jasmine.createSpyObj('globalErrors', ['reportUnhandledRejections']);
}
});

View File

@@ -1,6 +1,6 @@
describe('UserContext', function() {
it('Behaves just like an plain object', function() {
const context = new jasmineUnderTest.UserContext(),
const context = new privateUnderTest.UserContext(),
properties = [];
for (const prop in context) {
@@ -15,9 +15,9 @@ describe('UserContext', function() {
describe('.fromExisting', function() {
describe('when using an already built context as model', function() {
beforeEach(function() {
this.context = new jasmineUnderTest.UserContext();
this.context = new privateUnderTest.UserContext();
this.context.key = 'value';
this.cloned = jasmineUnderTest.UserContext.fromExisting(this.context);
this.cloned = privateUnderTest.UserContext.fromExisting(this.context);
});
it('returns a cloned object', function() {
@@ -34,7 +34,7 @@ describe('UserContext', function() {
this.context = {};
this.value = 'value';
this.context.key = this.value;
this.cloned = jasmineUnderTest.UserContext.fromExisting(this.context);
this.cloned = privateUnderTest.UserContext.fromExisting(this.context);
});
it('returns an object with the same attributes', function() {
@@ -46,7 +46,7 @@ describe('UserContext', function() {
});
it('returns an UserContext', function() {
expect(this.cloned.constructor).toBe(jasmineUnderTest.UserContext);
expect(this.cloned.constructor).toBe(privateUnderTest.UserContext);
});
});
});

View File

@@ -1,33 +1,33 @@
describe('util', function() {
describe('isArray_', function() {
describe('isArray', function() {
it('should return true if the argument is an array', function() {
expect(jasmineUnderTest.isArray_([])).toBe(true);
expect(jasmineUnderTest.isArray_(['a'])).toBe(true);
expect(privateUnderTest.isArray([])).toBe(true);
expect(privateUnderTest.isArray(['a'])).toBe(true);
});
it('should return false if the argument is not an array', function() {
expect(jasmineUnderTest.isArray_(undefined)).toBe(false);
expect(jasmineUnderTest.isArray_({})).toBe(false);
expect(jasmineUnderTest.isArray_(function() {})).toBe(false);
expect(jasmineUnderTest.isArray_('foo')).toBe(false);
expect(jasmineUnderTest.isArray_(5)).toBe(false);
expect(jasmineUnderTest.isArray_(null)).toBe(false);
expect(privateUnderTest.isArray(undefined)).toBe(false);
expect(privateUnderTest.isArray({})).toBe(false);
expect(privateUnderTest.isArray(function() {})).toBe(false);
expect(privateUnderTest.isArray('foo')).toBe(false);
expect(privateUnderTest.isArray(5)).toBe(false);
expect(privateUnderTest.isArray(null)).toBe(false);
});
});
describe('isObject_', function() {
describe('isObject', function() {
it('should return true if the argument is an object', function() {
expect(jasmineUnderTest.isObject_({})).toBe(true);
expect(jasmineUnderTest.isObject_({ an: 'object' })).toBe(true);
expect(privateUnderTest.isObject({})).toBe(true);
expect(privateUnderTest.isObject({ an: 'object' })).toBe(true);
});
it('should return false if the argument is not an object', function() {
expect(jasmineUnderTest.isObject_(undefined)).toBe(false);
expect(jasmineUnderTest.isObject_([])).toBe(false);
expect(jasmineUnderTest.isObject_(function() {})).toBe(false);
expect(jasmineUnderTest.isObject_('foo')).toBe(false);
expect(jasmineUnderTest.isObject_(5)).toBe(false);
expect(jasmineUnderTest.isObject_(null)).toBe(false);
expect(privateUnderTest.isObject(undefined)).toBe(false);
expect(privateUnderTest.isObject([])).toBe(false);
expect(privateUnderTest.isObject(function() {})).toBe(false);
expect(privateUnderTest.isObject('foo')).toBe(false);
expect(privateUnderTest.isObject(5)).toBe(false);
expect(privateUnderTest.isObject(null)).toBe(false);
});
});
@@ -45,130 +45,121 @@ describe('util', function() {
describe('isPromise', function() {
it('should return true when passed a native promise', function() {
expect(jasmineUnderTest.isPromise(mockNativePromise)).toBe(true);
expect(privateUnderTest.isPromise(mockNativePromise)).toBe(true);
});
it('should return false for promise like objects', function() {
expect(jasmineUnderTest.isPromise(mockPromiseLikeObject)).toBe(false);
expect(privateUnderTest.isPromise(mockPromiseLikeObject)).toBe(false);
});
it('should return false for strings', function() {
expect(jasmineUnderTest.isPromise('hello')).toBe(false);
expect(privateUnderTest.isPromise('hello')).toBe(false);
});
it('should return false for numbers', function() {
expect(jasmineUnderTest.isPromise(3)).toBe(false);
expect(privateUnderTest.isPromise(3)).toBe(false);
});
it('should return false for null', function() {
expect(jasmineUnderTest.isPromise(null)).toBe(false);
expect(privateUnderTest.isPromise(null)).toBe(false);
});
it('should return false for undefined', function() {
expect(jasmineUnderTest.isPromise(undefined)).toBe(false);
expect(privateUnderTest.isPromise(undefined)).toBe(false);
});
it('should return false for arrays', function() {
expect(jasmineUnderTest.isPromise([])).toBe(false);
expect(privateUnderTest.isPromise([])).toBe(false);
});
it('should return false for objects', function() {
expect(jasmineUnderTest.isPromise({})).toBe(false);
expect(privateUnderTest.isPromise({})).toBe(false);
});
it('should return false for boolean values', function() {
expect(jasmineUnderTest.isPromise(true)).toBe(false);
expect(privateUnderTest.isPromise(true)).toBe(false);
});
});
describe('isPromiseLike', function() {
it('should return true when passed a native promise', function() {
expect(jasmineUnderTest.isPromiseLike(mockNativePromise)).toBe(true);
expect(privateUnderTest.isPromiseLike(mockNativePromise)).toBe(true);
});
it('should return true for promise like objects', function() {
expect(jasmineUnderTest.isPromiseLike(mockPromiseLikeObject)).toBe(
expect(privateUnderTest.isPromiseLike(mockPromiseLikeObject)).toBe(
true
);
});
it('should return false if then is not a function', function() {
expect(
jasmineUnderTest.isPromiseLike({ then: { its: 'Not a function :O' } })
privateUnderTest.isPromiseLike({
then: { its: 'Not a function :O' }
})
).toBe(false);
});
it('should return false for strings', function() {
expect(jasmineUnderTest.isPromiseLike('hello')).toBe(false);
expect(privateUnderTest.isPromiseLike('hello')).toBe(false);
});
it('should return false for numbers', function() {
expect(jasmineUnderTest.isPromiseLike(3)).toBe(false);
expect(privateUnderTest.isPromiseLike(3)).toBe(false);
});
it('should return false for null', function() {
expect(jasmineUnderTest.isPromiseLike(null)).toBe(false);
expect(privateUnderTest.isPromiseLike(null)).toBe(false);
});
it('should return false for undefined', function() {
expect(jasmineUnderTest.isPromiseLike(undefined)).toBe(false);
expect(privateUnderTest.isPromiseLike(undefined)).toBe(false);
});
it('should return false for arrays', function() {
expect(jasmineUnderTest.isPromiseLike([])).toBe(false);
expect(privateUnderTest.isPromiseLike([])).toBe(false);
});
it('should return false for objects', function() {
expect(jasmineUnderTest.isPromiseLike({})).toBe(false);
expect(privateUnderTest.isPromiseLike({})).toBe(false);
});
it('should return false for boolean values', function() {
expect(jasmineUnderTest.isPromiseLike(true)).toBe(false);
expect(privateUnderTest.isPromiseLike(true)).toBe(false);
});
});
});
describe('isUndefined', function() {
it('reports if a variable is defined', function() {
let a;
expect(jasmineUnderTest.util.isUndefined(a)).toBe(true);
expect(jasmineUnderTest.util.isUndefined(undefined)).toBe(true);
const defined = 'diz be undefined yo';
expect(jasmineUnderTest.util.isUndefined(defined)).toBe(false);
});
});
describe('cloneArgs', function() {
it('clones primitives as-is', function() {
expect(jasmineUnderTest.util.cloneArgs([true, false])).toEqual([
expect(privateUnderTest.util.cloneArgs([true, false])).toEqual([
true,
false
]);
expect(jasmineUnderTest.util.cloneArgs([0, 1])).toEqual([0, 1]);
expect(jasmineUnderTest.util.cloneArgs(['str'])).toEqual(['str']);
expect(privateUnderTest.util.cloneArgs([0, 1])).toEqual([0, 1]);
expect(privateUnderTest.util.cloneArgs(['str'])).toEqual(['str']);
});
it('clones Regexp objects as-is', function() {
const regex = /match/;
expect(jasmineUnderTest.util.cloneArgs([regex])).toEqual([regex]);
expect(privateUnderTest.util.cloneArgs([regex])).toEqual([regex]);
});
it('clones Date objects as-is', function() {
const date = new Date(2022, 1, 1);
expect(jasmineUnderTest.util.cloneArgs([date])).toEqual([date]);
expect(privateUnderTest.util.cloneArgs([date])).toEqual([date]);
});
it('clones null and undefined', function() {
expect(jasmineUnderTest.util.cloneArgs([null])).toEqual([null]);
expect(jasmineUnderTest.util.cloneArgs([undefined])).toEqual([undefined]);
expect(privateUnderTest.util.cloneArgs([null])).toEqual([null]);
expect(privateUnderTest.util.cloneArgs([undefined])).toEqual([undefined]);
});
});
describe('getPropertyDescriptor', function() {
it('get property descriptor from object', function() {
const obj = { prop: 1 },
actual = jasmineUnderTest.util.getPropertyDescriptor(obj, 'prop'),
actual = privateUnderTest.util.getPropertyDescriptor(obj, 'prop'),
expected = Object.getOwnPropertyDescriptor(obj, 'prop');
expect(actual).toEqual(expected);
@@ -176,7 +167,7 @@ describe('util', function() {
it('get property descriptor from object property', function() {
const proto = { prop: 1 },
actual = jasmineUnderTest.util.getPropertyDescriptor(proto, 'prop'),
actual = privateUnderTest.util.getPropertyDescriptor(proto, 'prop'),
expected = Object.getOwnPropertyDescriptor(proto, 'prop');
expect(actual).toEqual(expected);
@@ -187,8 +178,8 @@ describe('util', function() {
it('returns the file containing jasmine.util', function() {
// Chrome sometimes reports foo.js as foo.js/, so tolerate
// a trailing slash if present.
expect(jasmineUnderTest.util.jasmineFile()).toMatch(/util.js\/?$/);
expect(jasmine.util.jasmineFile()).toMatch(/jasmine.js\/?$/);
expect(privateUnderTest.util.jasmineFile()).toMatch(/util.js\/?$/);
expect(jasmine.private.util.jasmineFile()).toMatch(/jasmine.js\/?$/);
});
});
});

View File

@@ -1,73 +1,73 @@
describe('Any', function() {
it('matches a string', function() {
const any = new jasmineUnderTest.Any(String);
const any = new privateUnderTest.Any(String);
expect(any.asymmetricMatch('foo')).toBe(true);
});
it('matches a number', function() {
const any = new jasmineUnderTest.Any(Number);
const any = new privateUnderTest.Any(Number);
expect(any.asymmetricMatch(1)).toBe(true);
});
it('matches a function', function() {
const any = new jasmineUnderTest.Any(Function);
const any = new privateUnderTest.Any(Function);
expect(any.asymmetricMatch(function() {})).toBe(true);
});
it('matches an Object', function() {
const any = new jasmineUnderTest.Any(Object);
const any = new privateUnderTest.Any(Object);
expect(any.asymmetricMatch({})).toBe(true);
});
it('matches a Boolean', function() {
const any = new jasmineUnderTest.Any(Boolean);
const any = new privateUnderTest.Any(Boolean);
expect(any.asymmetricMatch(true)).toBe(true);
});
it('matches a Map', function() {
const any = new jasmineUnderTest.Any(Map);
const any = new privateUnderTest.Any(Map);
expect(any.asymmetricMatch(new Map())).toBe(true);
});
it('matches a Set', function() {
const any = new jasmineUnderTest.Any(Set);
const any = new privateUnderTest.Any(Set);
expect(any.asymmetricMatch(new Set())).toBe(true);
});
it('matches a TypedArray', function() {
const any = new jasmineUnderTest.Any(Uint32Array);
const any = new privateUnderTest.Any(Uint32Array);
expect(any.asymmetricMatch(new Uint32Array([]))).toBe(true);
});
it('matches a Symbol', function() {
const any = new jasmineUnderTest.Any(Symbol);
const any = new privateUnderTest.Any(Symbol);
expect(any.asymmetricMatch(Symbol())).toBe(true);
});
it('matches another constructed object', function() {
const Thing = function() {},
any = new jasmineUnderTest.Any(Thing);
any = new privateUnderTest.Any(Thing);
expect(any.asymmetricMatch(new Thing())).toBe(true);
});
it('does not treat null as an Object', function() {
const any = new jasmineUnderTest.Any(Object);
const any = new privateUnderTest.Any(Object);
expect(any.asymmetricMatch(null)).toBe(false);
});
it("jasmineToString's itself", function() {
const any = new jasmineUnderTest.Any(Number);
const any = new privateUnderTest.Any(Number);
expect(any.jasmineToString()).toEqual('<jasmine.any(Number)>');
expect(any.jasmineToString()).toEqual('<jasmine.any(Number)>');
@@ -76,7 +76,7 @@ describe('Any', function() {
describe('when called without an argument', function() {
it('tells the user to pass a constructor or use jasmine.anything()', function() {
expect(function() {
new jasmineUnderTest.Any();
new privateUnderTest.Any();
}).toThrowError(TypeError, /constructor.*anything/);
});
});

View File

@@ -1,67 +1,67 @@
describe('Anything', function() {
it('matches a string', function() {
const anything = new jasmineUnderTest.Anything();
const anything = new privateUnderTest.Anything();
expect(anything.asymmetricMatch('foo')).toBe(true);
});
it('matches a number', function() {
const anything = new jasmineUnderTest.Anything();
const anything = new privateUnderTest.Anything();
expect(anything.asymmetricMatch(42)).toBe(true);
});
it('matches an object', function() {
const anything = new jasmineUnderTest.Anything();
const anything = new privateUnderTest.Anything();
expect(anything.asymmetricMatch({ foo: 'bar' })).toBe(true);
});
it('matches an array', function() {
const anything = new jasmineUnderTest.Anything();
const anything = new privateUnderTest.Anything();
expect(anything.asymmetricMatch([1, 2, 3])).toBe(true);
});
it('matches a Map', function() {
const anything = new jasmineUnderTest.Anything();
const anything = new privateUnderTest.Anything();
expect(anything.asymmetricMatch(new Map())).toBe(true);
});
it('matches a Set', function() {
const anything = new jasmineUnderTest.Anything();
const anything = new privateUnderTest.Anything();
expect(anything.asymmetricMatch(new Set())).toBe(true);
});
it('matches a TypedArray', function() {
const anything = new jasmineUnderTest.Anything();
const anything = new privateUnderTest.Anything();
expect(anything.asymmetricMatch(new Uint32Array([]))).toBe(true);
});
it('matches a Symbol', function() {
const anything = new jasmineUnderTest.Anything();
const anything = new privateUnderTest.Anything();
expect(anything.asymmetricMatch(Symbol())).toBe(true);
});
it("doesn't match undefined", function() {
const anything = new jasmineUnderTest.Anything();
const anything = new privateUnderTest.Anything();
expect(anything.asymmetricMatch()).toBe(false);
expect(anything.asymmetricMatch(undefined)).toBe(false);
});
it("doesn't match null", function() {
const anything = new jasmineUnderTest.Anything();
const anything = new privateUnderTest.Anything();
expect(anything.asymmetricMatch(null)).toBe(false);
});
it("jasmineToString's itself", function() {
const anything = new jasmineUnderTest.Anything();
const anything = new privateUnderTest.Anything();
expect(anything.jasmineToString()).toEqual('<jasmine.anything>');
});

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