Compare commits

...

332 Commits

Author SHA1 Message Date
Steve Gravrock
9f0488dc32 Bump version to 6.0.0-beta.1
Some checks are pending
Test in latest available Safari / build (push) Waiting to run
2025-12-06 10:56:56 -08:00
Steve Gravrock
f3dba82b04 Revert to using window.onload
To support top level await, jasmine-browser-runner needs to be able
to delay env execution until after spec files have initialized. The
old-fashioned event listener style makes that straightforward.
2025-12-01 18:33:08 -08:00
Steve Gravrock
c999ce0787 Update some dev dependencies 2025-12-01 17:57:37 -08:00
Steve Gravrock
5b76bf9552 Merge branch '6.0' 2025-12-01 17:48:49 -08:00
Steve Gravrock
9cf9b856b0 Bump version to 5.13.0
Some checks are pending
Test in latest available Safari / build (push) Waiting to run
2025-12-01 17:25:03 -08:00
Steve Gravrock
db6c142afd Copy 6.0.0-beta.0 release notes from branch 2025-11-28 11:49:05 -08:00
Steve Gravrock
79405426fa Bump version to 6.0.0-beta.0
Some checks are pending
Test in latest available Safari / build (push) Waiting to run
2025-11-28 11:35:46 -08:00
Steve Gravrock
00b09a9a04 Simplify HtmlReporterV2 initialization and boot1.js 2025-11-28 09:47:31 -08:00
Steve Gravrock
f5e9b61f73 Replace isArray helper with native Array.isArray 2025-11-28 08:09:51 -08:00
Steve Gravrock
4371081763 Deprecate jsApiReporter and remove it from boot1.js
jsApiReporter was initially added as part of the pre-1.0 Ruby based browser
runner. It looks like it was designed to resolve a race condition betweeen
jasmine-core's startup in the browser and the Ruby runner's startup. Modern
runners handle that either by buffering messages in a custom reporter (e.g.
jasmine-browser-runner's BatchReporter) or by calling env.execute() after a
communication channel has been set up (e.g. the old Jasmine ruby gem). In
any other context, a custom reporter is easier to use than jsApiReporter
because it doesn't require polling.

Adding jsApiReporter to the env imposes small but measurable penalties in
time and space, both of which are proportional to the size of the test
suite.

Other than jasmine-py and Testdouble's jasmine-rails gem, neither of which
ever supported jasmine-core 4 or later, I can find scant evidence of
interest and no evidence of usage after about 2012.
2025-11-28 08:08:50 -08:00
Steve Gravrock
9530ff68ab Fix event listener leaks in own specs 2025-11-28 07:13:52 -08:00
Steve Gravrock
51dc79dc22 rm dead code 2025-11-27 08:45:24 -08:00
Steve Gravrock
b559faec2a HtmlReporterV2: show correct progress when running a subset of specs 2025-11-27 07:24:45 -08:00
Steve Gravrock
d7b1456584 Document the set of possible spec statuses 2025-11-27 07:24:44 -08:00
Steve Gravrock
23894c1a0a Detect monkey patching and emit a deprecation warning.
This isn't comprehensive but it should be broad enough to ensure that most
people who would be affected by blocking monkey patching see a warning.
Covers the jasmine namespace as well as classes that are monkey patched by
zone.js.

Replacing globals (describe/it/etc) doesn't trigger a warning because they
belong to the user and are expected to be replaced.
2025-11-27 07:23:57 -08:00
Steve Gravrock
1e691b2470 Prettier 2025-11-27 06:53:27 -08:00
Steve Gravrock
c5555dd8cc Better debug logging for spec that occasionally fails in FF 2025-11-27 06:30:09 -08:00
Steve Gravrock
32168be6c7 Statically expose pretty printer as jasmine.pp
Pretty printing is occasionally useful outside of the places where a
configured pretty printer is injected (matchers and asymmetric equality
testers). Users sometimes use the private basicPrettyPrinter for that.
jasmine.pp is part of the public interface and uses the current runable's
custom object formatters.
2025-11-22 09:46:45 -08:00
Steve Gravrock
78c14f81a8 Copy 6.0.0-alpha.2 release notes from branch 2025-11-15 14:40:39 -08:00
Steve Gravrock
788eba34b6 Bump version to 6.0.0-alpha.2
Some checks are pending
Test in latest available Safari / build (push) Waiting to run
2025-11-15 10:22:27 -08:00
Steve Gravrock
1f31b4b0f6 Increase font size in HTML reporters 2025-11-15 09:06:19 -08:00
Steve Gravrock
00a8a11904 Merge branch 'slow-reporter' into 6.0 2025-11-15 09:01:44 -08:00
Steve Gravrock
3899d83fb6 HtmlReporterV2: show median and mean spec run time 2025-11-15 09:01:06 -08:00
Steve Gravrock
8f13684a01 Add a slowest specs list to HTMLReporterV2 2025-11-15 08:57:25 -08:00
Steve Gravrock
bdf63f2402 Remove code to support browsers that don't have MessageChannel
Jasmine hasn't actually run in any such browsers since 2.x.
2025-11-12 21:59:17 -08:00
Steve Gravrock
9c2ffae2f9 Add experimental safariYieldStrategy: "time" config option
This greatly improves speed, at least in jasmine-core's own tests.
2025-11-12 21:08:59 -08:00
Steve Gravrock
7b2807b321 Convert clearStack from a function to an object 2025-11-11 18:54:25 -08:00
Steve Gravrock
e930622548 Merge branch 'main' into 6.0 2025-11-11 09:14:14 -08:00
Steve Gravrock
56e2832ebe Add manual and cron triggers to Safari build 2025-11-11 09:13:34 -08:00
Steve Gravrock
d31d33aeb3 Introduce a tab bar
This will make it easier to add a third tab to HtmlReporterV2.
2025-11-09 09:58:57 -08:00
Steve Gravrock
e4c69e960e Add 'use strict' to AllOf.js 2025-11-04 05:52:45 -08:00
Steve Gravrock
a8431f33bd Merge branch '5.99' into 6.0 2025-11-03 17:22:11 -08:00
Steve Gravrock
4995c967ac Merge branch 'main' into 5.99 2025-11-03 17:14:24 -08:00
Steve Gravrock
9a9d3994da Add Safari 26 to supported browsers 2025-11-03 07:37:11 -08:00
Steve Gravrock
ff9feb29d3 Configurable spec/suite filename detection
* Adds extraItStackFrames and extraDescribeStackFrames config properties.
* Un-deprecates the filename properties of reporter events.
* Fixes #2065.
2025-11-01 14:17:01 -07:00
Steve Gravrock
fee7e6e64e Merge branch 'jonahd-g-main'
* Adds jasmine.allOf asymmetric equality tester
* Merges #2087 from @jonahd-g
* Fixes #2083
2025-11-01 09:01:53 -07:00
Steve Gravrock
18d4d38655 Fix version number in 5.12.1 release notes 2025-10-29 19:55:00 -07:00
Steve Gravrock
53e9bc68d2 Bump version to 5.12.1 2025-10-29 19:53:34 -07:00
Steve Gravrock
2be50e1b87 Merge branch 'bonkevin-fix-custom-matcher'
* Fixes custom matchers in top-level specs
* Merges #2088 from @bonkevin
2025-10-29 19:44:06 -07:00
bonkevin
27a1257b6d fix: unavailable custom matchers on top-it 2025-10-29 13:04:10 -04:00
Jonah Bron
75658e0566 jasmine.allOf AsymmetricEqualityTester
New asymmetric equality tester that accepts a variable number of arguments, and will pass if all of them evaluate as being equal to the input value.
Includes unit tests
2025-10-27 10:10:16 -07:00
Steve Gravrock
85322d1877 Re-add support for partial spec name filtering
No UI for this but users can construct URLs manually.
Fixes #2085.
2025-10-24 17:26:49 -07:00
Steve Gravrock
6667a42301 Docs: Fix HtmlReporterV2 ctor example 2025-10-22 16:36:58 -07:00
Steve Gravrock
020dffd504 Don't spy on getGlobal 2025-10-19 10:08:05 -07:00
Steve Gravrock
4201fd848f Require spec/suite property keys to be strings, not just anything that's cloneable and serializable
This matches the jsdoc.
2025-10-19 09:15:12 -07:00
Steve Gravrock
9a67c4e24d Copy 6.0.0-alpha.1 release notes from branch 2025-10-18 13:05:29 -07:00
Steve Gravrock
d66d0d9d2e Fixed standalone distribution 2025-10-18 12:31:48 -07:00
Steve Gravrock
f4b9c889b9 Bump version to 6.0.0-alpha.1 2025-10-18 11:58:17 -07:00
Steve Gravrock
388d7fb1a2 Merge branch '5.99' into 6.0 2025-10-18 10:23:37 -07:00
Steve Gravrock
e993a4a363 Merge branch 'main' into 5.99 2025-10-18 10:21:13 -07:00
Steve Gravrock
54ac39a192 Fix position of duration in both HTML reporters 2025-10-18 09:58:55 -07:00
Steve Gravrock
ea882c2f1e HtmlReporterV2: Show a non-color indication of status while running 2025-10-18 09:46:16 -07:00
Steve Gravrock
4cc605756a Extract an OverallStatusBar widget 2025-10-17 07:58:01 -07:00
Steve Gravrock
b6426d2414 Flesh out jasmine-html.js API reference 2025-10-15 21:50:56 -07:00
Steve Gravrock
2352249441 Clean up progress bar CSS 2025-10-15 19:42:46 -07:00
Steve Gravrock
4dfc34a7a0 Make AlertsView#add private 2025-10-15 19:37:18 -07:00
Steve Gravrock
695a805844 Backfill missing HTML reporter tests 2025-10-15 19:32:47 -07:00
Steve Gravrock
10bc655622 HtmlReporterV2: Show details of failures as soon as they occur 2025-10-14 21:49:33 -07:00
Steve Gravrock
4663280528 Move state tracking out of FailuresView 2025-10-14 21:46:07 -07:00
Steve Gravrock
75347d9ba0 Removed slow test for long-removed IE specific code 2025-10-09 16:42:42 -07:00
Steve Gravrock
86387c9068 HtmlReporterV2: replace dots with progress bar 2025-10-09 16:42:42 -07:00
Steve Gravrock
9b3cc08818 Deprecate HtmlReporter and HtmlSpecFilter 2025-10-09 16:42:42 -07:00
Steve Gravrock
0ad54fc6f0 Clicking a link in HtmlReporterV2 does exact filtering 2025-10-09 16:42:42 -07:00
Steve Gravrock
c042665d9c HtmlReporterV2 doesn't need specStarted 2025-10-09 16:42:42 -07:00
Steve Gravrock
a457cf1b81 Simplify boot1.js 2025-10-09 16:42:42 -07:00
Steve Gravrock
fb814b5f94 Refactor HtmlReporterV2 test setup 2025-10-09 16:42:42 -07:00
Steve Gravrock
77c3b8b07e Clone HtmlSpecFilter and HtmlReporter in preparation for backward-incompatible changes 2025-10-09 16:42:42 -07:00
Steve Gravrock
bd89ef66c8 Move HtmlReporter components to their own files 2025-10-09 16:42:42 -07:00
Steve Gravrock
01f050eeaa Turn DomContext class back into a function 2025-10-09 16:42:42 -07:00
Steve Gravrock
de44e909f2 Ignore HtmlReporter's createElement and createTextNode options
Injected DOM wrappers were a nice idea in theory but everyone just passes
wrappers around document.createElement/document.createTextNode. That
includes HtmlReporter's unit tests and karma-jasmine-html-reporter, the
only known 5.x-compatible library that constructs an HtmlReporter.
2025-10-09 16:42:42 -07:00
Steve Gravrock
10ad40357a HTML reporter: cache configuration throughout each run 2025-10-09 16:42:42 -07:00
Steve Gravrock
1f521f2a7f Convert what's left of HtmlReporter to an ES6 class 2025-10-09 16:42:42 -07:00
Steve Gravrock
5b3e12e4c5 Decompose HtmlReporter into smaller components 2025-10-09 16:42:37 -07:00
Steve Gravrock
7ba53b25f7 Bump version to 5.12.0 2025-10-05 12:02:56 -07:00
Steve Gravrock
cfd8f11b30 Merge branch '5.99' into 6.0 2025-10-05 10:08:40 -07:00
Steve Gravrock
d95ebf303a Merge branch 'main' into 5.99 2025-10-05 10:04:05 -07:00
Steve Gravrock
dbc1f9244e Revert "Clicking a link in the HTML reporter does exact filtering"
This change broke spec filtering in Karma by changing the format of the
`spec` query parameter. Although karma-jasmine-html-reporter uses
jasmine-core's HtmlSpecFilter, karm-jasmine provides its own spec filter
that interprets the query parameters itself.

This feature may be reintroduced in 6.0 as a breaking change.

This reverts commit 8309416cb2.
2025-10-05 09:59:36 -07:00
Steve Gravrock
489b83c61b Revert "Move knowledge of query parameters out of boot1.js"
This reverts commit 6715f24fd0.
2025-10-05 09:54:25 -07:00
Steve Gravrock
b881b0077d Warn if jasmine-core is loaded as an ES module in browsers 2025-10-05 09:03:27 -07:00
Steve Gravrock
67ef721c85 Fix startup crash in Karma
The previous commit left one code path un-converted to globalThis. That
exposed a bug in Karma: Karma loads jasmine-core via <script type="module">
even though it's not an ES module. In ES modules, the value of `this`
outside of a method is undefined rather than window. The Angular dev tools
try to work around that bug by monkey-patching window to look like GJS's
window object, which worked until the GJS hack was removed in the previous
commit.
2025-10-05 09:00:37 -07:00
Steve Gravrock
c4abf3265d Use globalThis to determine the global object during initialization
This slightly simplifies the init code and hardens Jasmine against broken
bundlers that assume everything can be wrapped in a "use strict" context.

This removes a workaround for incompatible `this` behavior in GJS. GJS was
never a supported envronment, but in any case the change is unlikely to
cause problems since GJS ha supported globalThis since 2020.
2025-10-05 07:23:54 -07:00
Steve Gravrock
68a7cbb991 Adopt strict mode throughout the codebase 2025-10-05 06:53:54 -07:00
Steve Gravrock
73a30ffc3e Made SpyRegistry compatible with strict mode 2025-10-05 06:53:39 -07:00
Steve Gravrock
18491e9b84 Encapsulate suite result 2025-10-05 06:19:16 -07:00
Steve Gravrock
0738ba6462 Omit irrelevant properties from suiteStarted 2025-10-05 06:16:57 -07:00
Steve Gravrock
712f9bac29 Encapsulate spec result 2025-10-05 06:01:27 -07:00
Steve Gravrock
d99bc3ab58 Encapsulate spec status 2025-10-05 06:01:27 -07:00
Steve Gravrock
418393c496 rm more vestiges of suite re-entry support 2025-10-05 06:01:27 -07:00
Steve Gravrock
2a83f5cc30 Don't mutate suite's failedExpectations from env 2025-10-05 06:01:15 -07:00
Steve Gravrock
bca56032e0 Expose browser errors uniformly outside of GlobalErrors 2025-10-04 12:48:14 -07:00
Steve Gravrock
c590095662 Copy 6.0.0-alpha.0 release notes from branch 2025-09-29 19:53:25 -07:00
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
317 changed files with 21957 additions and 11341 deletions

View File

@@ -1,17 +1,21 @@
# Run tests against supported Node versions, and (except for pull requests)
# against supported browsers.
# against supported browsers that are available on Saucelabs.
version: 2.1
executors:
node24:
docker:
- image: cimg/node:24.0.0
working_directory: ~/workspace
node22:
docker:
- image: cimg/node:22.0.0
working_directory: ~/workspace
node20:
docker:
- image: cimg/node:20.0.0
working_directory: ~/workspace
node18:
docker:
- image: cimg/node:18.0.0
working_directory: ~/workspace
jobs:
build:
@@ -57,22 +61,24 @@ jobs:
at: .
- run:
name: Run tests in parallel
command: npx grunt execSpecsInParallel
command: npm run test:parallel
test_browsers: &test_browsers
executor: node18
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_WORKFLOW_JOB_ID
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
scripts/run-sauce-browsers
exitcode=$?
set -o errexit
scripts/stop-sauce-connect $(cat sauce-pidfile)
scripts/stop-sauce-connect
exit $exitcode
workflows:
@@ -94,27 +100,40 @@ workflows:
push:
jobs:
- build:
executor: node24
name: build_node_24
- build:
executor: node22
name: build_node_22
- build:
executor: node20
name: build_node_20
- build:
executor: node18
name: build_node_18
- test_node:
executor: node22
name: test_node_22
requires:
- build_node_22
- test_node:
executor: node20
name: test_node_20
requires:
- build_node_20
- test_node:
executor: node18
name: test_node_18
requires:
- build_node_18
- test_parallel:
executor: node18
name: test_parallel_node_18
executor: node24
name: test_parallel_node_24
requires:
- build_node_18
- build_node_24
- test_parallel:
executor: node22
name: test_parallel_node_22
requires:
- build_node_22
- test_parallel:
executor: node20
name: test_parallel_node_20
requires:
- build_node_20
- test_parallel:
executor: node20
name: test_parallel_node_20
@@ -122,7 +141,7 @@ workflows:
- build_node_20
- test_browsers:
requires:
- build_node_18
- 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

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

View File

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

View File

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

23
.github/workflows/safari.yml vendored Normal file
View File

@@ -0,0 +1,23 @@
name: Test in latest available Safari
on:
push:
pull_request:
workflow_dispatch:
schedule:
- cron: '0 0 * * *'
jobs:
build:
runs-on: macos-latest
steps:
- name: Report Safari version
run: osascript -e 'get version of application "Safari"'
- uses: actions/checkout@v4
- name: Use Node.js 22.x
uses: actions/setup-node@v4
with:
node-version: 22.x
- run: npm install
- run: npm run build
- run: JASMINE_BROWSER=safari npm run ci

3
.prettierrc Normal file
View File

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

View File

@@ -1,104 +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);
done(false);
}
);
}
);
grunt.registerTask("execSpecsInParallel",
"Run Jasmine core specs in parallel in Node.js",
function() {
// Need to require this here rather than at the top of the file
// so that we don't break verifyNoGlobals above by loading jasmine-core
// too early
const ParallelRunner = require('jasmine/parallel');
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 done = this.async();
const runner = new ParallelRunner({
jasmineCore: require('./lib/jasmine-core.js'),
numWorkers
});
runner.loadConfigFile('./spec/support/jasmine.json')
.then(() => {
runner.exitOnCompletion = false;
return runner.execute();
}).then(
jasmineDoneInfo => done(jasmineDoneInfo.overallStatus === 'passed'),
err => {
console.error(err);
done(false);
}
);
}
);
grunt.registerTask("execSpecsInNode:performance",
"Run Jasmine performance specs in Node.js",
function() {
require("shelljs").exec("node_modules/.bin/jasmine JASMINE_CONFIG_PATH=spec/support/jasmine-performance.json");
}
);
};
function verifyNoGlobals(fn) {
const initialGlobals = Object.keys(global);
fn();
const extras = Object.keys(global).filter(k => !initialGlobals.includes(k));
if (extras.length !== 0) {
throw new Error('Globals were unexpectedly created: ' + extras.join(', '));
}
}

View File

@@ -1,5 +1,5 @@
Copyright (c) 2008-2019 Pivotal Labs
Copyright (c) 2008-2023 The Jasmine developers
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,4 +1,4 @@
<a name="README">[<img src="https://rawgithub.com/jasmine/jasmine/main/images/jasmine-horizontal.svg" width="400px" />](http://jasmine.github.io)</a>
<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
@@ -27,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 | 18, 20 |
| Safari | 15-17 |
| Chrome | Evergreen |
| Firefox | Evergreen, 102, 115 |
| Edge | Evergreen |
| Environment | Supported versions |
|-------------------|----------------------------------|
| Node | 20, 22, 24 |
| Safari | 16*, 17*, 26* |
| 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
@@ -56,5 +60,5 @@ To find out what environments work with a particular Jasmine release, see the [r
* Sheel Choksi
Copyright (c) 2008-2019 Pivotal Labs<br>
Copyright (c) 2008-2023 The Jasmine developers<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

@@ -28,9 +28,18 @@ should also rev to that version.
When ready to release - specs are all green and the stories are done:
1. Update the release notes in `release_notes` - use the Anchorman gem to generate the markdown file and edit accordingly. Include a list of supported environments.
1. Update the version in `package.json`
1. Run `npm run build`.
1. Update the release notes in `release_notes` - use the Anchorman gem to
generate the Markdown file and edit accordingly. Include a list of supported
environments. Get that information from these places:
* For Node, see .circleci/config.yml or the README.
* For Firefox ESR and Safari <=17, see scripts/run-sauce-browsers or the README.
* For evergreen browsers, trigger a Circle CI run and check the
[Saucelabs dashboard](https://app.saucelabs.com/dashboard/tests?ownerId=90a771d55857492da3bd5251a2d92457&ownerType=user&ownerName=jasmine-js&start=last7days)
once it's finished.
* For Safari >17, trigger the [Safari action](https://github.com/jasmine/jasmine/actions/workflows/safari.yml)
and get the version from the output.
2. Update the version in `package.json`
3. Run `npm run build`.
### Commit and push core changes
@@ -41,13 +50,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
@@ -55,11 +64,6 @@ Probably only need to do this when releasing a minor version, and not a patch
version. See [the README file in the docs repo](https://github.com/jasmine/jasmine.github.io/blob/master/README.md)
for instructions.
1. `rake update_edge_jasmine`
1. `npm run jsdoc`
1. `rake release[${version}]` to copy the current edge docs to the new version
1. Commit and push.
### Release the `jasmine` NPM package
See <https://github.com/jasmine/jasmine-npm/blob/main/RELEASE.md>.

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("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,56 +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'
},
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,29 @@
/*
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.
*/
'use strict';
/**
* Note: Only available on Node.
* @module jasmine-core
@@ -6,7 +32,7 @@
const jasmineRequire = require('./jasmine-core/jasmine.js');
module.exports = jasmineRequire;
const boot = (function() {
const bootWithoutGlobals = (function() {
let jasmine, jasmineInterface;
return function bootWithoutGlobals(reinitialize) {
@@ -16,20 +42,22 @@ const boot = (function() {
jasmineInterface = jasmineRequire.interface(jasmine, env);
}
return {jasmine, jasmineInterface};
return { jasmine, jasmineInterface };
};
}());
})();
/**
* Boots a copy of Jasmine and returns an object as described in {@link jasmine}.
* If boot is called multiple times, the same object is returned every time
* unless true is passed.
* @param {boolean} [reinitialize=false] Whether to create a new copy of Jasmine if one already exists
* @param {boolean} [reinitialize=true] Whether to create a new copy of Jasmine if one already exists
* @type {function}
* @return {jasmine}
*/
module.exports.boot = function(reinitialize) {
const {jasmine, jasmineInterface} = boot(reinitialize);
if (reinitialize === undefined) {
reinitialize = true;
}
const { jasmine, jasmineInterface } = bootWithoutGlobals(reinitialize);
for (const k in jasmineInterface) {
global[k] = jasmineInterface[k];
@@ -41,14 +69,13 @@ module.exports.boot = function(reinitialize) {
/**
* Boots a copy of Jasmine and returns an object containing the properties
* that would normally be added to the global object. If noGlobals is called
* multiple times, the same object is returned every time unless true is passed.
* multiple times, the same object is returned every time.
*
* @param {boolean} [reinitialize=false] Whether to create a new copy of Jasmine if one already exists
* @example
* const {describe, beforeEach, it, expect, jasmine} = require('jasmine-core').noGlobals();
*/
module.exports.noGlobals = function(reinitialize) {
const {jasmineInterface} = boot(reinitialize);
module.exports.noGlobals = function() {
const { jasmineInterface } = bootWithoutGlobals(false);
return jasmineInterface;
};
@@ -63,16 +90,16 @@ const rootPath = path.join(__dirname, 'jasmine-core'),
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;
break;
}
}
});

View File

@@ -1,6 +1,6 @@
/*
Copyright (c) 2008-2019 Pivotal Labs
Copyright (c) 2008-2024 The Jasmine developers
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
@@ -21,6 +21,9 @@ 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.
*/
'use strict';
/**
This file starts the process of "booting" Jasmine. It initializes Jasmine,
makes its globals available, and creates the env. This file should be loaded

View File

@@ -1,6 +1,6 @@
/*
Copyright (c) 2008-2019 Pivotal Labs
Copyright (c) 2008-2024 The Jasmine developers
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
@@ -21,6 +21,9 @@ 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.
*/
'use strict';
/**
This file finishes 'booting' Jasmine, performing all of the necessary
initialization before executing the loaded environment and all of a project's
@@ -36,98 +39,26 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
(function() {
const env = jasmine.getEnv();
const urls = new jasmine.HtmlReporterV2Urls();
/**
* ## 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.
* Configures Jasmine based on the current set of query parameters. This
* supports all parameters set by the HTML reporter as well as
* spec=partialPath, which filters out specs whose paths don't contain the
* parameter.
*/
env.configure(urls.configFromCurrentUrl());
const queryString = new jasmine.QueryString({
getWindowLocation: function() {
return window.location;
}
});
const filterSpecs = !!queryString.getParam('spec');
const config = {
stopOnSpecFailure: queryString.getParam('stopOnSpecFailure'),
stopSpecOnExpectationFailure: queryString.getParam(
'stopSpecOnExpectationFailure'
),
hideDisabled: queryString.getParam('hideDisabled')
};
const random = queryString.getParam('random');
if (random !== undefined && random !== '') {
config.random = random;
}
const seed = queryString.getParam('seed');
if (seed) {
config.seed = seed;
}
/**
* ## Reporters
* The `HtmlReporter` builds all of the HTML UI for the runner page. This reporter paints the dots, stars, and x's for specs, as well as all spec names and all failures (if any).
*/
const htmlReporter = new jasmine.HtmlReporter({
env: env,
navigateWithNewParam: function(key, value) {
return queryString.navigateWithNewParam(key, value);
},
addToExistingQueryString: function(key, value) {
return queryString.fullStringWithNewParam(key, value);
},
getContainer: function() {
return document.body;
},
createElement: function() {
return document.createElement.apply(document, arguments);
},
createTextNode: function() {
return document.createTextNode.apply(document, arguments);
},
timer: new jasmine.Timer(),
filterSpecs: filterSpecs
});
/**
* The `jsApiReporter` also receives spec results, and is used by any environment that needs to extract the results from JavaScript.
*/
env.addReporter(jsApiReporter);
env.addReporter(htmlReporter);
/**
* Filter which specs will be run by matching the start of the full name against the `spec` query param.
*/
const specFilter = new jasmine.HtmlSpecFilter({
filterString: function() {
return queryString.getParam('spec');
}
});
config.specFilter = function(spec) {
return specFilter.matches(spec.getFullName());
};
env.configure(config);
/**
* ## Execution
*
* Replace the browser window's `onload`, ensure it's called, and then run all of the loaded specs. This includes initializing the `HtmlReporter` instance and then executing the loaded Jasmine environment. All of this will happen after all of the specs are loaded.
*/
const currentWindowOnload = window.onload;
window.onload = function() {
if (currentWindowOnload) {
currentWindowOnload();
}
htmlReporter.initialize();
// The HTML reporter needs to be set up here so it can access the DOM. Other
// reporters can be added at any time before env.execute() is called.
const htmlReporter = new jasmine.HtmlReporterV2({ env, urls });
env.addReporter(htmlReporter);
env.execute();
};
})();

File diff suppressed because it is too large Load Diff

View File

@@ -8,7 +8,7 @@ body {
background-color: #eee;
padding: 5px;
margin: -8px;
font-size: 11px;
font-size: 12px;
font-family: Monaco, "Lucida Console", monospace;
line-height: 14px;
color: #333;
@@ -63,6 +63,7 @@ body {
float: right;
line-height: 28px;
padding-right: 9px;
font-size: 12px;
}
.jasmine_html-reporter .jasmine-symbol-summary {
overflow: hidden;
@@ -115,6 +116,23 @@ body {
color: #ba9d37;
content: "•";
}
.jasmine_html-reporter progress {
width: 100%;
}
.jasmine_html-reporter progress[value] {
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
}
.jasmine_html-reporter progress[value]::-webkit-progress-value, .jasmine_html-reporter progress[value]::-moz-progress-bar {
background: #007069;
}
.failed .jasmine_html-reporter progress[value]::-webkit-progress-value, .failed .jasmine_html-reporter progress[value]::-moz-progress-bar {
background: #ca3a11;
}
.jasmine_html-reporter progress.failed[value]::-webkit-progress-value, .jasmine_html-reporter progress.failed[value]::-moz-progress-bar {
background: #ca3a11;
}
.jasmine_html-reporter .jasmine-run-options {
float: right;
margin-right: 5px;
@@ -145,8 +163,12 @@ body {
display: block;
color: #eee;
}
.jasmine_html-reporter .jasmine-bar.jasmine-in-progress {
color: #333;
}
.jasmine_html-reporter .jasmine-bar.jasmine-failed, .jasmine_html-reporter .jasmine-bar.jasmine-errored {
background-color: #ca3a11;
color: #eee;
border-bottom: 1px solid #eee;
}
.jasmine_html-reporter .jasmine-bar.jasmine-passed {
@@ -176,11 +198,17 @@ body {
color: white;
}
.jasmine_html-reporter.jasmine-spec-list .jasmine-bar.jasmine-menu.jasmine-failure-list,
.jasmine_html-reporter.jasmine-spec-list .jasmine-results .jasmine-failures {
.jasmine_html-reporter.jasmine-spec-list .jasmine-results .jasmine-failures,
.jasmine_html-reporter.jasmine-spec-list .jasmine-performance-view {
display: none;
}
.jasmine_html-reporter.jasmine-failure-list .jasmine-bar.jasmine-menu.jasmine-spec-list,
.jasmine_html-reporter.jasmine-failure-list .jasmine-summary {
.jasmine_html-reporter.jasmine-failure-list .jasmine-summary,
.jasmine_html-reporter.jasmine-failure-list .jasmine-performance-view {
display: none;
}
.jasmine_html-reporter.jasmine-performance .jasmine-results .jasmine-failures,
.jasmine_html-reporter.jasmine-performance .jasmine-summary {
display: none;
}
.jasmine_html-reporter .jasmine-results {
@@ -229,6 +257,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;
}
@@ -295,4 +326,26 @@ 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;
}
.jasmine-hidden {
display: none;
}
.jasmine-tab + .jasmine-tab:before {
content: " | ";
}
.jasmine-performance-view h2, .jasmine-performance-view h3 {
margin-top: 1em;
margin-bottom: 1em;
}
.jasmine-performance-view table {
border-spacing: 5px;
}
.jasmine-performance-view th, .jasmine-performance-view td {
text-align: left;
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +1,7 @@
{
"name": "jasmine-core",
"license": "MIT",
"version": "5.1.2",
"version": "6.0.0-beta.1",
"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",
@@ -34,75 +36,23 @@
"package.json"
],
"devDependencies": {
"eslint": "^8.36.0",
"eslint-plugin-compat": "^4.0.0",
"glob": "^10.2.3",
"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": "^5.0.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": "^13.0.0",
"globals": "^16.0.0",
"jasmine": "github:jasmine/jasmine-npm",
"jasmine-browser-runner": "github:jasmine/jasmine-browser-runner",
"jsdom": "^22.0.0",
"load-grunt-tasks": "^5.1.0",
"jsdom": "^26.0.0",
"prettier": "1.17.1",
"sass": "^1.58.3",
"shelljs": "^0.8.3",
"temp": "^0.9.0"
},
"prettier": {
"singleQuote": true
},
"eslintConfig": {
"extends": [
"plugin:compat/recommended"
],
"env": {
"browser": true,
"node": true,
"es2017": true
},
"parserOptions": {
"ecmaVersion": 2018
},
"rules": {
"quotes": [
"error",
"single",
{
"avoidEscape": true
}
],
"no-unused-vars": [
"error",
{
"args": "none"
}
],
"no-implicit-globals": "error",
"block-spacing": "error",
"func-call-spacing": [
"error",
"never"
],
"key-spacing": "error",
"no-tabs": "error",
"no-trailing-spaces": "error",
"no-whitespace-before-property": "error",
"semi": [
"error",
"always"
],
"space-before-blocks": "error",
"no-eval": "error",
"no-var": "error",
"no-debugger": "error"
}
"sass": "^1.58.3"
},
"browserslist": [
"Safari >= 15",
"Safari >= 16",
"Firefox >= 102",
"last 2 Chrome versions",
"last 2 Edge versions"

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

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

27
release_notes/5.12.0.md Normal file
View File

@@ -0,0 +1,27 @@
# Jasmine Core 5.12.0 Release Notes
This release reverts the exact spec filtering feature introduced in 5.11.0,
which broke spec filtering in Karma.
## 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 | 141* |
| 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)_

29
release_notes/5.12.1.md Normal file
View File

@@ -0,0 +1,29 @@
Jasmine Core 5.12.1 Release Notes
## Bug fixes
* Fix custom matchers in top-level specs
* Merges [#2088](https://github.com/jasmine/jasmine/pull/2088) from @bonkevin
## 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 | 141* |
| Firefox | 102**, 115**, 128**, 140, 144* |
| Edge | 141* |
\* 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.13.0.md Normal file
View File

@@ -0,0 +1,44 @@
# Jasmine Core 5.13.0 Release Notes
## Changes to supported environments
Safari 26 is now supported on a best-effort basis.
Due to the limited availability of Safari 18 and later on free CI services,
Safari support in future jasmine-core versions will be limited to:
* Best-effort support for the latest Safari version available on GitHub Actions,
which may change at any time.
* Best-effort support for Safari 16 and 17 for as long as it remains practical.
## New Features
* New `extraItStackFrames` and `extraDescribeStackFrames` config options to fix
the filename properties of reporter events in configurations that wrap
`it`/`describe`, such as zone.js. The `filename` properties of reporter events
are no longer deprecated.
* `jasmine.allOf` asymmetric equality tester
* Merges [#2087](https://github.com/jasmine/jasmine/issues/2083) from @jonahd-g
* Fixes [#2083](https://github.com/jasmine/jasmine/pull/2087)
## Supported environments
This version has been tested in the following environments.
| Environment | Supported versions |
|-------------|--------------------------------|
| Node | 18.20.5**, 20, 22, 24 |
| Safari** | 16, 17, 26.1 |
| Chrome | 142* |
| Firefox | 102**, 115**, 128**, 140, 145* |
| Edge | 142* |
\* 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,116 @@
# Jasmine Core 6.0.0-alpha.1 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.1`,
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.1`.
## Breaking changes
### Changes that affect reporters
* Irrelevant properties such as `status` and `failedExpectations` are omitted
from [the event passed to suiteStarted](https://jasmine.github.io/api/6.0.0-alpha.1/global.html#SuiteStartedEvent).
This change 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.
### Changes that affect browser boot files
* The `createElement` and `createTextNode` options of `HtmlReporter` are ignored.
`HtmlReporter` now unconditionally uses `document.createElement` and
`document.createTextNode`.
### Changes that affect spec writing
* HTML reporters cache configuration throughout each run. Configuration changes
made while specs are running will not affect reporter behavior.
* Global error spies always receive a single argument. Previously, the browser
error event was passed as the second argument.
## New features
* A new `HtmlReporterV2` with several improvements over the old `HtmlReporter`:
* Clicking a spec/suite link does exact filtering rather than a substring
match.
* The old dots are replaced with a progress bar. This improves usability with
large suites and fixes an accessibility problem.
* Details of failed specs are displayed as soon as each spec finishes.
* Initialization and wire-up in boot files are much simpler.
If you're using jasmine-browser-runner or copying boot1.js from the standalone
distribution, you'll automatically get the new reporter. If you maintain your
own boot files, you'll get the old reporter unless you update your boot1.js
to match the one that's in this package.
The new reporter produces `spec` query string parameters that are different
from those created by the old reporter. If you use non-Jasmine software that
interprets the `spec` parameter, such as karma-jasmine, you may not be able to
adopt `HtmlReporterV2` unlesss it's updated.
* Use `globalThis` to determine the global object during initialization
This makes jasmine-core more tolerant of buggy bundlers or loaders that
cause `this` to be undefined in the global context.
## Deprecations
* Warn if jasmine-core is loaded as an ES module in a browser.
This is an untested and unsupported configuration that has been known to cause
problems in the past.
* Deprecated `HtmlReporter` and `HtmlSpecFilter` in favor of `HtmlReporterV2`.
## Documentation improvements
* Improved API reference documentation for APIs that are used from browser boot
files.
## Internal improvements
* Removed remaining code that supported suite re-entry.
* Encapsulated suite and spec result and status management.
* Adopted strict mode throughout the codebase.
* Decomposed `HtmlReporter` into components and converted to ES6 classes.
* Made global error handling more uniform between browsers and Node.
## Supported environments
This version has been tested in the following environments.
| Environment | Supported versions |
|-------------------|--------------------------------|
| Node | 20, 22, 24 |
| Safari | 16**, 17** |
| Chrome | 141* |
| Firefox | 102**, 115**, 128**, 140, 143* |
| Edge | 141* |
\* 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,90 @@
# Jasmine Core 6.0.0-alpha.2 Release Notes
This is a pre-release, intended to offer a preview of upcoming 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.2`,
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.2`.
## Changes to supported environments
Safari 26 is now supported on a best-effort basis.†
Due to the limited availability of Safari 18 and later on free CI services,
Safari support in future jasmine-core versions will be limited to:
* Best-effort support for the latest Safari version available on GitHub Actions,
which may change at any time.
* Best-effort support for Safari 16 and 17 for as long as it remains practical.
## Bug Fixes
* Fix custom matchers in top-level specs††
* Merges [#2088](https://github.com/jasmine/jasmine/pull/2088) from @bonkevin
## New features
* Larger body font size in HTML reporters
* New Performance tab in HtmlReporterV2 shows metrics and a list of the slowest
specs.
* Experimental `safariYieldStrategy: "time"` config option, which may make
Jasmine run significantly faster in Safari and similar browsers. So far, this
option has not been tested on a wide variety of workloads. Feedback is
appreciated.
* New `extraItStackFrames` and `extraDescribeStackFrames` config options to fix
the filename properties of reporter events in configurations that wrap
`it`/`describe`, such as zone.js.†
* `jasmine.allOf asymmetric` equality tester†
* Merges [#2087](https://github.com/jasmine/jasmine/pull/2087) from @jonahd-g
* Fixes [#2083](https://github.com/jasmine/jasmine/issues/2083)
* Re-add support for partial spec name filtering via `spec` query parameter
* Fixes [#2085](https://github.com/jasmine/jasmine/issues/2085).
* Require spec/suite property keys to be strings, not just anything that's
cloneable and serializable. This matches the existing API reference
documentation.
## Documentation improvements
* Fix HtmlReporterV2 ctor example
## Internal Improvements
* Remove code to support browsers that don't have MessageChannel. Jasmine hasn't
run in any such browsers since 2.x.
† Also likely to be included in a future 5.x release.<br>
†† Also released in 5.12.1.
## Supported environments
This version has been tested in the following environments.
| Environment | Supported versions |
|-------------------|--------------------------------|
| Node | 20, 22, 24 |
| Safari | 16**, 17**, 26** |
| Chrome | 142* |
| Firefox | 102**, 115**, 128**, 140, 145* |
| Edge | 142* |
\* 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,74 @@
# Jasmine Core 6.0.0-beta.0 Release Notes
This is a pre-release, intended to offer a preview of upcoming 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-beta.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-beta.0`.
## Breaking changes
* boot1.js no longer adds jsApiReporter to the env.
* HtmlReporterV2 initialization and boot1.js have been simplified. If you
maintain your own boot file, update it to match the boot1.js included in this
package.
## New features
* Statically exposed pretty printer as jasmine.pp().
## Bug fixes
* Fixed HtmlReporterV2 progress bar when running a subset of specs.
## Deprecations
* jsApiReporter is deprecated.
* Detect monkey patching and emit a deprecation warning.
## Documentation improvements
* Documented the set of possible spec statuses.
## Internal improvements
* Replaced isArray helper with native Array.isArray
## Supported environments
This version has been tested in the following environments.
| Environment | Supported versions |
|-------------------|--------------------------------|
| Node | 20, 22, 24 |
| Safari | 16**, 17**, 26.1** |
| Chrome | 142* |
| Firefox | 102**, 115**, 128**, 140, 145* |
| Edge | 142* |
\* 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,47 @@
# Jasmine Core 6.0.0-beta.1 Release Notes
This is a pre-release, intended to offer a preview of upcoming changes and to
solicit feedback.
A corresponding release of the `jasmine` package is not planned because the
change in this release only affects browser users.
## 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-beta.1`,
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-beta.1`.
## Bug Fixes
* Revert to using window.onload in boot1.js. This fixes top level await in
jasmine-browser-runner.
## Supported environments
This version has been tested in the following environments.
| Environment | Supported versions |
|-------------------|--------------------------------|
| Node | 20, 22, 24 |
| Safari** | 16, 17, 26.1 |
| Chrome | 143* |
| Firefox | 102**, 115**, 128**, 140, 145* |
| Edge | 142* |
\* 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

@@ -1,5 +1,10 @@
#!/bin/sh
# Run tests in supported browsers that are available on Saucelabs.
# Note: The latest Safari version is tested via GitHub Actions because Saucelabs
# only makes it available to paid enterprise accounts. See
# .github/workflows/safari.yml.
run_browser() {
browser=$1
version=$2
@@ -25,22 +30,23 @@ run_browser() {
passfile=`mktemp -t jasmine-results.XXXXXX` || exit 1
failfile=`mktemp -t jasmine-results.XXXXXX` || exit 1
# As of 2023-09-30, Sauce Connect doesn't work with the latest Chrome version
# on the default Linux. Run on Mac OS instead. The OS specification may need to
# be updated or removed when new Chrome versions stop being available on Mac OS
# 12, although historically this has taken several major OS versions.
# See <https://saucelabs.com/products/supported-browsers-devices>.
# On Saucelabs, the test suite frequently runs ~30s slower on Mac OS than it
# does on Linux, so it's probably worth removing the OS specification once Sauce
# Connect works with Chrome latest on Linux again.
run_browser chrome latest "macOS 12"
run_browser chrome latest
run_browser firefox latest
run_browser firefox 115
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 safari 17
run_browser safari 16
run_browser safari 15
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
});
@@ -30,8 +30,8 @@ describe('AsyncExpectation', function() {
it('converts a fail to a pass', function() {
const addExpectationResult = jasmine.createSpy('addExpectationResult'),
actual = Promise.reject(new Error('nope')),
expectation = jasmineUnderTest.Expectation.asyncFactory({
matchersUtil: new jasmineUnderTest.MatchersUtil({
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(new Error('nope')),
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

@@ -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;
@@ -1051,4 +1247,25 @@ describe('Clock (acceptance)', function() {
clock.tick(400);
});
describe('Warning about monkey patching', function() {
for (const name of ['tick', 'mockDate', 'install', 'uninstall']) {
it(`warns if Clock#${name} is monkey patched`, function() {
spyOn(console, 'error');
const clock = new privateUnderTest.Clock({}, function() {}, {});
const patch = {};
clock[name] = patch;
// eslint-disable-next-line no-console
expect(console.error).toHaveBeenCalledOnceWith(
jasmine.stringContaining('DEPRECATION: Monkey patching detected.')
);
// eslint-disable-next-line no-console
expect(console.error).toHaveBeenCalledOnceWith(
jasmine.stringContaining('ClockSpec.js')
);
expect(clock[name]).toBe(patch);
});
}
});
});

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,166 @@
describe('Configuration', function() {
const standardBooleanKeys = [
'random',
'stopOnSpecFailure',
'stopSpecOnExpectationFailure',
'failSpecWithNoExpectations',
'hideDisabled',
'autoCleanClosures',
'forbidDuplicateNames',
'detectLateRejectionHandling',
'verboseDeprecations'
];
const allKeys = [
...standardBooleanKeys,
'seed',
'specFilter',
'extraItStackFrames',
'extraDescribeStackFrames',
'safariYieldStrategy'
];
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);
expect(subject.extraItStackFrames).toEqual(0);
expect(subject.extraDescribeStackFrames).toEqual(0);
expect(subject.safariYieldStrategy).toEqual('count');
});
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();
});
it('sets extraItStackFrames when not undefined', function() {
const subject = new privateUnderTest.Configuration();
subject.update({ extraItStackFrames: undefined });
expect(subject.extraItStackFrames).toEqual(0);
subject.update({ extraItStackFrames: 100000 });
expect(subject.extraItStackFrames).toEqual(100000);
});
it('sets extraDescribeStackFrames when not undefined', function() {
const subject = new privateUnderTest.Configuration();
subject.update({ extraDescribeStackFrames: undefined });
expect(subject.extraDescribeStackFrames).toEqual(0);
subject.update({ extraDescribeStackFrames: 100000 });
expect(subject.extraDescribeStackFrames).toEqual(100000);
});
it('sets safariYieldStrategy when valid', function() {
const subject = new privateUnderTest.Configuration();
subject.update({ safariYieldStrategy: undefined });
expect(subject.safariYieldStrategy).toEqual('count');
subject.update({ safariYieldStrategy: 'time' });
expect(subject.safariYieldStrategy).toEqual('time');
subject.update({ safariYieldStrategy: 'count' });
expect(subject.safariYieldStrategy).toEqual('count');
});
it('rejcts invalid safariYieldStrategy values', function() {
const subject = new privateUnderTest.Configuration();
expect(function() {
subject.update({ safariYieldStrategy: 'thyme' });
}).toThrowError(
"Invalid safariYieldStrategy value. Valid values are 'count' and 'time'."
);
});
});
});

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

@@ -1,8 +1,7 @@
// TODO: Fix these unit tests!
describe('Env', function() {
let env;
beforeEach(function() {
env = new jasmineUnderTest.Env();
env = new privateUnderTest.Env();
});
afterEach(function() {
@@ -13,22 +12,20 @@ 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'
);
});
});
describe('#topSuite', function() {
it('returns an object that describes the tree of suites and specs', function() {
spyOn(env, 'deprecated');
env.it('a top level spec');
env.describe('a suite', function() {
env.it('a spec');
@@ -38,24 +35,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(
@@ -95,16 +92,16 @@ describe('Env', function() {
});
});
it('accepts its own current configureation', function() {
it('accepts its own current configuration', function() {
env.configure(env.configuration());
});
it('can configure specs to throw errors on expectation failures', function() {
env.configure({ stopSpecOnExpectationFailure: true });
spyOn(jasmineUnderTest, 'Spec').and.callThrough();
spyOn(privateUnderTest, 'Spec').and.callThrough();
env.it('foo', function() {});
expect(jasmineUnderTest.Spec).toHaveBeenCalledWith(
expect(privateUnderTest.Spec).toHaveBeenCalledWith(
jasmine.objectContaining({
throwOnExpectationFailure: true
})
@@ -114,9 +111,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
})
@@ -124,7 +121,6 @@ describe('Env', function() {
});
it('ignores configuration properties that are present but undefined', function() {
spyOn(env, 'deprecated');
const initialConfig = {
random: true,
seed: '123',
@@ -150,9 +146,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
})
@@ -160,9 +156,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
})
@@ -198,6 +194,29 @@ describe('Env', function() {
expect(innerSuite.parentSuite).toBe(suite);
expect(spec.getFullName()).toEqual('outer suite inner suite a spec');
});
it('sets the caller filename correctly when extraDescribeStackFrames is not set', function() {
// IIFE is used to match the stack depth when global describe() is called
const suite = (function() {
return env[methodName]('a suite', function() {
env.it('a spec');
});
})();
expect(suite.filename).toMatch(/EnvSpec\.js$/);
});
it('sets the caller filename correctly when extraDescribeStackFrames is set', function() {
env.configure({ extraDescribeStackFrames: 2 });
// IIFE is used to match the stack depth when global describe() is called
const suite = (function() {
return specHelpers.callerFilenameShim(function() {
return env[methodName]('a suite', function() {
env.it('a spec');
});
});
})();
expect(suite.filename).toMatch(/EnvSpec\.js$/);
});
}
describe('#describe', function() {
@@ -205,7 +224,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');
});
@@ -301,6 +319,25 @@ describe('Env', function() {
.not.toEqual('');
expect(spec.pend).toBeFalsy();
});
it('sets the caller filename correctly when extraItStackFrames is not set', function() {
// IIFE is used to match the stack depth when global it() is called
const spec = (function() {
return env[methodName]('a spec', function() {});
})();
expect(spec.filename).toMatch(/EnvSpec\.js$/);
});
it('sets the caller filename correctly when extraItStackFrames is set', function() {
env.configure({ extraItStackFrames: 2 });
// IIFE is used to match the stack depth when global it() is called
const spec = (function() {
return specHelpers.callerFilenameShim(function() {
return env[methodName]('a spec', function() {});
});
})();
expect(spec.filename).toMatch(/EnvSpec\.js$/);
});
}
describe('#it', function() {
@@ -338,7 +375,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() {});
@@ -347,9 +384,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
});
@@ -488,14 +525,14 @@ describe('Env', function() {
it('does not throw an error when called in a describe', function() {
env.setParallelLoadingState('helpers');
check();
check(1);
env.setParallelLoadingState('specs');
check();
check(2);
function check() {
function check(disambiguator) {
let done = false;
env.describe('a suite', function() {
env.describe('a suite ' + disambiguator, function() {
expect(function() {
env.it('a spec');
env.beforeAll(function() {});
@@ -595,14 +632,14 @@ describe('Env', function() {
it('does not throw an error when called in a describe', function() {
env.setParallelLoadingState('helpers');
check();
check(1);
env.setParallelLoadingState('specs');
check();
check(2);
function check() {
function check(disambiguator) {
let done = false;
env.describe('a suite', function() {
env.describe('a suite ' + disambiguator, function() {
expect(function() {
env.it('a spec');
env.afterAll(function() {});
@@ -626,9 +663,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();
});
});
@@ -642,9 +682,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();
@@ -655,12 +699,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;
@@ -673,10 +717,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
});
@@ -686,12 +730,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;
@@ -705,10 +749,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
});
@@ -716,14 +760,13 @@ describe('Env', function() {
it("does not expose the suite as 'this'", function() {
let suiteThis;
spyOn(env, 'deprecated');
env.describe('a suite', function() {
suiteThis = this;
env.it('has a spec');
});
expect(suiteThis).not.toBeInstanceOf(jasmineUnderTest.Suite);
expect(suiteThis).not.toBeInstanceOf(privateUnderTest.Suite);
});
describe('#execute', function() {
@@ -732,7 +775,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() {
@@ -740,9 +783,9 @@ 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);
});
@@ -751,9 +794,9 @@ describe('Env', function() {
it('should not reset the topSuite if parallelReset was called since the last run', async function() {
await env.execute();
env.parallelReset();
spyOn(jasmineUnderTest.Suite.prototype, 'reset');
spyOn(privateUnderTest.Suite.prototype, 'reset');
await env.execute();
expect(jasmineUnderTest.Suite.prototype.reset).not.toHaveBeenCalled();
expect(privateUnderTest.Suite.prototype.reset).not.toHaveBeenCalled();
});
describe('In parallel mode', function() {
@@ -831,4 +874,44 @@ describe('Env', function() {
}).toThrowError('Jasmine cannot be configured via Env in parallel mode');
});
});
describe('Warning about monkey patching', function() {
afterEach(function() {
// deprecateMonkeyPatching() uses jasmine.getEnv(), not the env from
// this suite. Clean it up so we don't leak event listeners.
jasmineUnderTest.getEnv().cleanup_();
privateUnderTest.currentEnv_ = null;
});
const names = [
'describe',
'xdescribe',
'fdescribe',
'it',
'xit',
'fit',
'beforeEach',
'afterEach',
'beforeAll',
'afterAll'
];
for (const name of names) {
it(`warns if Env#${name} is monkey patched`, function() {
spyOn(console, 'error');
const patch = {};
env[name] = patch;
// eslint-disable-next-line no-console
expect(console.error).toHaveBeenCalledOnceWith(
jasmine.stringContaining('DEPRECATION: Monkey patching detected.')
);
// eslint-disable-next-line no-console
expect(console.error).toHaveBeenCalledOnceWith(
jasmine.stringContaining('EnvSpec.js')
);
expect(env[name]).toBe(patch);
});
}
});
});

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,14 +215,14 @@ 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 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/);
});
@@ -215,7 +236,7 @@ describe('ExceptionFormatter', function() {
CustomError.prototype.anInheritedProp = 'something';
const error = new CustomError('nope');
const result = new jasmineUnderTest.ExceptionFormatter().stack(error);
const result = new privateUnderTest.ExceptionFormatter().stack(error);
expect(result).not.toContain('anInheritedProp');
});
@@ -230,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 });
@@ -249,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 });
@@ -262,8 +283,8 @@ 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');
@@ -272,7 +293,7 @@ describe('ExceptionFormatter', function() {
describe('when the error has a cause property', function() {
it('recursively includes the cause in the stack trace in this environment', function() {
const subject = new jasmineUnderTest.ExceptionFormatter();
const subject = new privateUnderTest.ExceptionFormatter();
const rootCause = new Error('root cause');
const proximateCause = new Error('proximate cause', {
cause: rootCause
@@ -306,7 +327,7 @@ describe('ExceptionFormatter', function() {
});
it('does not throw if cause is a non Error', function() {
const formatter = new jasmineUnderTest.ExceptionFormatter();
const formatter = new privateUnderTest.ExceptionFormatter();
expect(function() {
formatter.stack(

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

View File

@@ -1,65 +1,68 @@
describe('GlobalErrors', function() {
it('calls the added handler on error', function() {
const fakeGlobal = browserGlobal();
const globals = browserGlobals();
const handler = jasmine.createSpy('errorHandler');
const errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
const errors = new privateUnderTest.GlobalErrors(
globals.global,
() => ({})
);
errors.install();
errors.pushListener(handler);
const error = new Error('nope');
dispatchErrorEvent(fakeGlobal, { error });
dispatchEvent(globals.listeners, 'error', { error });
expect(handler).toHaveBeenCalledWith(
jasmine.is(error),
jasmine.objectContaining({ error: jasmine.is(error) })
);
expect(handler).toHaveBeenCalledWith(jasmine.is(error));
});
it('is not affected by overriding global.onerror', function() {
const fakeGlobal = browserGlobal();
const globals = browserGlobals();
const handler = jasmine.createSpy('errorHandler');
const errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
const errors = new privateUnderTest.GlobalErrors(
globals.global,
() => ({})
);
errors.install();
errors.pushListener(handler);
fakeGlobal.onerror = () => {};
globals.global.onerror = () => {};
const error = new Error('nope');
dispatchErrorEvent(fakeGlobal, { error });
dispatchEvent(globals.listeners, 'error', { error });
expect(handler).toHaveBeenCalledWith(
jasmine.is(error),
jasmine.objectContaining({ error: jasmine.is(error) })
);
expect(handler).toHaveBeenCalledWith(jasmine.is(error));
});
it('only calls the most recent handler', function() {
const fakeGlobal = browserGlobal();
const globals = browserGlobals();
const handler1 = jasmine.createSpy('errorHandler1');
const handler2 = jasmine.createSpy('errorHandler2');
const errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
const errors = new privateUnderTest.GlobalErrors(
globals.global,
() => ({})
);
errors.install();
errors.pushListener(handler1);
errors.pushListener(handler2);
const error = new Error('nope');
dispatchErrorEvent(fakeGlobal, { error });
dispatchEvent(globals.listeners, 'error', { error });
expect(handler1).not.toHaveBeenCalled();
expect(handler2).toHaveBeenCalledWith(
jasmine.is(error),
jasmine.objectContaining({ error: jasmine.is(error) })
);
expect(handler2).toHaveBeenCalledWith(jasmine.is(error));
});
it('calls previous handlers when one is removed', function() {
const fakeGlobal = browserGlobal();
const globals = browserGlobals();
const handler1 = jasmine.createSpy('errorHandler1');
const handler2 = jasmine.createSpy('errorHandler2');
const errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
const errors = new privateUnderTest.GlobalErrors(
globals.global,
() => ({})
);
errors.install();
errors.pushListener(handler1);
@@ -68,43 +71,46 @@ describe('GlobalErrors', function() {
errors.popListener(handler2);
const error = new Error('nope');
dispatchErrorEvent(fakeGlobal, { error });
dispatchEvent(globals.listeners, 'error', { error });
expect(handler1).toHaveBeenCalledWith(
jasmine.is(error),
jasmine.objectContaining({ error: jasmine.is(error) })
);
expect(handler1).toHaveBeenCalledWith(jasmine.is(error));
expect(handler2).not.toHaveBeenCalled();
});
it('throws when no listener is passed to #popListener', function() {
const errors = new jasmineUnderTest.GlobalErrors({});
const errors = new privateUnderTest.GlobalErrors({});
expect(function() {
errors.popListener();
}).toThrowError('popListener expects a listener');
});
it('uninstalls itself', function() {
const fakeGlobal = browserGlobal();
const errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
const globals = browserGlobals();
const errors = new privateUnderTest.GlobalErrors(
globals.global,
() => ({})
);
function unrelatedListener() {}
errors.install();
fakeGlobal.addEventListener('error', unrelatedListener);
globals.global.addEventListener('error', unrelatedListener);
errors.uninstall();
expect(fakeGlobal.listeners_.error).toEqual([unrelatedListener]);
expect(globals.listeners.error).toEqual([unrelatedListener]);
});
it('rethrows the original error when there is no handler', function() {
const fakeGlobal = browserGlobal();
const errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
const globals = browserGlobals();
const errors = new privateUnderTest.GlobalErrors(
globals.global,
() => ({})
);
const originalError = new Error('nope');
errors.install();
try {
dispatchErrorEvent(fakeGlobal, { error: originalError });
dispatchEvent(globals.listeners, 'error', { error: originalError });
} catch (e) {
expect(e).toBe(originalError);
}
@@ -112,36 +118,53 @@ describe('GlobalErrors', function() {
errors.uninstall();
});
it("reports browser error events that don't have errors", function() {
const globals = browserGlobals();
const handler = jasmine.createSpy('errorHandler');
const errors = new privateUnderTest.GlobalErrors(
globals.global,
() => ({})
);
errors.install();
errors.pushListener(handler);
const event = {
message: 'Uncaught SyntaxError: Unexpected end of input',
error: undefined,
filename: 'borkenSpec.js',
lineno: 42
};
dispatchEvent(globals.listeners, 'error', event);
expect(handler).toHaveBeenCalledWith({
message: 'Uncaught SyntaxError: Unexpected end of input',
filename: 'borkenSpec.js',
lineno: 42,
stack: '@borkenSpec.js:42'
});
});
it('reports uncaught exceptions in node.js', function() {
const fakeGlobal = {
process: {
on: jasmine.createSpy('process.on'),
removeListener: jasmine.createSpy('process.removeListener'),
listeners: jasmine
.createSpy('process.listeners')
.and.returnValue(['foo']),
removeAllListeners: jasmine.createSpy('process.removeAllListeners')
}
},
handler = jasmine.createSpy('errorHandler'),
errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
const globals = nodeGlobals();
const errors = new privateUnderTest.GlobalErrors(
globals.global,
() => ({})
);
const handler = jasmine.createSpy('errorHandler');
function originalHandler() {}
globals.listeners.uncaughtException = [originalHandler];
errors.install();
expect(fakeGlobal.process.on).toHaveBeenCalledWith(
'uncaughtException',
expect(globals.listeners.uncaughtException).toEqual([
jasmine.any(Function)
);
expect(fakeGlobal.process.listeners).toHaveBeenCalledWith(
'uncaughtException'
);
expect(fakeGlobal.process.removeAllListeners).toHaveBeenCalledWith(
'uncaughtException'
);
]);
expect(globals.listeners.uncaughtException).not.toEqual([
originalHandler()
]);
errors.pushListener(handler);
const addedListener = fakeGlobal.process.on.calls.argsFor(0)[1];
addedListener(new Error('bar'));
dispatchEvent(globals.listeners, 'uncaughtException', new Error('bar'));
expect(handler).toHaveBeenCalledWith(new Error('bar'));
expect(handler.calls.argsFor(0)[0].jasmineMessage).toBe(
@@ -150,47 +173,31 @@ describe('GlobalErrors', function() {
errors.uninstall();
expect(fakeGlobal.process.removeListener).toHaveBeenCalledWith(
'uncaughtException',
addedListener
);
expect(fakeGlobal.process.on).toHaveBeenCalledWith(
'uncaughtException',
'foo'
);
expect(globals.listeners.uncaughtException).toEqual([originalHandler]);
});
describe('Reporting unhandled promise rejections in node.js', function() {
it('reports rejections with `Error` reasons', function() {
const fakeGlobal = {
process: {
on: jasmine.createSpy('process.on'),
removeListener: jasmine.createSpy('process.removeListener'),
listeners: jasmine
.createSpy('process.listeners')
.and.returnValue(['foo']),
removeAllListeners: jasmine.createSpy('process.removeAllListeners')
}
},
handler = jasmine.createSpy('errorHandler'),
errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
const globals = nodeGlobals();
const errors = new privateUnderTest.GlobalErrors(
globals.global,
() => ({})
);
const handler = jasmine.createSpy('errorHandler');
function originalHandler() {}
globals.listeners.unhandledRejection = [originalHandler];
errors.install();
expect(fakeGlobal.process.on).toHaveBeenCalledWith(
'unhandledRejection',
expect(globals.listeners.unhandledRejection).toEqual([
jasmine.any(Function)
);
expect(fakeGlobal.process.listeners).toHaveBeenCalledWith(
'unhandledRejection'
);
expect(fakeGlobal.process.removeAllListeners).toHaveBeenCalledWith(
'unhandledRejection'
);
]);
expect(globals.listeners.unhandledRejection).not.toEqual([
originalHandler()
]);
errors.pushListener(handler);
const addedListener = fakeGlobal.process.on.calls.argsFor(1)[1];
addedListener(new Error('bar'));
dispatchEvent(globals.listeners, 'unhandledRejection', new Error('bar'));
expect(handler).toHaveBeenCalledWith(new Error('bar'));
expect(handler.calls.argsFor(0)[0].jasmineMessage).toBe(
@@ -199,38 +206,21 @@ describe('GlobalErrors', function() {
errors.uninstall();
expect(fakeGlobal.process.removeListener).toHaveBeenCalledWith(
'unhandledRejection',
addedListener
);
expect(fakeGlobal.process.on).toHaveBeenCalledWith(
'unhandledRejection',
'foo'
);
expect(globals.listeners.unhandledRejection).toEqual([originalHandler]);
});
it('reports rejections with non-`Error` reasons', function() {
const fakeGlobal = {
process: {
on: jasmine.createSpy('process.on'),
removeListener: function() {},
listeners: function() {
return [];
},
removeAllListeners: function() {}
}
},
handler = jasmine.createSpy('errorHandler'),
errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
const globals = nodeGlobals();
const errors = new privateUnderTest.GlobalErrors(
globals.global,
() => ({})
);
const handler = jasmine.createSpy('errorHandler');
errors.install();
errors.pushListener(handler);
expect(fakeGlobal.process.on.calls.argsFor(1)[0]).toEqual(
'unhandledRejection'
);
const addedListener = fakeGlobal.process.on.calls.argsFor(1)[1];
addedListener(17);
dispatchEvent(globals.listeners, 'unhandledRejection', 17);
expect(handler).toHaveBeenCalledWith(
new Error(
@@ -242,27 +232,17 @@ describe('GlobalErrors', function() {
});
it('reports rejections with no reason provided', function() {
const fakeGlobal = {
process: {
on: jasmine.createSpy('process.on'),
removeListener: function() {},
listeners: function() {
return [];
},
removeAllListeners: function() {}
}
},
handler = jasmine.createSpy('errorHandler'),
errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
const globals = nodeGlobals();
const errors = new privateUnderTest.GlobalErrors(
globals.global,
() => ({})
);
const handler = jasmine.createSpy('errorHandler');
errors.install();
errors.pushListener(handler);
expect(fakeGlobal.process.on.calls.argsFor(1)[0]).toEqual(
'unhandledRejection'
);
const addedListener = fakeGlobal.process.on.calls.argsFor(1)[1];
addedListener(undefined);
dispatchEvent(globals.listeners, 'unhandledRejection', undefined);
expect(handler).toHaveBeenCalledWith(
new Error(
@@ -272,75 +252,368 @@ describe('GlobalErrors', function() {
)
);
});
describe('When detectLateRejectionHandling is true', function() {
let globals, errors;
beforeEach(function() {
globals = nodeGlobals();
errors = new privateUnderTest.GlobalErrors(globals.global, () => ({
detectLateRejectionHandling: true
}));
});
it('subscribes and unsubscribes from the rejectionHandled event', function() {
function originalHandler() {}
globals.global.process.on('rejectionHandled', originalHandler);
errors.install();
expect(globals.listeners.rejectionHandled).toEqual([
jasmine.any(Function)
]);
expect(globals.listeners.rejectionHandled).not.toEqual([
originalHandler
]);
errors.uninstall();
expect(globals.listeners.rejectionHandled).toEqual([originalHandler]);
});
describe("When the unhandledRejection event doesn't have a promise", function() {
it('immediately reports the rejection', function() {
const handler = jasmine.createSpy('errorHandler');
errors.install();
errors.pushListener(handler);
dispatchEvent(
globals.listeners,
'unhandledRejection',
new Error('nope'),
undefined
);
expect(handler).toHaveBeenCalledWith(new Error('nope'));
expect(handler.calls.argsFor(0)[0].jasmineMessage).toBe(
'Unhandled promise rejection: Error: nope'
);
});
});
describe('When the unhandledRejection event has a promise property', function() {
it('does not immediately report the rejection', function() {
const handler = jasmine.createSpy('errorHandler');
errors.install();
errors.pushListener(handler);
const promise = Promise.reject('nope');
promise.catch(() => {});
dispatchEvent(
globals.listeners,
'unhandledRejection',
'nope',
promise
);
expect(handler).not.toHaveBeenCalled();
});
describe('When reportUnhandledRejections is called', function() {
it('reports rejections that have not been handled', function() {
const handler = jasmine.createSpy('errorHandler');
errors.install();
errors.pushListener(handler);
const reason = new Error('nope');
const promise = Promise.reject(reason);
promise.catch(() => {});
dispatchEvent(
globals.listeners,
'unhandledRejection',
reason,
promise
);
errors.reportUnhandledRejections();
expect(handler).toHaveBeenCalledWith(new Error('nope'));
expect(handler.calls.argsFor(0)[0].jasmineMessage).toBe(
'Unhandled promise rejection: Error: nope'
);
});
it('does not report rejections that have been handled', function() {
const handler = jasmine.createSpy('errorHandler');
errors.install();
errors.pushListener(handler);
const reason = new Error('nope');
const promise = Promise.reject(reason);
promise.catch(() => {});
dispatchEvent(
globals.listeners,
'unhandledRejection',
reason,
promise
);
dispatchEvent(globals.listeners, 'rejectionHandled', promise);
errors.reportUnhandledRejections();
expect(handler).not.toHaveBeenCalled();
});
it('does not report the same rejection on subsequent calls', function() {
const handler = jasmine.createSpy('errorHandler');
errors.install();
errors.pushListener(handler);
const promise = Promise.reject('nope');
promise.catch(() => {});
dispatchEvent(
globals.listeners,
'unhandledRejection',
'nope',
promise
);
errors.reportUnhandledRejections();
expect(handler).toHaveBeenCalled();
handler.calls.reset();
errors.reportUnhandledRejections();
expect(handler).not.toHaveBeenCalled();
});
});
});
});
});
describe('Reporting unhandled promise rejections in the browser', function() {
it('subscribes and unsubscribes from the unhandledrejection event', function() {
const fakeGlobal = browserGlobal();
const errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
const globals = browserGlobals();
const errors = new privateUnderTest.GlobalErrors(
globals.global,
() => ({})
);
errors.install();
expect(fakeGlobal.listeners_.unhandledrejection).toEqual([
expect(globals.listeners.unhandledrejection).toEqual([
jasmine.any(Function)
]);
errors.uninstall();
expect(fakeGlobal.listeners_.unhandledrejection).toEqual([]);
expect(globals.listeners.unhandledrejection).toEqual([]);
});
it('reports rejections whose reason is a string', function() {
const fakeGlobal = browserGlobal();
const globals = browserGlobals();
const handler = jasmine.createSpy('errorHandler');
const errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
const errors = new privateUnderTest.GlobalErrors(
globals.global,
() => ({})
);
errors.install();
errors.pushListener(handler);
const event = { reason: 'nope' };
dispatchUnhandledRejectionEvent(fakeGlobal, event);
dispatchEvent(globals.listeners, 'unhandledrejection', event);
expect(handler).toHaveBeenCalledWith(
'Unhandled promise rejection: nope',
event
);
expect(handler).toHaveBeenCalledWith('Unhandled promise rejection: nope');
});
it('reports rejections whose reason is an Error', function() {
const fakeGlobal = browserGlobal();
const globals = browserGlobals();
const handler = jasmine.createSpy('errorHandler');
const errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
const errors = new privateUnderTest.GlobalErrors(
globals.global,
() => ({})
);
errors.install();
errors.pushListener(handler);
const reason = new Error('bar');
const event = { reason };
dispatchUnhandledRejectionEvent(fakeGlobal, event);
dispatchEvent(globals.listeners, 'unhandledrejection', event);
expect(handler).toHaveBeenCalledWith(
expect(handler).toHaveBeenCalledTimes(1);
const received = handler.calls.argsFor(0)[0];
expect(received).toBeInstanceOf(Error);
expect(received).toEqual(
jasmine.objectContaining({
jasmineMessage: 'Unhandled promise rejection: Error: bar',
message: reason.message,
stack: reason.stack
}),
event
})
);
});
describe('When detectLateRejectionHandling is true', function() {
let globals, errors;
beforeEach(function() {
globals = browserGlobals();
errors = new privateUnderTest.GlobalErrors(globals.global, () => ({
detectLateRejectionHandling: true
}));
});
it('subscribes and unsubscribes from the rejectionhandled event', function() {
errors.install();
expect(globals.listeners.rejectionhandled).toEqual([
jasmine.any(Function)
]);
errors.uninstall();
expect(globals.listeners.rejectionhandled).toEqual([]);
});
describe("When the unhandledrejection event doesn't have a promise property", function() {
it('immediately reports the rejection', function() {
const handler = jasmine.createSpy('errorHandler');
errors.install();
errors.pushListener(handler);
const event = { reason: 'nope' };
dispatchEvent(globals.listeners, 'unhandledrejection', event);
expect(handler).toHaveBeenCalledWith(
'Unhandled promise rejection: nope'
);
});
});
describe('When the unhandledrejection event has a promise property', function() {
it('does not immediately report the rejection', function() {
const handler = jasmine.createSpy('errorHandler');
errors.install();
errors.pushListener(handler);
const promise = Promise.reject('nope');
promise.catch(() => {});
dispatchEvent(globals.listeners, 'unhandledrejection', {
reason: 'nope',
promise
});
expect(handler).not.toHaveBeenCalled();
});
describe('When reportUnhandledRejections is called', function() {
it('reports rejections that have not been handled', function() {
const handler = jasmine.createSpy('errorHandler');
errors.install();
errors.pushListener(handler);
const promise = Promise.reject('nope');
promise.catch(() => {});
dispatchEvent(globals.listeners, 'unhandledrejection', {
reason: 'nope',
promise
});
errors.reportUnhandledRejections();
expect(handler).toHaveBeenCalledWith(
'Unhandled promise rejection: nope'
);
});
it('does not report rejections that have been handled', function() {
const handler = jasmine.createSpy('errorHandler');
errors.install();
errors.pushListener(handler);
const promise = Promise.reject('nope');
promise.catch(() => {});
dispatchEvent(globals.listeners, 'unhandledrejection', {
reason: 'nope',
promise
});
dispatchEvent(globals.listeners, 'rejectionhandled', { promise });
errors.reportUnhandledRejections();
expect(handler).not.toHaveBeenCalled();
});
it('does not report the same rejection on subsequent calls', function() {
const handler = jasmine.createSpy('errorHandler');
errors.install();
errors.pushListener(handler);
const promise = Promise.reject('nope');
promise.catch(() => {});
dispatchEvent(globals.listeners, 'unhandledrejection', {
reason: 'nope',
promise
});
errors.reportUnhandledRejections();
expect(handler).toHaveBeenCalled();
handler.calls.reset();
errors.reportUnhandledRejections();
expect(handler).not.toHaveBeenCalled();
});
});
});
});
});
describe('Reporting uncaught exceptions in node.js', function() {
it('prepends a descriptive message when the error is not an `Error`', function() {
const globals = nodeGlobals();
const errors = new privateUnderTest.GlobalErrors(
globals.global,
() => ({})
);
const handler = jasmine.createSpy('errorHandler');
errors.install();
errors.pushListener(handler);
dispatchEvent(globals.listeners, 'uncaughtException', 17);
expect(handler).toHaveBeenCalledWith(new Error('Uncaught exception: 17'));
});
it('substitutes a descriptive message when the error is falsy', function() {
const globals = nodeGlobals();
const errors = new privateUnderTest.GlobalErrors(
globals.global,
() => ({})
);
const handler = jasmine.createSpy('errorHandler');
errors.install();
errors.pushListener(handler);
dispatchEvent(globals.listeners, 'uncaughtException', undefined);
expect(handler).toHaveBeenCalledWith(
new Error('Uncaught exception with no error or message')
);
});
});
describe('#setOverrideListener', function() {
it('overrides the existing handlers in browsers until removed', function() {
const fakeGlobal = browserGlobal();
const globals = browserGlobals();
const handler0 = jasmine.createSpy('handler0');
const handler1 = jasmine.createSpy('handler1');
const overrideHandler = jasmine.createSpy('overrideHandler');
const errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
const errors = new privateUnderTest.GlobalErrors(
globals.global,
() => ({})
);
errors.install();
errors.pushListener(handler0);
errors.setOverrideListener(overrideHandler, () => {});
errors.pushListener(handler1);
dispatchErrorEvent(fakeGlobal, { error: 'foo' });
dispatchEvent(globals.listeners, 'error', { error: 'foo' });
expect(overrideHandler).toHaveBeenCalledWith('foo');
expect(handler0).not.toHaveBeenCalled();
@@ -349,55 +622,48 @@ describe('GlobalErrors', function() {
errors.removeOverrideListener();
const event = { error: 'baz' };
dispatchErrorEvent(fakeGlobal, event);
dispatchEvent(globals.listeners, 'error', event);
expect(overrideHandler).not.toHaveBeenCalledWith('baz');
expect(handler1).toHaveBeenCalledWith('baz', event);
expect(handler1).toHaveBeenCalledWith('baz');
});
it('overrides the existing handlers in Node until removed', function() {
const globalEventListeners = {};
const fakeGlobal = {
process: {
on: (name, listener) => (globalEventListeners[name] = listener),
removeListener: () => {},
listeners: name => globalEventListeners[name],
removeAllListeners: name => (globalEventListeners[name] = [])
}
};
const globals = nodeGlobals();
const handler0 = jasmine.createSpy('handler0');
const handler1 = jasmine.createSpy('handler1');
const overrideHandler = jasmine.createSpy('overrideHandler');
const errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
const errors = new privateUnderTest.GlobalErrors(
globals.global,
() => ({})
);
errors.install();
errors.pushListener(handler0);
errors.setOverrideListener(overrideHandler);
errors.pushListener(handler1);
globalEventListeners['uncaughtException'](new Error('foo'));
dispatchEvent(globals.listeners, 'uncaughtException', new Error('foo'));
expect(overrideHandler).toHaveBeenCalledWith(new Error('foo'));
expect(handler0).not.toHaveBeenCalled();
expect(handler1).not.toHaveBeenCalled();
overrideHandler.calls.reset();
errors.removeOverrideListener();
globalEventListeners['uncaughtException'](new Error('bar'));
expect(overrideHandler).not.toHaveBeenCalledWith(new Error('bar'));
dispatchEvent(globals.listeners, 'uncaughtException', new Error('bar'));
expect(overrideHandler).not.toHaveBeenCalled();
expect(handler1).toHaveBeenCalledWith(new Error('bar'));
});
it('handles unhandled promise rejections in browsers', function() {
const globalEventListeners = {};
const fakeGlobal = {
addEventListener(name, listener) {
globalEventListeners[name] = listener;
},
removeEventListener() {}
};
const globals = browserGlobals();
const handler = jasmine.createSpy('handler');
const overrideHandler = jasmine.createSpy('overrideHandler');
const errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
const errors = new privateUnderTest.GlobalErrors(
globals.global,
() => ({})
);
errors.install();
errors.pushListener(handler);
@@ -405,7 +671,7 @@ describe('GlobalErrors', function() {
const reason = new Error('bar');
globalEventListeners['unhandledrejection']({ reason: reason });
dispatchEvent(globals.listeners, 'unhandledrejection', { reason });
expect(overrideHandler).toHaveBeenCalledWith(
jasmine.objectContaining({
@@ -418,32 +684,21 @@ describe('GlobalErrors', function() {
});
it('handles unhandled promise rejections in Node', function() {
const globalEventListeners = {};
const fakeGlobal = {
process: {
on(name, listener) {
globalEventListeners[name] = listener;
},
removeListener() {},
listeners(name) {
return globalEventListeners[name];
},
removeAllListeners(name) {
globalEventListeners[name] = null;
}
}
};
const globals = nodeGlobals();
const handler0 = jasmine.createSpy('handler0');
const handler1 = jasmine.createSpy('handler1');
const overrideHandler = jasmine.createSpy('overrideHandler');
const errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
const errors = new privateUnderTest.GlobalErrors(
globals.global,
() => ({})
);
errors.install();
errors.pushListener(handler0);
errors.setOverrideListener(overrideHandler, () => {});
errors.pushListener(handler1);
globalEventListeners['unhandledRejection'](new Error('nope'));
dispatchEvent(globals.listeners, 'unhandledRejection', new Error('nope'));
expect(overrideHandler).toHaveBeenCalledWith(new Error('nope'));
expect(handler0).not.toHaveBeenCalled();
@@ -451,7 +706,7 @@ describe('GlobalErrors', function() {
});
it('throws if there is already an override handler', function() {
const errors = new jasmineUnderTest.GlobalErrors(browserGlobal());
const errors = new privateUnderTest.GlobalErrors(browserGlobals().global);
errors.setOverrideListener(() => {}, () => {});
expect(function() {
@@ -463,7 +718,7 @@ describe('GlobalErrors', function() {
describe('#removeOverrideListener', function() {
it("calls the handler's onRemove callback", function() {
const onRemove = jasmine.createSpy('onRemove');
const errors = new jasmineUnderTest.GlobalErrors(browserGlobal());
const errors = new privateUnderTest.GlobalErrors(browserGlobals().global);
errors.setOverrideListener(() => {}, onRemove);
errors.removeOverrideListener();
@@ -472,43 +727,69 @@ describe('GlobalErrors', function() {
});
it('does not throw if there is no handler', function() {
const errors = new jasmineUnderTest.GlobalErrors(browserGlobal());
const errors = new privateUnderTest.GlobalErrors(browserGlobals().global);
expect(() => errors.removeOverrideListener()).not.toThrow();
});
});
function browserGlobal() {
function browserGlobals() {
const listeners = {
error: [],
unhandledrejection: [],
rejectionhandled: []
};
return {
listeners_: { error: [], unhandledrejection: [] },
addEventListener(eventName, listener) {
this.listeners_[eventName].push(listener);
},
removeEventListener(eventName, listener) {
this.listeners_[eventName] = this.listeners_[eventName].filter(
l => l !== listener
);
listeners,
global: {
addEventListener(eventName, listener) {
listeners[eventName].push(listener);
},
removeEventListener(eventName, listener) {
listeners[eventName] = listeners[eventName].filter(
l => l !== listener
);
}
}
};
}
function dispatchErrorEvent(global, event) {
expect(global.listeners_.error.length)
.withContext('number of error listeners')
.toBeGreaterThan(0);
for (const l of global.listeners_.error) {
l(event);
}
function nodeGlobals() {
const listeners = {
uncaughtException: [],
unhandledRejection: [],
rejectionHandled: []
};
return {
listeners,
global: {
process: {
on(eventName, listener) {
listeners[eventName].push(listener);
},
removeListener(eventName, listener) {
listeners[eventName] = listeners[eventName].filter(
l => l !== listener
);
},
removeAllListeners(eventName) {
listeners[eventName] = [];
},
listeners(eventName) {
return listeners[eventName];
}
}
}
};
}
function dispatchUnhandledRejectionEvent(global, event) {
expect(global.listeners_.unhandledrejection.length)
.withContext('number of unhandledrejection listeners')
function dispatchEvent(listeners, eventName, ...args) {
expect(listeners[eventName].length)
.withContext(`number of ${eventName} listeners`)
.toBeGreaterThan(0);
for (const l of global.listeners_.unhandledrejection) {
l(event);
for (const l of listeners[eventName]) {
l.apply(null, args);
}
}
});

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

@@ -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() {
@@ -195,7 +211,7 @@ describe('QueueRunner', function() {
}
},
failFn = jasmine.createSpy('fail'),
queueRunner = new jasmineUnderTest.QueueRunner({
queueRunner = new privateUnderTest.QueueRunner({
queueableFns: [queueableFn1],
fail: failFn,
onComplete: function() {
@@ -211,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() {
@@ -221,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
});
@@ -239,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
@@ -272,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
@@ -287,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
@@ -310,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
@@ -340,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
@@ -364,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
});
@@ -383,7 +396,7 @@ describe('QueueRunner', function() {
}
},
nextQueueableFn = { fn: jasmine.createSpy('nextFn') },
queueRunner = new jasmineUnderTest.QueueRunner({
queueRunner = new privateUnderTest.QueueRunner({
queueableFns: [queueableFn, nextQueueableFn]
});
queueRunner.execute();
@@ -399,7 +412,7 @@ describe('QueueRunner', function() {
doneReturn = done();
}
};
const queueRunner = new jasmineUnderTest.QueueRunner({
const queueRunner = new privateUnderTest.QueueRunner({
queueableFns: [queueableFn]
});
@@ -409,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();
},
@@ -421,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
@@ -474,9 +486,9 @@ describe('QueueRunner', function() {
errorListeners.pop();
}
},
clearStack = jasmine.createSpy('clearStack'),
clearStack = jasmine.createSpyObj('clearStack', ['clearStack']),
onException = jasmine.createSpy('onException'),
queueRunner = new jasmineUnderTest.QueueRunner({
queueRunner = new privateUnderTest.QueueRunner({
queueableFns: [queueableFn],
globalErrors: globalErrors,
clearStack: clearStack,
@@ -486,10 +498,10 @@ describe('QueueRunner', function() {
queueRunner.execute();
jasmine.clock().tick();
expect(clearStack).toHaveBeenCalled();
expect(clearStack.clearStack).toHaveBeenCalled();
expect(errorListeners.length).toEqual(1);
errorListeners[0](error);
clearStack.calls.argsFor(0)[0]();
clearStack.clearStack.calls.argsFor(0)[0]();
expect(onException).toHaveBeenCalledWith(error);
});
});
@@ -532,7 +544,7 @@ describe('QueueRunner', function() {
return p2;
}
},
queueRunner = new jasmineUnderTest.QueueRunner({
queueRunner = new privateUnderTest.QueueRunner({
queueableFns: [queueableFn1, queueableFn2],
onComplete: onComplete
});
@@ -563,7 +575,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
});
@@ -581,13 +593,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
});
@@ -606,10 +617,9 @@ describe('QueueRunner', function() {
});
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
});
@@ -630,7 +640,7 @@ describe('QueueRunner', 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
});
@@ -648,7 +658,7 @@ describe('QueueRunner', function() {
}
},
onExceptionCallback = jasmine.createSpy('on exception callback'),
queueRunner = new jasmineUnderTest.QueueRunner({
queueRunner = new privateUnderTest.QueueRunner({
queueableFns: [queueableFn],
onException: onExceptionCallback
});
@@ -660,13 +670,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]
});
@@ -679,7 +688,7 @@ describe('QueueRunner', function() {
const SkipPolicy = jasmine.createSpy('SkipPolicy ctor');
const queueableFns = [{ fn: () => {} }, { fn: () => {} }];
new jasmineUnderTest.QueueRunner({
new privateUnderTest.QueueRunner({
queueableFns,
SkipPolicy
});
@@ -701,7 +710,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;
@@ -722,7 +731,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;
@@ -748,17 +757,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)
);
});
@@ -774,9 +783,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();
@@ -805,7 +814,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);
@@ -815,7 +824,7 @@ describe('QueueRunner', function() {
}
},
queueableFns: [queueableFn, nextQueueableFn, cleanupFn],
SkipPolicy: jasmineUnderTest.CompleteOnFirstErrorSkipPolicy
SkipPolicy: privateUnderTest.CompleteOnFirstErrorSkipPolicy
});
queueRunner.execute();
@@ -834,9 +843,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();
@@ -856,9 +865,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();
@@ -872,7 +881,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
});
@@ -899,22 +908,22 @@ describe('QueueRunner', function() {
},
afterFn = { fn: jasmine.createSpy('afterFn') },
completeCallback = jasmine.createSpy('completeCallback'),
clearStack = jasmine.createSpy('clearStack'),
queueRunner = new jasmineUnderTest.QueueRunner({
clearStack = jasmine.createSpyObj('clearStack', ['clearStack']),
queueRunner = new privateUnderTest.QueueRunner({
queueableFns: [asyncFn, afterFn],
clearStack: clearStack,
onComplete: completeCallback
});
clearStack.and.callFake(function(fn) {
clearStack.clearStack.and.callFake(function(fn) {
fn();
});
queueRunner.execute();
jasmine.clock().tick();
expect(afterFn.fn).toHaveBeenCalled();
expect(clearStack).toHaveBeenCalled();
clearStack.calls.argsFor(0)[0]();
expect(clearStack.clearStack).toHaveBeenCalled();
clearStack.clearStack.calls.argsFor(0)[0]();
expect(completeCallback).toHaveBeenCalled();
});
});
@@ -924,7 +933,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 }]
});
});
@@ -937,7 +946,7 @@ describe('QueueRunner', function() {
this.queueRunner.execute();
expect(context.constructor).toBe(jasmineUnderTest.UserContext);
expect(context.constructor).toBe(privateUnderTest.UserContext);
});
});
@@ -947,8 +956,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,10 +12,10 @@ 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']);
@@ -23,9 +23,9 @@ describe('ReportDispatcher', function() {
dispatcher.addReporter(reporter);
dispatcher.addReporter(anotherReporter);
dispatcher.foo(123, 456);
dispatcher.foo({ an: 'event' });
expect(queueRunnerFactory).toHaveBeenCalledWith(
expect(runQueue).toHaveBeenCalledWith(
jasmine.objectContaining({
queueableFns: [
{ fn: jasmine.any(Function) },
@@ -35,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');
dispatcher.bar({ another: 'event' });
expect(queueRunnerFactory).toHaveBeenCalledWith(
expect(runQueue).toHaveBeenCalledWith(
jasmine.objectContaining({
queueableFns: [
{ fn: jasmine.any(Function) },
@@ -58,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: []
})
@@ -85,90 +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']);
dispatcher.provideFallbackReporter(reporter);
dispatcher.foo(123, 456);
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']);
dispatcher.provideFallbackReporter(fallbackReporter);
dispatcher.addReporter(reporter);
dispatcher.foo(123, 456);
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']);
dispatcher.addReporter(reporter1);
dispatcher.foo(123);
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);
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
});

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

@@ -0,0 +1,506 @@
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.markedPending = attrs.markedPending || false;
this.sharedUserContext = function() {
return attrs.userContext || {};
};
this.startedEvent = jasmine.createSpy('startedEvent');
this.doneEvent = jasmine.createSpy('doneEvent');
this.hasOwnFailedExpectations = jasmine.createSpy(
'hasOwnFailedExpectations'
);
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 (!Array.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' }
});
topSuite.doneEvent.and.returnValue({});
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' }
});
topSuite.doneEvent.and.returnValue({});
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
});
suite.startedEvent.and.returnValue('suite started event');
runQueue.calls.mostRecent().args[0].queueableFns[0].fn('foo');
expect(reportDispatcher.suiteStarted).toHaveBeenCalledWith(
'suite started event'
);
suite.doneEvent.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);
parent.startedEvent.and.returnValue('parent suite started event');
queueableFns[0].fn();
expect(reportDispatcher.suiteStarted).toHaveBeenCalledWith(
'parent suite started event'
);
verifyAndFinishSpec(spec, queueableFns[1], true);
parent.doneEvent.and.returnValue('parent suite done event');
runQueue.calls.argsFor(1)[0].onComplete();
expect(reportDispatcher.suiteDone).toHaveBeenCalledWith(
'parent suite done event'
);
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

@@ -1,6 +1,6 @@
describe('ClearStack', function() {
describe('StackClearer', function() {
it('works in an integrationy way', function(done) {
const clearStack = jasmineUnderTest.getClearStack(
const { clearStack } = privateUnderTest.getStackClearer(
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.getStackClearer(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() {
@@ -32,17 +73,6 @@ describe('ClearStack', function() {
}
};
});
describe('when MessageChannel is unavailable', function() {
usesQueueMicrotaskWithSetTimeout(function() {
return {
navigator: {
userAgent: 'CERN-LineMode/2.15 libwww/2.17b3',
MessageChannel: undefined
}
};
});
});
});
describe('in Node', function() {
@@ -63,7 +93,7 @@ describe('ClearStack', function() {
...makeGlobal(),
MessageChannel: fakeMessageChannel
};
const clearStack = jasmineUnderTest.getClearStack(global);
const { clearStack } = privateUnderTest.getStackClearer(global);
let called = false;
clearStack(function() {
@@ -84,7 +114,7 @@ describe('ClearStack', function() {
return fakeChannel;
}
};
const clearStack = jasmineUnderTest.getClearStack(global);
const { clearStack } = privateUnderTest.getStackClearer(global);
for (let i = 0; i < 9; i++) {
clearStack(function() {});
@@ -109,7 +139,7 @@ describe('ClearStack', function() {
setTimeout,
MessageChannel: fakeMessageChannel
};
const clearStack = jasmineUnderTest.getClearStack(global);
const { clearStack } = privateUnderTest.getStackClearer(global);
const fn = jasmine.createSpy('second clearStack function');
clearStack(function() {
@@ -129,7 +159,7 @@ describe('ClearStack', function() {
fn();
}
};
const clearStack = jasmineUnderTest.getClearStack(global);
const { clearStack } = privateUnderTest.getStackClearer(global);
let called = false;
clearStack(function() {
@@ -139,30 +169,82 @@ describe('ClearStack', function() {
expect(called).toBe(true);
});
it('uses setTimeout instead of queueMicrotask every 10 calls to make sure we release the CPU', function() {
const queueMicrotask = jasmine.createSpy('queueMicrotask');
const setTimeout = jasmine.createSpy('setTimeout');
const global = {
...makeGlobal(),
queueMicrotask,
setTimeout
};
const clearStack = jasmineUnderTest.getClearStack(global);
function hasSetTimeoutBehavior(configure) {
it('uses setTimeout instead of queueMicrotask every 10 calls', function() {
const queueMicrotask = jasmine.createSpy('queueMicrotask');
const setTimeout = jasmine.createSpy('setTimeout');
const global = {
...makeGlobal(),
queueMicrotask,
setTimeout
};
const stackClearer = privateUnderTest.getStackClearer(global);
for (let i = 0; i < 9; i++) {
clearStack(function() {});
}
if (configure) {
configure(stackClearer);
}
expect(queueMicrotask).toHaveBeenCalled();
expect(setTimeout).not.toHaveBeenCalled();
for (let i = 0; i < 9; i++) {
stackClearer.clearStack(function() {});
}
clearStack(function() {});
expect(queueMicrotask).toHaveBeenCalledTimes(9);
expect(setTimeout).toHaveBeenCalledTimes(1);
expect(queueMicrotask).toHaveBeenCalled();
expect(setTimeout).not.toHaveBeenCalled();
clearStack(function() {});
expect(queueMicrotask).toHaveBeenCalledTimes(10);
expect(setTimeout).toHaveBeenCalledTimes(1);
stackClearer.clearStack(function() {});
expect(queueMicrotask).toHaveBeenCalledTimes(9);
expect(setTimeout).toHaveBeenCalledTimes(1);
stackClearer.clearStack(function() {});
expect(queueMicrotask).toHaveBeenCalledTimes(10);
expect(setTimeout).toHaveBeenCalledTimes(1);
});
}
hasSetTimeoutBehavior();
describe('With yield strategy explicitly set to count', function() {
hasSetTimeoutBehavior(function(stackClearer) {
stackClearer.setSafariYieldStrategy('count');
});
});
describe('With yield strategy set to time', function() {
beforeEach(function() {
jasmine.clock().install();
jasmine.clock().mockDate();
});
afterEach(function() {
jasmine.clock().uninstall();
});
it('uses setTimeout instead of queueMicrotask every 25 milliseconds', function() {
const queueMicrotask = jasmine.createSpy('queueMicrotask');
const setTimeout = jasmine.createSpy('setTimeout');
const global = {
...makeGlobal(),
queueMicrotask,
setTimeout
};
const stackClearer = privateUnderTest.getStackClearer(global);
stackClearer.setSafariYieldStrategy('time');
// 10+ counts should not trigger a setTimeout if they happen fast enough
jasmine.clock().tick(24);
for (let i = 0; i < 11; i++) {
stackClearer.clearStack(function() {});
}
expect(queueMicrotask).toHaveBeenCalled();
expect(setTimeout).not.toHaveBeenCalled();
queueMicrotask.calls.reset();
jasmine.clock().tick(1);
stackClearer.clearStack(function() {});
expect(queueMicrotask).not.toHaveBeenCalled();
expect(setTimeout).toHaveBeenCalledTimes(1);
});
});
}
@@ -174,7 +256,7 @@ describe('ClearStack', function() {
fn();
}
};
const clearStack = jasmineUnderTest.getClearStack(global);
const { clearStack } = privateUnderTest.getStackClearer(global);
let called = false;
clearStack(function() {
@@ -192,7 +274,7 @@ describe('ClearStack', function() {
queueMicrotask,
setTimeout
};
const clearStack = jasmineUnderTest.getClearStack(global);
const { clearStack } = privateUnderTest.getStackClearer(global);
clearStack(function() {});
clearStack(function() {});

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) {
@@ -176,20 +190,132 @@ 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() {
jasmineUnderTest.Suite.prototype.handleException.and.callThrough();
privateUnderTest.Suite.prototype.handleException.and.callThrough();
const env = { configuration: () => ({}) };
const suiteBuilder = new jasmineUnderTest.SuiteBuilder({ env });
const suiteBuilder = new privateUnderTest.SuiteBuilder({ env });
suiteBuilder.topSuite.handleException(new Error('nope'));
suiteBuilder.parallelReset();
expect(suiteBuilder.topSuite.result).toEqual({
expect(suiteBuilder.topSuite.doneEvent()).toEqual({
id: suiteBuilder.topSuite.id,
description: 'Jasmine__TopLevel__Suite',
fullName: '',
status: 'passed',
failedExpectations: [],
deprecationWarnings: [],
duration: null,
@@ -201,7 +327,7 @@ describe('SuiteBuilder', function() {
it('removes children of the top suite', function() {
const env = { configuration: () => ({}) };
const suiteBuilder = new jasmineUnderTest.SuiteBuilder({ env });
const suiteBuilder = new privateUnderTest.SuiteBuilder({ env });
suiteBuilder.describe('a suite', function() {
suiteBuilder.it('a nested spec');
});
@@ -214,7 +340,7 @@ describe('SuiteBuilder', function() {
it('preserves top suite befores and afters', function() {
const env = { configuration: () => ({}) };
const suiteBuilder = new jasmineUnderTest.SuiteBuilder({ env });
const suiteBuilder = new privateUnderTest.SuiteBuilder({ env });
function beforeAll() {}
function beforeEach() {}
@@ -244,7 +370,7 @@ describe('SuiteBuilder', function() {
it('resets totalSpecsDefined', function() {
const env = { configuration: () => ({}) };
const suiteBuilder = new jasmineUnderTest.SuiteBuilder({ env });
const suiteBuilder = new privateUnderTest.SuiteBuilder({ env });
suiteBuilder.it('a spec');
suiteBuilder.parallelReset();
@@ -254,7 +380,7 @@ describe('SuiteBuilder', function() {
it('resets focusedRunables', function() {
const env = { configuration: () => ({}) };
const suiteBuilder = new jasmineUnderTest.SuiteBuilder({ env });
const suiteBuilder = new privateUnderTest.SuiteBuilder({ env });
suiteBuilder.fit('a spec', function() {});
suiteBuilder.parallelReset();

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.doneEvent().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');
expect(suite.doneEvent().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');
expect(suite.doneEvent().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.result.failedExpectations).toEqual([
expect(suite.doneEvent().status).toBe('failed');
expect(suite.doneEvent().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([]);
expect(suite.doneEvent().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,
@@ -173,12 +175,12 @@ describe('Suite', function() {
message: jasmine.stringMatching(/^Error: nope/)
})
);
expect(suite.result.failedExpectations).toEqual([]);
expect(suite.doneEvent().failedExpectations).toEqual([]);
});
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 = {
@@ -192,12 +194,12 @@ describe('Suite', function() {
suite.addExpectationResult(false, data, true);
expect(onLateError).not.toHaveBeenCalled();
expect(suite.result.failedExpectations.length).toEqual(1);
expect(suite.doneEvent().failedExpectations.length).toEqual(1);
});
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');
@@ -210,12 +212,12 @@ describe('Suite', function() {
message: jasmine.stringMatching(/^Error: oops/)
})
);
expect(suite.result.failedExpectations).toEqual([]);
expect(suite.doneEvent().failedExpectations).toEqual([]);
});
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');
@@ -223,11 +225,11 @@ describe('Suite', function() {
suite.handleException(error);
expect(onLateError).not.toHaveBeenCalled();
expect(suite.result.failedExpectations.length).toEqual(1);
expect(suite.doneEvent().failedExpectations.length).toEqual(1);
});
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',
@@ -246,24 +248,24 @@ describe('Suite', function() {
});
suite.startTimer();
suite.endTimer();
expect(suite.getResult().duration).toEqual(77000);
expect(suite.doneEvent().duration).toEqual(77000);
});
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');
expect(suite.doneEvent().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');
expect(suite.doneEvent().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,12 +330,12 @@ 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();
const result = suite.getResult();
const result = suite.doneEvent();
expect(result.status).toBe('passed');
expect(result.failedExpectations).toHaveSize(0);
});
@@ -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,89 @@ 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 a string', function() {
const suite = new privateUnderTest.Suite({});
expect(function() {
suite.setSuiteProperty({}, '');
}).toThrowError('Key must be a string');
});
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");
});
it('throws if the value is not JSON-serializable', function() {
const suite = new privateUnderTest.Suite({});
expect(function() {
const v = {};
v.self = v;
suite.setSuiteProperty('k', v);
}).toThrowError("Value can't be cloned");
});
});
describe('#startedEvent', function() {
it('includes only properties that are known before execution', function() {
const topSuite = new privateUnderTest.Suite({});
const parentSuite = new privateUnderTest.Suite({
id: 'suite1',
parentSuite: topSuite,
description: 'a parent suite'
});
const suite = new privateUnderTest.Suite({
id: 'suite2',
parentSuite,
reportedParentSuiteId: parentSuite.id,
description: 'a suite',
filename: 'somefile.js',
getPath() {
return ['a parent suite', 'a spec'];
},
queueableFn: { fn: () => {} }
});
expect(suite.startedEvent()).toEqual({
id: 'suite2',
parentSuiteId: 'suite1',
description: 'a suite',
fullName: 'a parent suite a suite',
filename: 'somefile.js'
});
});
});
});

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.doneEvent().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.doneEvent().status).toEqual('pending');
expect(spec.doneEvent().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.doneEvent().status).toEqual('pending');
expect(spec.doneEvent().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.doneEvent();
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,17 @@
describe('util', function() {
describe('isArray_', function() {
it('should return true if the argument is an array', function() {
expect(jasmineUnderTest.isArray_([])).toBe(true);
expect(jasmineUnderTest.isArray_(['a'])).toBe(true);
});
it('should return false if the argument is not an array', function() {
expect(jasmineUnderTest.isArray_(undefined)).toBe(false);
expect(jasmineUnderTest.isArray_({})).toBe(false);
expect(jasmineUnderTest.isArray_(function() {})).toBe(false);
expect(jasmineUnderTest.isArray_('foo')).toBe(false);
expect(jasmineUnderTest.isArray_(5)).toBe(false);
expect(jasmineUnderTest.isArray_(null)).toBe(false);
});
});
describe('isObject_', function() {
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 +29,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 +151,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 +162,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

@@ -0,0 +1,63 @@
describe('AllOf', function() {
it('matches a single value', function() {
const matchersUtil = new privateUnderTest.MatchersUtil();
const allOf = new privateUnderTest.AllOf('foo');
expect(allOf.asymmetricMatch('foo', matchersUtil)).toBeTrue();
});
it('matches a single matcher', function() {
const matchersUtil = new privateUnderTest.MatchersUtil();
const allOf = new privateUnderTest.AllOf(
new privateUnderTest.StringContaining('oo')
);
expect(allOf.asymmetricMatch('foo', matchersUtil)).toBeTrue();
});
it('matches multiple matchers', function() {
const matchersUtil = new privateUnderTest.MatchersUtil();
const allOf = new privateUnderTest.AllOf(
new privateUnderTest.StringContaining('o'),
new privateUnderTest.StringContaining('f')
);
expect(allOf.asymmetricMatch('foo', matchersUtil)).toBeTrue();
});
it('does not match when value does not match', function() {
const matchersUtil = new privateUnderTest.MatchersUtil();
const allOf = new privateUnderTest.AllOf('bar');
expect(allOf.asymmetricMatch('foo', matchersUtil)).toBeFalse();
});
it('does not match when any matchers fail', function() {
const matchersUtil = new privateUnderTest.MatchersUtil();
const allOf = new privateUnderTest.AllOf(
new privateUnderTest.StringContaining('o'),
new privateUnderTest.StringContaining('x')
);
expect(allOf.asymmetricMatch('foo', matchersUtil)).toBeFalse();
});
it('jasmineToStrings itself', function() {
const matcher = new privateUnderTest.AllOf('o');
const pp = jasmine.createSpy('pp').and.returnValue('sample');
expect(matcher.jasmineToString(pp)).toEqual('<jasmine.allOf(sample)>');
expect(pp).toHaveBeenCalledWith(['o']);
});
describe('when called without an argument', function() {
it('tells the user to pass a constructor argument', function() {
expect(function() {
new privateUnderTest.AllOf();
}).toThrowError(
TypeError,
'jasmine.allOf() expects at least one argument to be passed.'
);
});
});
});

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>');
});

View File

@@ -1,12 +1,12 @@
describe('ArrayContaining', function() {
it('matches any actual to an empty array', function() {
const containing = new jasmineUnderTest.ArrayContaining([]);
const containing = new privateUnderTest.ArrayContaining([]);
expect(containing.asymmetricMatch('foo')).toBe(true);
});
it('does not work when not passed an array', function() {
const containing = new jasmineUnderTest.ArrayContaining('foo');
const containing = new privateUnderTest.ArrayContaining('foo');
expect(function() {
containing.asymmetricMatch([]);
@@ -14,36 +14,36 @@ describe('ArrayContaining', function() {
});
it('matches when the item is in the actual', function() {
const containing = new jasmineUnderTest.ArrayContaining(['foo']);
const matchersUtil = new jasmineUnderTest.MatchersUtil();
const containing = new privateUnderTest.ArrayContaining(['foo']);
const matchersUtil = new privateUnderTest.MatchersUtil();
expect(containing.asymmetricMatch(['foo'], matchersUtil)).toBe(true);
});
it('matches when additional items are in the actual', function() {
const containing = new jasmineUnderTest.ArrayContaining(['foo']);
const matchersUtil = new jasmineUnderTest.MatchersUtil();
const containing = new privateUnderTest.ArrayContaining(['foo']);
const matchersUtil = new privateUnderTest.MatchersUtil();
expect(containing.asymmetricMatch(['foo', 'bar'], matchersUtil)).toBe(true);
});
it('does not match when the item is not in the actual', function() {
const containing = new jasmineUnderTest.ArrayContaining(['foo']);
const matchersUtil = new jasmineUnderTest.MatchersUtil();
const containing = new privateUnderTest.ArrayContaining(['foo']);
const matchersUtil = new privateUnderTest.MatchersUtil();
expect(containing.asymmetricMatch(['bar'], matchersUtil)).toBe(false);
});
it('does not match when the actual is not an array', function() {
const containing = new jasmineUnderTest.ArrayContaining(['foo']);
const matchersUtil = new jasmineUnderTest.MatchersUtil();
const containing = new privateUnderTest.ArrayContaining(['foo']);
const matchersUtil = new privateUnderTest.MatchersUtil();
expect(containing.asymmetricMatch('foo', matchersUtil)).toBe(false);
});
it('jasmineToStrings itself', function() {
const sample = [],
matcher = new jasmineUnderTest.ArrayContaining(sample),
matcher = new privateUnderTest.ArrayContaining(sample),
pp = jasmine.createSpy('pp').and.returnValue('sample');
expect(matcher.jasmineToString(pp)).toEqual(
@@ -64,8 +64,8 @@ describe('ArrayContaining', function() {
return true;
}
};
const containing = new jasmineUnderTest.ArrayContaining(['fooVal']);
const matchersUtil = new jasmineUnderTest.MatchersUtil({
const containing = new privateUnderTest.ArrayContaining(['fooVal']);
const matchersUtil = new privateUnderTest.MatchersUtil({
customTesters: [tester]
});

View File

@@ -1,13 +1,13 @@
describe('ArrayWithExactContents', function() {
it('matches an array with the same items in a different order', function() {
const matcher = new jasmineUnderTest.ArrayWithExactContents(['a', 2, /a/]);
const matchersUtil = new jasmineUnderTest.MatchersUtil();
const matcher = new privateUnderTest.ArrayWithExactContents(['a', 2, /a/]);
const matchersUtil = new privateUnderTest.MatchersUtil();
expect(matcher.asymmetricMatch([2, 'a', /a/], matchersUtil)).toBe(true);
});
it('does not work when not passed an array', function() {
const matcher = new jasmineUnderTest.ArrayWithExactContents('foo');
const matcher = new privateUnderTest.ArrayWithExactContents('foo');
expect(function() {
matcher.asymmetricMatch([]);
@@ -15,8 +15,8 @@ describe('ArrayWithExactContents', function() {
});
it('does not match when an item is missing', function() {
const matcher = new jasmineUnderTest.ArrayWithExactContents(['a', 2, /a/]);
const matchersUtil = new jasmineUnderTest.MatchersUtil();
const matcher = new privateUnderTest.ArrayWithExactContents(['a', 2, /a/]);
const matchersUtil = new privateUnderTest.MatchersUtil();
expect(matcher.asymmetricMatch(['a', 2], matchersUtil)).toBe(false);
expect(matcher.asymmetricMatch(['a', 2, undefined], matchersUtil)).toBe(
@@ -25,15 +25,15 @@ describe('ArrayWithExactContents', function() {
});
it('does not match when there is an extra item', function() {
const matcher = new jasmineUnderTest.ArrayWithExactContents(['a']);
const matchersUtil = new jasmineUnderTest.MatchersUtil();
const matcher = new privateUnderTest.ArrayWithExactContents(['a']);
const matchersUtil = new privateUnderTest.MatchersUtil();
expect(matcher.asymmetricMatch(['a', 2], matchersUtil)).toBe(false);
});
it('jasmineToStrings itself', function() {
const sample = [],
matcher = new jasmineUnderTest.ArrayWithExactContents(sample),
matcher = new privateUnderTest.ArrayWithExactContents(sample),
pp = jasmine.createSpy('pp').and.returnValue('sample');
expect(matcher.jasmineToString(pp)).toEqual(
@@ -54,8 +54,8 @@ describe('ArrayWithExactContents', function() {
return true;
}
};
const matcher = new jasmineUnderTest.ArrayWithExactContents(['fooVal']);
const matchersUtil = new jasmineUnderTest.MatchersUtil({
const matcher = new privateUnderTest.ArrayWithExactContents(['fooVal']);
const matchersUtil = new privateUnderTest.MatchersUtil({
customTesters: [tester]
});

View File

@@ -1,20 +1,20 @@
describe('Empty', function() {
it('matches an empty object', function() {
const empty = new jasmineUnderTest.Empty();
const empty = new privateUnderTest.Empty();
expect(empty.asymmetricMatch({})).toBe(true);
expect(empty.asymmetricMatch({ undefined: false })).toBe(false);
});
it('matches an empty array', function() {
const empty = new jasmineUnderTest.Empty();
const empty = new privateUnderTest.Empty();
expect(empty.asymmetricMatch([])).toBe(true);
expect(empty.asymmetricMatch([1, 12, 3])).toBe(false);
});
it('matches an empty string', function() {
const empty = new jasmineUnderTest.Empty();
const empty = new privateUnderTest.Empty();
expect(empty.asymmetricMatch('')).toBe(true);
expect(empty.asymmetricMatch('')).toBe(true);
@@ -22,7 +22,7 @@ describe('Empty', function() {
});
it('matches an empty map', function() {
const empty = new jasmineUnderTest.Empty();
const empty = new privateUnderTest.Empty();
const fullMap = new Map();
fullMap.set('thing', 2);
@@ -31,7 +31,7 @@ describe('Empty', function() {
});
it('matches an empty set', function() {
const empty = new jasmineUnderTest.Empty();
const empty = new privateUnderTest.Empty();
const fullSet = new Set();
fullSet.add(3);
@@ -40,7 +40,7 @@ describe('Empty', function() {
});
it('matches an empty typed array', function() {
const empty = new jasmineUnderTest.Empty();
const empty = new privateUnderTest.Empty();
expect(empty.asymmetricMatch(new Int16Array())).toBe(true);
expect(empty.asymmetricMatch(new Int16Array([1, 2]))).toBe(false);

View File

@@ -1,6 +1,6 @@
describe('Falsy', function() {
it('is true for an empty string', function() {
const falsy = new jasmineUnderTest.Falsy();
const falsy = new privateUnderTest.Falsy();
expect(falsy.asymmetricMatch('')).toBe(true);
expect(falsy.asymmetricMatch('')).toBe(true);
@@ -8,7 +8,7 @@ describe('Falsy', function() {
});
it('is false for a number that is 0', function() {
const falsy = new jasmineUnderTest.Falsy(Number);
const falsy = new privateUnderTest.Falsy(Number);
expect(falsy.asymmetricMatch(1)).toBe(false);
expect(falsy.asymmetricMatch(0)).toBe(true);
@@ -17,20 +17,20 @@ describe('Falsy', function() {
});
it('is true for a null or undefined', function() {
const falsy = new jasmineUnderTest.Falsy(Function);
const falsy = new privateUnderTest.Falsy(Function);
expect(falsy.asymmetricMatch(null)).toBe(true);
expect(falsy.asymmetricMatch(undefined)).toBe(true);
});
it('is true for NaN', function() {
const falsy = new jasmineUnderTest.Falsy(Object);
const falsy = new privateUnderTest.Falsy(Object);
expect(falsy.asymmetricMatch(NaN)).toBe(true);
});
it('is true for a false Boolean', function() {
const falsy = new jasmineUnderTest.Falsy(Boolean);
const falsy = new privateUnderTest.Falsy(Boolean);
expect(falsy.asymmetricMatch(false)).toBe(true);
expect(falsy.asymmetricMatch(true)).toBe(false);

View File

@@ -1,28 +1,28 @@
describe('Is', function() {
it('passes for primitives that are ===', function() {
const exactly = new jasmineUnderTest.Is(17);
const exactly = new privateUnderTest.Is(17);
expect(exactly.asymmetricMatch(17)).toBeTrue();
});
it('fails for primitives that are not ===', function() {
const exactly = new jasmineUnderTest.Is(42);
const exactly = new privateUnderTest.Is(42);
expect(exactly.asymmetricMatch('42')).toBeFalse();
});
it('passes for the same object instance', function() {
const obj = {};
const exactly = new jasmineUnderTest.Is(obj);
const exactly = new privateUnderTest.Is(obj);
expect(exactly.asymmetricMatch(obj)).toBeTrue();
});
it('fails for different object instances, even if they are deep value equal', function() {
const exactly = new jasmineUnderTest.Is({});
const exactly = new privateUnderTest.Is({});
expect(exactly.asymmetricMatch({})).toBeFalse();
});
it('describes itself for use in diffs and pretty printing', function() {
const exactly = new jasmineUnderTest.Is({ foo: ['bar'] });
const pp = jasmineUnderTest.basicPrettyPrinter_;
const exactly = new privateUnderTest.Is({ foo: ['bar'] });
const pp = privateUnderTest.basicPrettyPrinter;
expect(exactly.jasmineToString(pp)).toEqual(
"<jasmine.is(Object({ foo: [ 'bar' ] }))>"
);

View File

@@ -1,7 +1,7 @@
describe('MapContaining', function() {
it('matches any actual map to an empty map', function() {
const actualMap = new Map([['foo', 'bar']]);
const containing = new jasmineUnderTest.MapContaining(new Map());
const containing = new privateUnderTest.MapContaining(new Map());
expect(containing.asymmetricMatch(actualMap)).toBe(true);
});
@@ -17,8 +17,8 @@ describe('MapContaining', function() {
[{ foo: 'bar' }, 'baz'],
['foo', [1, 2, 3]]
]);
const containing = new jasmineUnderTest.MapContaining(containingMap);
const matchersUtil = new jasmineUnderTest.MatchersUtil();
const containing = new privateUnderTest.MapContaining(containingMap);
const matchersUtil = new privateUnderTest.MatchersUtil();
expect(containing.asymmetricMatch(actualMap, matchersUtil)).toBe(true);
});
@@ -33,8 +33,8 @@ describe('MapContaining', function() {
[{ foo: 'bar' }, 'baz'],
['foo', [1, 2, 3]]
]);
const containing = new jasmineUnderTest.MapContaining(containingMap);
const matchersUtil = new jasmineUnderTest.MatchersUtil();
const containing = new privateUnderTest.MapContaining(containingMap);
const matchersUtil = new privateUnderTest.MatchersUtil();
expect(containing.asymmetricMatch(actualMap, matchersUtil)).toBe(false);
});
@@ -43,8 +43,8 @@ describe('MapContaining', function() {
const actualMap = new Map([['foo', [1, 2, 3]], [{ foo: 'bar' }, 'baz']]);
const containingMap = new Map([[{ foo: 'bar' }, 'baz'], ['foo', [1, 2]]]);
const containing = new jasmineUnderTest.MapContaining(containingMap);
const matchersUtil = new jasmineUnderTest.MatchersUtil();
const containing = new privateUnderTest.MapContaining(containingMap);
const matchersUtil = new privateUnderTest.MatchersUtil();
expect(containing.asymmetricMatch(actualMap, matchersUtil)).toBe(false);
});
@@ -60,8 +60,8 @@ describe('MapContaining', function() {
[jasmineUnderTest.stringMatching(/^foo\d/), 'bar'],
['baz', jasmineUnderTest.arrayContaining([2, 3])]
]);
const containing = new jasmineUnderTest.MapContaining(containingMap);
const matchersUtil = new jasmineUnderTest.MatchersUtil();
const containing = new privateUnderTest.MapContaining(containingMap);
const matchersUtil = new privateUnderTest.MatchersUtil();
expect(containing.asymmetricMatch(actualMap, matchersUtil)).toBe(true);
});
@@ -73,8 +73,8 @@ describe('MapContaining', function() {
[jasmineUnderTest.stringMatching(/^foo\d/), 'bar'],
['baz', jasmineUnderTest.arrayContaining([2, 3])]
]);
const containing = new jasmineUnderTest.MapContaining(containingMap);
const matchersUtil = new jasmineUnderTest.MatchersUtil();
const containing = new privateUnderTest.MapContaining(containingMap);
const matchersUtil = new privateUnderTest.MatchersUtil();
expect(containing.asymmetricMatch(actualMap, matchersUtil)).toBe(false);
});
@@ -86,8 +86,8 @@ describe('MapContaining', function() {
[jasmineUnderTest.stringMatching(/^foo\d/), 'bar'],
['baz', jasmineUnderTest.arrayContaining([4, 5])]
]);
const containing = new jasmineUnderTest.MapContaining(containingMap);
const matchersUtil = new jasmineUnderTest.MatchersUtil();
const containing = new privateUnderTest.MapContaining(containingMap);
const matchersUtil = new privateUnderTest.MatchersUtil();
expect(containing.asymmetricMatch(actualMap, matchersUtil)).toBe(false);
});
@@ -100,11 +100,11 @@ describe('MapContaining', function() {
]);
const containingMap = new Map([
['foo', new jasmineUnderTest.MapContaining(new Map([['foo1', 1]]))],
[new jasmineUnderTest.MapContaining(new Map([[2, 'bar2']])), 'bar']
['foo', new privateUnderTest.MapContaining(new Map([['foo1', 1]]))],
[new privateUnderTest.MapContaining(new Map([[2, 'bar2']])), 'bar']
]);
const containing = new jasmineUnderTest.MapContaining(containingMap);
const matchersUtil = new jasmineUnderTest.MatchersUtil();
const containing = new privateUnderTest.MapContaining(containingMap);
const matchersUtil = new privateUnderTest.MatchersUtil();
expect(containing.asymmetricMatch(actualMap, matchersUtil)).toBe(true);
});
@@ -117,10 +117,10 @@ describe('MapContaining', function() {
: a === b;
}
const actualMap = new Map([['foo', -1]]);
const containing = new jasmineUnderTest.MapContaining(
const containing = new privateUnderTest.MapContaining(
new Map([['foo', -2]])
);
const matchersUtil = new jasmineUnderTest.MatchersUtil({
const matchersUtil = new privateUnderTest.MatchersUtil({
customTesters: [tester]
});
@@ -130,13 +130,13 @@ describe('MapContaining', function() {
it('does not match when actual is not a map', function() {
const containingMap = new Map([['foo', 'bar']]);
expect(
new jasmineUnderTest.MapContaining(containingMap).asymmetricMatch('foo')
new privateUnderTest.MapContaining(containingMap).asymmetricMatch('foo')
).toBe(false);
expect(
new jasmineUnderTest.MapContaining(containingMap).asymmetricMatch(-1)
new privateUnderTest.MapContaining(containingMap).asymmetricMatch(-1)
).toBe(false);
expect(
new jasmineUnderTest.MapContaining(containingMap).asymmetricMatch({
new privateUnderTest.MapContaining(containingMap).asymmetricMatch({
foo: 'bar'
})
).toBe(false);
@@ -144,7 +144,7 @@ describe('MapContaining', function() {
it('throws an error when sample is not a map', function() {
expect(function() {
new jasmineUnderTest.MapContaining({ foo: 'bar' }).asymmetricMatch(
new privateUnderTest.MapContaining({ foo: 'bar' }).asymmetricMatch(
new Map()
);
}).toThrowError(/You must provide a map/);
@@ -152,7 +152,7 @@ describe('MapContaining', function() {
it('defines a `jasmineToString` method', function() {
const sample = new Map(),
containing = new jasmineUnderTest.MapContaining(sample),
containing = new privateUnderTest.MapContaining(sample),
pp = jasmine.createSpy('pp').and.returnValue('sample');
expect(containing.jasmineToString(pp)).toEqual(

View File

@@ -1,20 +1,20 @@
describe('NotEmpty', function() {
it('matches a non empty object', function() {
const notEmpty = new jasmineUnderTest.NotEmpty();
const notEmpty = new privateUnderTest.NotEmpty();
expect(notEmpty.asymmetricMatch({ undefined: false })).toBe(true);
expect(notEmpty.asymmetricMatch({})).toBe(false);
});
it('matches a non empty array', function() {
const notEmpty = new jasmineUnderTest.NotEmpty();
const notEmpty = new privateUnderTest.NotEmpty();
expect(notEmpty.asymmetricMatch([1, 12, 3])).toBe(true);
expect(notEmpty.asymmetricMatch([])).toBe(false);
});
it('matches a non empty string', function() {
const notEmpty = new jasmineUnderTest.NotEmpty();
const notEmpty = new privateUnderTest.NotEmpty();
expect(notEmpty.asymmetricMatch('12312')).toBe(true);
expect(notEmpty.asymmetricMatch('')).toBe(false);
@@ -22,7 +22,7 @@ describe('NotEmpty', function() {
});
it('matches a non empty map', function() {
const notEmpty = new jasmineUnderTest.NotEmpty();
const notEmpty = new privateUnderTest.NotEmpty();
const fullMap = new Map();
fullMap.set('one', 1);
const emptyMap = new Map();
@@ -32,7 +32,7 @@ describe('NotEmpty', function() {
});
it('matches a non empty set', function() {
const notEmpty = new jasmineUnderTest.NotEmpty();
const notEmpty = new privateUnderTest.NotEmpty();
const filledSet = new Set();
filledSet.add(1);
const emptySet = new Set();
@@ -42,7 +42,7 @@ describe('NotEmpty', function() {
});
it('matches a non empty typed array', function() {
const notEmpty = new jasmineUnderTest.NotEmpty();
const notEmpty = new privateUnderTest.NotEmpty();
expect(notEmpty.asymmetricMatch(new Int16Array([1, 2, 3]))).toBe(true);
expect(notEmpty.asymmetricMatch(new Int16Array())).toBe(false);

View File

@@ -1,13 +1,13 @@
describe('ObjectContaining', function() {
it('matches any object actual to an empty object', function() {
const containing = new jasmineUnderTest.ObjectContaining({});
const matchersUtil = new jasmineUnderTest.MatchersUtil();
const containing = new privateUnderTest.ObjectContaining({});
const matchersUtil = new privateUnderTest.MatchersUtil();
expect(containing.asymmetricMatch({ foo: 1 }, matchersUtil)).toBe(true);
});
it('does not match when the actual is not an object', function() {
const containing = new jasmineUnderTest.ObjectContaining({});
const containing = new privateUnderTest.ObjectContaining({});
[1, true, undefined, 'a string'].forEach(function(actual) {
expect(containing.asymmetricMatch(actual)).toBe(false);
@@ -15,7 +15,7 @@ describe('ObjectContaining', function() {
});
it('does not match an empty object actual', function() {
const containing = new jasmineUnderTest.ObjectContaining('foo');
const containing = new privateUnderTest.ObjectContaining('foo');
expect(function() {
containing.asymmetricMatch({});
@@ -23,8 +23,8 @@ describe('ObjectContaining', function() {
});
it('matches when the key/value pair is present in the actual', function() {
const containing = new jasmineUnderTest.ObjectContaining({ foo: 'fooVal' });
const matchersUtil = new jasmineUnderTest.MatchersUtil();
const containing = new privateUnderTest.ObjectContaining({ foo: 'fooVal' });
const matchersUtil = new privateUnderTest.MatchersUtil();
expect(
containing.asymmetricMatch({ foo: 'fooVal', bar: 'barVal' }, matchersUtil)
@@ -32,8 +32,8 @@ describe('ObjectContaining', function() {
});
it('does not match when the key/value pair is not present in the actual', function() {
const containing = new jasmineUnderTest.ObjectContaining({ foo: 'fooVal' });
const matchersUtil = new jasmineUnderTest.MatchersUtil();
const containing = new privateUnderTest.ObjectContaining({ foo: 'fooVal' });
const matchersUtil = new privateUnderTest.MatchersUtil();
expect(
containing.asymmetricMatch(
@@ -44,8 +44,8 @@ describe('ObjectContaining', function() {
});
it('does not match when the key is present but the value is different in the actual', function() {
const containing = new jasmineUnderTest.ObjectContaining({ foo: 'other' });
const matchersUtil = new jasmineUnderTest.MatchersUtil();
const containing = new privateUnderTest.ObjectContaining({ foo: 'other' });
const matchersUtil = new privateUnderTest.MatchersUtil();
expect(
containing.asymmetricMatch({ foo: 'fooVal', bar: 'barVal' }, matchersUtil)
@@ -54,7 +54,7 @@ describe('ObjectContaining', function() {
it("jasmineToString's itself", function() {
const sample = {},
matcher = new jasmineUnderTest.ObjectContaining(sample),
matcher = new privateUnderTest.ObjectContaining(sample),
pp = jasmine.createSpy('pp').and.returnValue('sample');
expect(matcher.jasmineToString(pp)).toEqual(
@@ -64,10 +64,10 @@ describe('ObjectContaining', function() {
});
it('matches recursively', function() {
const containing = new jasmineUnderTest.ObjectContaining({
one: new jasmineUnderTest.ObjectContaining({ two: {} })
const containing = new privateUnderTest.ObjectContaining({
one: new privateUnderTest.ObjectContaining({ two: {} })
});
const matchersUtil = new jasmineUnderTest.MatchersUtil();
const matchersUtil = new privateUnderTest.MatchersUtil();
expect(containing.asymmetricMatch({ one: { two: {} } }, matchersUtil)).toBe(
true
@@ -75,10 +75,10 @@ describe('ObjectContaining', function() {
});
it('matches when key is present with undefined value', function() {
const containing = new jasmineUnderTest.ObjectContaining({
const containing = new privateUnderTest.ObjectContaining({
one: undefined
});
const matchersUtil = new jasmineUnderTest.MatchersUtil();
const matchersUtil = new privateUnderTest.MatchersUtil();
expect(containing.asymmetricMatch({ one: undefined }, matchersUtil)).toBe(
true
@@ -86,17 +86,17 @@ describe('ObjectContaining', function() {
});
it('does not match when key with undefined value is not present', function() {
const containing = new jasmineUnderTest.ObjectContaining({
const containing = new privateUnderTest.ObjectContaining({
one: undefined
});
const matchersUtil = new jasmineUnderTest.MatchersUtil();
const matchersUtil = new privateUnderTest.MatchersUtil();
expect(containing.asymmetricMatch({}, matchersUtil)).toBe(false);
});
it('matches defined properties', function() {
const containing = new jasmineUnderTest.ObjectContaining({ foo: 'fooVal' });
const matchersUtil = new jasmineUnderTest.MatchersUtil();
const containing = new privateUnderTest.ObjectContaining({ foo: 'fooVal' });
const matchersUtil = new privateUnderTest.MatchersUtil();
const definedPropertyObject = {};
Object.defineProperty(definedPropertyObject, 'foo', {
@@ -110,8 +110,8 @@ describe('ObjectContaining', function() {
});
it('matches prototype properties', function() {
const containing = new jasmineUnderTest.ObjectContaining({ foo: 'fooVal' });
const matchersUtil = new jasmineUnderTest.MatchersUtil();
const containing = new privateUnderTest.ObjectContaining({ foo: 'fooVal' });
const matchersUtil = new privateUnderTest.MatchersUtil();
const prototypeObject = { foo: 'fooVal' };
const obj = Object.create(prototypeObject);
@@ -131,8 +131,8 @@ describe('ObjectContaining', function() {
return true;
}
};
const containing = new jasmineUnderTest.ObjectContaining({ foo: 'fooVal' });
const matchersUtil = new jasmineUnderTest.MatchersUtil({
const containing = new privateUnderTest.ObjectContaining({ foo: 'fooVal' });
const matchersUtil = new privateUnderTest.MatchersUtil({
customTesters: [tester]
});
@@ -144,8 +144,8 @@ describe('ObjectContaining', function() {
describe('valuesForDiff_', function() {
describe('when other is not an object', function() {
it('sets self to jasmineToString()', function() {
const containing = new jasmineUnderTest.ObjectContaining({}),
pp = jasmineUnderTest.makePrettyPrinter(),
const containing = new privateUnderTest.ObjectContaining({}),
pp = privateUnderTest.makePrettyPrinter(),
result = containing.valuesForDiff_('a', pp);
expect(result).toEqual({
@@ -159,11 +159,11 @@ describe('ObjectContaining', function() {
it('includes keys that are present in both other and sample', function() {
const sample = { a: 1, b: 2 },
other = { a: 3, b: 4 },
containing = new jasmineUnderTest.ObjectContaining(sample),
containing = new privateUnderTest.ObjectContaining(sample),
result = containing.valuesForDiff_(other);
expect(result.self).not.toBeInstanceOf(
jasmineUnderTest.ObjectContaining
privateUnderTest.ObjectContaining
);
expect(result).toEqual({
self: sample,
@@ -174,11 +174,11 @@ describe('ObjectContaining', function() {
it('includes keys that are present only in sample', function() {
const sample = { a: 1, b: 2 },
other = { a: 3 },
containing = new jasmineUnderTest.ObjectContaining(sample),
containing = new privateUnderTest.ObjectContaining(sample),
result = containing.valuesForDiff_(other);
expect(result.self).not.toBeInstanceOf(
jasmineUnderTest.ObjectContaining
privateUnderTest.ObjectContaining
);
expect(containing.valuesForDiff_(other)).toEqual({
self: sample,
@@ -192,11 +192,11 @@ describe('ObjectContaining', function() {
it('omits keys that are present only in other', function() {
const sample = { a: 1, b: 2 },
other = { a: 3, b: 4, c: 5 },
containing = new jasmineUnderTest.ObjectContaining(sample),
containing = new privateUnderTest.ObjectContaining(sample),
result = containing.valuesForDiff_(other);
expect(result.self).not.toBeInstanceOf(
jasmineUnderTest.ObjectContaining
privateUnderTest.ObjectContaining
);
expect(result).toEqual({
self: sample,

View File

@@ -1,7 +1,7 @@
describe('SetContaining', function() {
it('matches any actual set to an empty set', function() {
const actualSet = new Set(['foo', 'bar']);
const containing = new jasmineUnderTest.SetContaining(new Set());
const containing = new privateUnderTest.SetContaining(new Set());
expect(containing.asymmetricMatch(actualSet)).toBe(true);
});
@@ -10,8 +10,8 @@ describe('SetContaining', function() {
const actualSet = new Set([{ foo: 'bar' }, 'baz', [1, 2, 3]]);
const containingSet = new Set([[1, 2, 3], { foo: 'bar' }]);
const containing = new jasmineUnderTest.SetContaining(containingSet);
const matchersUtil = new jasmineUnderTest.MatchersUtil();
const containing = new privateUnderTest.SetContaining(containingSet);
const matchersUtil = new privateUnderTest.MatchersUtil();
expect(containing.asymmetricMatch(actualSet, matchersUtil)).toBe(true);
});
@@ -20,8 +20,8 @@ describe('SetContaining', function() {
const actualSet = new Set([{ foo: 'bar' }, 'baz', [1, 2, 3]]);
const containingSet = new Set([[1, 2], { foo: 'bar' }]);
const containing = new jasmineUnderTest.SetContaining(containingSet);
const matchersUtil = new jasmineUnderTest.MatchersUtil();
const containing = new privateUnderTest.SetContaining(containingSet);
const matchersUtil = new privateUnderTest.MatchersUtil();
expect(containing.asymmetricMatch(actualSet, matchersUtil)).toBe(false);
});
@@ -33,8 +33,8 @@ describe('SetContaining', function() {
jasmineUnderTest.stringMatching(/^foo\d/),
jasmineUnderTest.arrayContaining([2, 3])
]);
const containing = new jasmineUnderTest.SetContaining(containingSet);
const matchersUtil = new jasmineUnderTest.MatchersUtil();
const containing = new privateUnderTest.SetContaining(containingSet);
const matchersUtil = new privateUnderTest.MatchersUtil();
expect(containing.asymmetricMatch(actualSet, matchersUtil)).toBe(true);
});
@@ -46,8 +46,8 @@ describe('SetContaining', function() {
jasmine.stringMatching(/^foo\d/),
jasmine.arrayContaining([2, 3])
]);
const containing = new jasmineUnderTest.SetContaining(containingSet);
const matchersUtil = new jasmineUnderTest.MatchersUtil();
const containing = new privateUnderTest.SetContaining(containingSet);
const matchersUtil = new privateUnderTest.MatchersUtil();
expect(containing.asymmetricMatch(actualSet, matchersUtil)).toBe(false);
});
@@ -56,11 +56,11 @@ describe('SetContaining', function() {
const actualSet = new Set(['foo', new Set([1, 'bar', 2]), 'other']);
const containingSet = new Set([
new jasmineUnderTest.SetContaining(new Set(['bar'])),
new privateUnderTest.SetContaining(new Set(['bar'])),
'foo'
]);
const containing = new jasmineUnderTest.SetContaining(containingSet);
const matchersUtil = new jasmineUnderTest.MatchersUtil();
const containing = new privateUnderTest.SetContaining(containingSet);
const matchersUtil = new privateUnderTest.MatchersUtil();
expect(containing.asymmetricMatch(actualSet, matchersUtil)).toBe(true);
});
@@ -73,8 +73,8 @@ describe('SetContaining', function() {
: a === b;
}
const actualSet = new Set(['foo', -1]);
const containing = new jasmineUnderTest.SetContaining(new Set([-2, 'foo']));
const matchersUtil = new jasmineUnderTest.MatchersUtil({
const containing = new privateUnderTest.SetContaining(new Set([-2, 'foo']));
const matchersUtil = new privateUnderTest.MatchersUtil({
customTesters: [tester]
});
@@ -84,19 +84,19 @@ describe('SetContaining', function() {
it('does not match when actual is not a set', function() {
const containingSet = new Set(['foo']);
expect(
new jasmineUnderTest.SetContaining(containingSet).asymmetricMatch('foo')
new privateUnderTest.SetContaining(containingSet).asymmetricMatch('foo')
).toBe(false);
expect(
new jasmineUnderTest.SetContaining(containingSet).asymmetricMatch(1)
new privateUnderTest.SetContaining(containingSet).asymmetricMatch(1)
).toBe(false);
expect(
new jasmineUnderTest.SetContaining(containingSet).asymmetricMatch(['foo'])
new privateUnderTest.SetContaining(containingSet).asymmetricMatch(['foo'])
).toBe(false);
});
it('throws an error when sample is not a set', function() {
expect(function() {
new jasmineUnderTest.SetContaining({ foo: 'bar' }).asymmetricMatch(
new privateUnderTest.SetContaining({ foo: 'bar' }).asymmetricMatch(
new Set()
);
}).toThrowError(/You must provide a set/);
@@ -104,7 +104,7 @@ describe('SetContaining', function() {
it('defines a `jasmineToString` method', function() {
const sample = new Set(),
containing = new jasmineUnderTest.SetContaining(sample),
containing = new privateUnderTest.SetContaining(sample),
pp = jasmine.createSpy('pp').and.returnValue('sample');
expect(containing.jasmineToString(pp)).toEqual(

View File

@@ -1,6 +1,6 @@
describe('StringContaining', function() {
it('searches for a provided substring when the expected is a String', function() {
const matcher = new jasmineUnderTest.StringContaining('foo');
const matcher = new privateUnderTest.StringContaining('foo');
expect(matcher.asymmetricMatch('barfoobaz')).toBe(true);
expect(matcher.asymmetricMatch('barbaz')).toBe(false);
@@ -8,17 +8,17 @@ describe('StringContaining', function() {
it('raises an Error when the expected is not a String', function() {
expect(function() {
new jasmineUnderTest.StringContaining(/foo/);
new privateUnderTest.StringContaining(/foo/);
}).toThrowError(/not a String/);
});
it('fails when the actual is not a String', function() {
const matcher = new jasmineUnderTest.StringContaining('x');
const matcher = new privateUnderTest.StringContaining('x');
expect(matcher.asymmetricMatch(['x'])).toBe(false);
});
it("jasmineToString's itself", function() {
const matching = new jasmineUnderTest.StringContaining('foo');
const matching = new privateUnderTest.StringContaining('foo');
expect(matching.jasmineToString()).toEqual(
'<jasmine.stringContaining("foo")>'

View File

@@ -1,13 +1,13 @@
describe('StringMatching', function() {
it('matches a string against a provided regexp', function() {
const matcher = new jasmineUnderTest.StringMatching(/foo/);
const matcher = new privateUnderTest.StringMatching(/foo/);
expect(matcher.asymmetricMatch('barfoobaz')).toBe(true);
expect(matcher.asymmetricMatch('barbaz')).toBe(false);
});
it('matches a string against provided string', function() {
const matcher = new jasmineUnderTest.StringMatching('foo');
const matcher = new privateUnderTest.StringMatching('foo');
expect(matcher.asymmetricMatch('barfoobaz')).toBe(true);
expect(matcher.asymmetricMatch('barbaz')).toBe(false);
@@ -15,12 +15,12 @@ describe('StringMatching', function() {
it('raises an Error when the expected is not a String or RegExp', function() {
expect(function() {
new jasmineUnderTest.StringMatching({});
new privateUnderTest.StringMatching({});
}).toThrowError(/not a String or a RegExp/);
});
it("jasmineToString's itself", function() {
const matching = new jasmineUnderTest.StringMatching(/^foo/);
const matching = new privateUnderTest.StringMatching(/^foo/);
expect(matching.jasmineToString()).toEqual(
'<jasmine.stringMatching(/^foo/)>'

View File

@@ -1,13 +1,13 @@
describe('Truthy', function() {
it('is true for a non empty string', function() {
const truthy = new jasmineUnderTest.Truthy();
const truthy = new privateUnderTest.Truthy();
expect(truthy.asymmetricMatch('foo')).toBe(true);
expect(truthy.asymmetricMatch('')).toBe(false);
});
it('is true for a number that is not 0', function() {
const truthy = new jasmineUnderTest.Truthy();
const truthy = new privateUnderTest.Truthy();
expect(truthy.asymmetricMatch(1)).toBe(true);
expect(truthy.asymmetricMatch(0)).toBe(false);
@@ -16,44 +16,44 @@ describe('Truthy', function() {
});
it('is true for a function', function() {
const truthy = new jasmineUnderTest.Truthy();
const truthy = new privateUnderTest.Truthy();
expect(truthy.asymmetricMatch(function() {})).toBe(true);
});
it('is true for an Object', function() {
const truthy = new jasmineUnderTest.Truthy();
const truthy = new privateUnderTest.Truthy();
expect(truthy.asymmetricMatch({})).toBe(true);
});
it('is true for a truthful Boolean', function() {
const truthy = new jasmineUnderTest.Truthy();
const truthy = new privateUnderTest.Truthy();
expect(truthy.asymmetricMatch(true)).toBe(true);
expect(truthy.asymmetricMatch(false)).toBe(false);
});
it('is true for an empty object', function() {
const truthy = new jasmineUnderTest.Truthy();
const truthy = new privateUnderTest.Truthy();
expect(truthy.asymmetricMatch({})).toBe(true);
});
it('is true for an empty array', function() {
const truthy = new jasmineUnderTest.Truthy();
const truthy = new privateUnderTest.Truthy();
expect(truthy.asymmetricMatch([])).toBe(true);
});
it('is true for a date', function() {
const truthy = new jasmineUnderTest.Truthy();
const truthy = new privateUnderTest.Truthy();
expect(truthy.asymmetricMatch(new Date())).toBe(true);
});
it('is true for a infiniti', function() {
const truthy = new jasmineUnderTest.Truthy();
const truthy = new privateUnderTest.Truthy();
expect(truthy.asymmetricMatch(Infinity)).toBe(true);
expect(truthy.asymmetricMatch(-Infinity)).toBe(true);

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