Compare commits

...

130 Commits

Author SHA1 Message Date
Gregg Van Hove
c82e5947f5 Version bump to 2.9.0 2018-01-16 17:29:34 -08:00
Gregg Van Hove
f76b452816 Correctly check for Symbols, not Sets in AnythingSpec 2018-01-12 17:45:00 -08:00
Gregg Van Hove
91aab8e1b8 Merge branch 'Volox-ie11-sets-support'
- Merges #1478 from @Volox
- Fixes #1355
2018-01-12 17:33:51 -08:00
Volonterio Riccardo
486a64658f Added complete support for Set also for IE11.
Fixes 1355
2018-01-12 12:18:35 +01:00
Gregg Van Hove
1dfdc89ba9 Merge branch 'ie11-maps-support' of https://github.com/Volox/jasmine into Volox-ie11-maps-support
- Merges #1477 from @Volox
- Fixes #1472
2018-01-11 17:50:56 -08:00
Volonterio Riccardo
085a1f8a16 Added complete support for Map also for IE11.
Fixes 1472
2018-01-11 18:13:24 +01:00
Steve Gravrock
aeeb5b7107 Bumped copyright dates 2018-01-10 10:16:02 -08:00
Steve Gravrock
cb6de64e58 Fixed DelayedFunctionScheduler IE 8 compatibility issue 2018-01-02 18:17:23 -08:00
Steve Gravrock
f69949ebc0 Merge branch 'fix-1426' of https://github.com/leahciMic/jasmine
- Merges #1427 from @eahciMic
- Fixes #1426
2018-01-02 08:18:32 -08:00
Michael Leaney
1136fddcde Remove .sort() and fix logic in test 2018-01-02 14:51:51 +08:00
Steve Gravrock
b943f6b736 Fixed SPEC HAS NO EXPECTATIONS warning in HTML reporter
[Finishes #153891435]
2017-12-23 11:14:37 -08:00
Steve Gravrock
b439e2fb37 Extracted results state management out of the HTML reporter
[#153891435]
2017-12-23 11:12:38 -08:00
Steve Gravrock
c79ce125c0 Merge branch 'node-timeouts' of https://github.com/chris--young/jasmine
- Merges #1470 from @chris--young
- Fixes #1469
2017-12-21 13:24:21 -08:00
Chris Young
62b815c485 Use timeout objects when in node
Fixes: https://github.com/jasmine/jasmine/issues/1469
2017-12-21 10:51:07 -08:00
Steve Gravrock
65b4499dec In Jasmine's own tests, don't leak between jasmine and jasmineUnderTest 2017-12-20 12:08:22 -08:00
Steve Gravrock
b6cc34d9e9 Correctly remove spies of window.onerror on IE 2017-12-18 16:34:37 -08:00
Steve Gravrock
d3a3cf1ff3 Collapsed StringPrettyPrinter into PrettyPrinter
We never really took advantage of the potential extensibility that the
split provided, and the boundary between the two was getting muddied
over time.
2017-12-18 09:43:25 -08:00
Steve Gravrock
c0d0513199 Stop serializing objects after hitting jasmine.MAX_PRETTY_PRINT_CHARS 2017-12-15 13:23:59 -08:00
Steve Gravrock
86a38fceb9 Truncate pretty printer output that is more than j$.MAX_PRETTY_PRINT_CHARS long 2017-12-13 08:09:22 -08:00
Steve Gravrock
c74af1d900 Reduced pretty printer limits to much smaller values
This should help prevent out of memory errors when printing large object
graphs. See #1291 and #1422.

[#153257532]
2017-12-13 08:07:43 -08:00
Gregg Van Hove
8ea4c6d3a1 Update contributing for new naming of jasmineUnderTest 2017-12-11 17:25:27 -08:00
Steve Gravrock
0b5dce7d84 Removed CodeClimate badge & configuration
CodeClimate's recommendations have been a mix of irrelevant and
actively harmful for some time. The latest problem is that it applies
length and complexity requirements equally to all functions whether
they act as privacy scopes, namespaces, classes or just regular old
functions.

Static analysis tools are only worthwhile if their recommendations add
enough value to offset the cost of slogging through them -- in other
words, if the signal to noise ratio is high enough. CodeClimate hasn't
carried its own weight for a long time, and it's only gotten worse with
each recent update.
2017-12-07 21:29:59 -08:00
Steve Gravrock
0183b1642d Removed stray debugger statement 2017-11-30 07:53:59 -08:00
Steve Gravrock
18ed34e9be Fixed failing specs 2017-11-29 09:32:12 -08:00
Steve Gravrock
f4caf27208 Fixed pending() for async/promise-returning specs
Fixes #1449.
Fixes #1450.
2017-11-29 08:57:29 -08:00
Steve Gravrock
13b9e669bd Merge branch 'feature/allow-callfake-to-be-async' of https://github.com/mrlannigan/jasmine into merge-test
- Merges #1448 from @mrlannigan
2017-11-10 10:01:45 -08:00
Julian Lannigan
2eef0747a0 Added test steps for other major node versions
addresses https://www.pivotaltracker.com/story/show/152685778
2017-11-10 10:23:02 -05:00
Julian Lannigan
7ac1244f58 changed tests to work in environments that dont support async/await 2017-11-10 10:17:48 -05:00
Steve Gravrock
6ef0e93880 Revert "Removed IE 8 and 9 from the testing matrix"
In case we end doing another 2.x release.

This reverts commit 0e95b04900.
2017-11-09 20:57:01 -08:00
Julian Lannigan
2be5e0a962 Allowed async functions to be passed into spy#callFake 2017-11-09 19:59:23 -05:00
Steve Gravrock
285f06d5c4 Merge branch 'promise-matching-1314' of https://github.com/sderickson/jasmine 2017-11-02 17:18:24 -07:00
Steve Gravrock
3aece2ac74 Merge branch 'master' of https://github.com/b-3-n/jasmine
- Merges #1445 from @b-3-n
- Fixes #1444
2017-11-02 08:17:27 -07:00
sgravrock
3a9d4ee08d Merge pull request #1440 from aaronang/patch-1
Add instruction to sync local master with upstream
2017-11-02 08:11:47 -07:00
Benjamin Mularczyk
b4dfdd7a48 Fix equality computation for ES6 Sets. 2017-11-01 01:01:26 +01:00
Aaron Ang
c260c67e7e Add instruction to sync local master with upstream 2017-10-26 04:56:44 -07:00
sgravrock
554d0efab7 Point CodeClimate at the right repo 2017-10-25 11:28:47 -07:00
Steve Gravrock
ea3cf14ef8 Exclude specs from CodeClimate ratings
CodeClimate makes recommendations that, while helpful for production
code, can be harmful for test code. In particular, following its
recommendations would lead us to aggressively de-duplicate even small
amounts of test setup code. That can create unwanted coupling, obscure
the intent of the tests, and make it harder to maintain tests as their
setup needs diverge.
2017-10-25 09:25:52 -07:00
Steve Gravrock
a4bdd47a8a Merge branch 'unittests' of https://github.com/voithos/jasmine
- Merges #1437 from @voithos
2017-10-25 08:49:58 -07:00
Zaven Muradyan
89306551a0 Add some unit tests that exercise jasmine.anything() and Map matching.
Follow-up to some recent commits that fixed issues around
jasmine.anything() matching. This simply adds some extra tests that
exercise usage of Symbols with jasmine.anything() and as Map keys.
2017-10-24 22:39:31 -07:00
Steve Gravrock
0e95b04900 Removed IE 8 and 9 from the testing matrix
[#150527985]
2017-10-24 08:39:00 -07:00
Steve Gravrock
da91294182 Merge branch 'anything-fix' of https://github.com/voithos/jasmine
- Merges #1436 from @voithos
2017-10-24 08:26:13 -07:00
Steve Gravrock
bd41a97cd5 Merge branch 'anysymbol-fix' of https://github.com/voithos/jasmine
- Merge #1435 from @voithos
2017-10-24 08:18:42 -07:00
Zaven Muradyan
6d3953356d Add special handling of asymmetric matcher objects as keys in Maps.
The previous Map equality code was assuming that the set of keys would
be identical between the two Maps. This change adds insertion-order
tracking for each key with its corresponding key. If one of the two keys
is an asymmetric equality obj, the keys are eq()'d, and if it succeeds,
the corresponding values are compared. Otherwise, the "main" key is
looked up directly in the other object; this is to prevent
similar-looking obj keys with different obj identities from comparing
equal.

Fixes #1432.
2017-10-23 23:01:50 -07:00
Zaven Muradyan
62f769767a Add support for jasmine.any(Symbol).
The original asymmetric matcher code for Any did not work with symbols
since `symbolInstance instanceof Symbol` is actually `false` (but,
`symbolInstance.constructor` is `Symbol). This simply adds an extra
clause that explicitly checks for symbol (if available) like the other
primitive types.

Also added some missing specs for other types, like Map, Set, etc.

Fixes #1431.
2017-10-23 20:07:56 -07:00
Steve Gravrock
03f7f76bca Removed Safari 6 from the test matrix
Sauce Labs no longer supports Safari <7.
2017-10-23 12:05:52 -07:00
Steve Gravrock
4c6dadb9b1 Merge branch 'github-1295' of https://github.com/ksvitkovsky/jasmine
- Merges #1411 from @ksvitkovsky
2017-10-23 11:58:30 -07:00
Gregg Van Hove
8973e4e44f Ignore package-lock.json
We want to make sure that Travis checks against the latest version of
our dependencies, and also maintain compatibility across multiple
versions of Node.

Signed-off-by: Steve Gravrock <sgravrock@pivotal.io>
2017-10-23 11:53:48 -07:00
Steve Gravrock
b7e9594c86 Merge branch 'jasmine-deep-clone-args' of https://github.com/aj-dev/jasmine
- Merges #1424 from @aj-dev
2017-10-23 11:32:58 -07:00
Steve Gravrock
6c363f11b3 Merge branch 'lintfix' of https://github.com/voithos/jasmine
- Merges #1428 from @voithos
2017-10-23 11:01:18 -07:00
sgravrock
0f650e1d03 Merge pull request #1429 from voithos/docfix
Fix "Before Committing" section of CONTRIBUTING.md.
2017-10-23 10:46:06 -07:00
Zaven Muradyan
78fe42ac2d Fix "Before Committing" section of CONTRIBUTING.md.
The old contributing docs had a misaligned nested unordered-list, which
resulted in it creating a new list instead of being nested under its
parent.

It also seems like it would be useful to have the "revert your changes"
prose as part of the "Before Committing" section, since otherwise people
may miss it and have to amend.
2017-10-15 22:48:57 -07:00
Zaven Muradyan
20d86b2a00 Fix lint warning in CallTracker.
The previous code was using `== null` to handle both `null` and
`undefined`, resulting in a jshint warning. The conditional is meant to
check for non-primitive values, and it happens to be the case that a
falsey value in JS is always a primitive, or `null` or `undefined`.
2017-10-15 12:31:54 -07:00
Michael Leaney
516e00d7ba Fix #1426 clearTimeout not correctly clearing a timeout
clearTimeout was not correctly handling the case of clearing a
timeout that is also scheduled to run at the same tick.

This fix adds a deletedKeys array that is checked whilst we are
running the scheduled functions for the current clock tick. If
a function exists in deletedKeys it will not be ran. deletedKeys
is then reset to an empty array.
2017-10-11 20:54:56 +08:00
Michael Leaney
c1957ecd7c Add test 2017-10-11 20:29:27 +08:00
Audrius Jakumavicius
1f6c91de7c Deep clone args before passing them to reporters 2017-10-07 01:32:39 +02:00
Gregg Van Hove
f6342ad4fe Add a note about defineProperty for spyOnProperty
- Fixes #1415
2017-08-24 12:22:11 -07:00
Gregg Van Hove
8cf8736cab bump version to 2.8.0 2017-08-24 11:41:41 -07:00
Gregg Van Hove
6d0f0ed915 Only use the extra setTimeout outside of IE8. 2017-08-10 16:38:13 -07:00
ksvitkovsky
7ca571a746 Remove runnable parameter for simplicity 2017-08-10 23:08:59 +04:00
ksvitkovsky
4240b3514b Check runnable instead of suite 2017-08-10 22:58:30 +04:00
ksvitkovsky
ab116fbd0f Throw an error on nested methods 2017-08-10 22:05:13 +04:00
ksvitkovsky
da2673e02f Test nested methods to throw an error 2017-08-10 22:05:13 +04:00
Gregg Van Hove
b2dee5cb1c Create CODE_OF_CONDUCT.md 2017-08-10 10:38:05 -07:00
Gregg Van Hove
1414281cff Merge branch 'github-1221' of https://github.com/ksvitkovsky/jasmine into ksvitkovsky-github-1221
- Merges #1412 from @ksvitkovsky
- Fixes #1221
2017-08-09 17:20:54 -07:00
Gregg Van Hove
2588b442e6 Integration specs that actually execute a suite need to be async 2017-08-09 11:51:26 -07:00
Gregg Van Hove
af41e5ea5c Properly wait until test env has completed before moving on. 2017-08-09 11:43:55 -07:00
Gregg Van Hove
8834142198 Update jsApiReporter docs to link to new suite and spec results 2017-08-09 10:36:53 -07:00
Gregg Van Hove
1926fc11ae Add jsdocs for reporter events 2017-08-09 10:21:59 -07:00
ksvitkovsky
e7049d3183 Add 'nothing' matcher and tests 2017-08-09 16:44:19 +04:00
Gregg Van Hove
80dba1138a Add explicit docs for the callback function passed to it etc. 2017-08-08 17:28:36 -07:00
Gregg Van Hove
9811ff71d2 Update afterAll documentation
- See jasmine/jasmine.github.io#76
2017-08-08 14:37:45 -07:00
Gregg Van Hove
301dd4f6b5 Merge branch 'kiewic-kiewic/toEqualArrayDiff'
- Closes #1375 from @kiewic
2017-08-08 09:55:32 -07:00
Gilberto Stankiewicz
090a6234c3 Make toEqual matcher report the difference between array elements when arrays have different length 2017-08-08 09:55:17 -07:00
Gregg Van Hove
8ad9abb19a Add arrayWithExactContents asymmetric matcher
- Fixes #817
2017-08-04 12:07:09 -07:00
Gregg Van Hove
8c0a8a1b33 Merge branch 'ksvitkovsky-github-1388'
- Closes #1408 from @ksvitkovsky
- Fixes #1388
2017-08-04 10:27:55 -07:00
ksvitkovsky
1db81c334d Ensure no message added on asym. match success 2017-08-04 10:26:51 -07:00
Gregg Van Hove
8dd0ad531c Merge branch 'ksvitkovsky-github-1403'
- Closes #1407 from @ksvitkovsky
- Fixes #1403
2017-08-04 09:55:09 -07:00
ksvitkovsky
f58002f0a6 Better primitives detection for saveArgsByValue 2017-08-04 09:40:42 -07:00
Gregg Van Hove
4d18e63b0f Merge branch 'ksvitkovsky-github-1180'
- Closes #1404 from @ksvitkovsky
- Fixes #1180
2017-08-04 09:34:16 -07:00
ksvitkovsky
4fc177d5ae Better pretty printing for typed arrays 2017-08-04 09:33:05 -07:00
Gregg Van Hove
acc8c23bf4 Merge branch 'theefer-fix-map+set-comparison'
- Merges #1406 from @theefer
- Fixes #1402
2017-08-04 09:23:31 -07:00
Sébastien Cevey
fc6ee243e9 Rewrite ES6 Set and Map comparison to ignore insertion order 2017-08-02 15:22:33 +01:00
Gregg Van Hove
b256741bec bump version to 2.7.0 2017-07-27 17:47:20 -07:00
Gregg Van Hove
1d4a2b5c22 Merge branch 'user-context' of https://github.com/darthjee/jasmine into darthjee-user-context
- Merges #1400 from @darthjee
2017-07-26 17:18:02 -07:00
darthjee
b0aac6b852 Add class UserContext 2017-07-26 23:40:22 +02:00
Gregg Van Hove
543e056056 Merge branch 'jberney-master'
- Merges #1399 from @jberney
2017-07-24 17:13:21 -07:00
Jonathan
7dfc5f506c Send unfocused tests through the same queue as focused tests 2017-07-23 09:01:15 -07:00
Gregg Van Hove
4cce7263c4 Update github issue and PR templates 2017-07-22 09:34:58 -07:00
Gregg Van Hove
31164106e5 Get the casing right for IE8 2017-07-18 21:51:32 -07:00
Gregg Van Hove
f71218a44b Allow IE8 to be weird when pretty printing 2017-07-18 21:07:38 -07:00
Gregg Van Hove
22e9d6df20 PrettyPrinter allows an object to have a toString that isn't a function
- Fixes #1389
2017-07-17 22:01:06 -07:00
Scott Erickson
6ddf64568e Add Promise checking to eq
Fixes #1314
2017-06-26 19:10:19 -07:00
Alpha Chen
aa72b0b7cf Simplified rounding code in toBeCloseTo
Signed-off-by: Steve Gravrock <sgravrock@pivotal.io>
2017-06-26 10:22:54 -07:00
Alpha Chen
9f3952ff87 Merge branch 'vanduynslagerp-fix-tobecloseto'
- Fixes #1382

Signed-off-by: Alpha Chen <achen@pivotal.io>
2017-06-26 10:21:13 -07:00
Pierre Vanduynslager
c604012793 Fix rounding in toBeCloseTo 2017-06-25 16:59:24 -04:00
Gregg Van Hove
6aa069d586 IE 8&9 use setTimeout to clearStack so mock clock gets tricky 2017-06-20 10:29:19 -07:00
Steve Gravrock
585287b9d6 When stop on failure is enabled, skip subsequent it() and beforeEach().
Note: afterEach() functions are still run, because skipping them is
highly likely to pollute specs that run after the failure.

[Finishes #92252330]

- Fixes #577
- Fixes #807
2017-06-19 13:48:43 -07:00
Gregg Van Hove
de862f8133 Only clear out the spec param for Run all link
- Fixes #1369
2017-06-16 14:07:40 -07:00
Gregg Van Hove
4e3ae726aa Bump version to 2.6.4 2017-06-16 13:44:43 -07:00
Steve Gravrock
9ee7b4ee0b Don't setTimeout() every time the stack is cleared via MessageChannel() 2017-06-16 09:58:41 -07:00
Gregg Van Hove
2e737f50ca Don't use window 2017-06-15 14:32:18 -07:00
Gregg Van Hove
c60d669940 Break into a setTimeout every once in a while
- Allows the CPU to run other things that used the real `setTimeout`

- Fixes #1327
- See #1334
- Fixes jasmine/gulp-jasmine-browser#48
2017-06-15 14:24:40 -07:00
Gregg Van Hove
4c491b2dc0 Pretty printer will now use MAX_PRETTY_PRINT_ARRAY_LENGTH for objects
- Fixes #1291
- Fixes #1360
2017-06-14 13:36:35 -07:00
Gregg Van Hove
f9d1e2f280 Merge branch 'EsrefDurna-master'
- Merges #1368 from @EsrefDurna
2017-06-12 14:03:11 -07:00
Esref Durna
270475bc61 updated package glob from 7.0.5 to 7.1.2
Npm link :  https://www.npmjs.com/package/glob
 Repository link : https://github.com/isaacs/node-glob/releases
2017-06-12 00:35:07 +00:00
Gregg Van Hove
acb9989178 Merge branch 'patch-1' of https://github.com/kant/jasmine into kant-patch-1
- Fix bower.json url to be https instead of http
- Merges #1365 from @kant
2017-06-07 14:22:24 -07:00
Gregg Van Hove
1fe1bbb6ed Merge branch 'master' of https://github.com/beatrichartz/jasmine into beatrichartz-master
- Merges #1362 from @beatrichartz
2017-06-07 14:16:48 -07:00
Gregg Van Hove
994878e6f6 bump version to 2.6.3
- cherry-pick release notes, etc.
2017-06-07 14:15:21 -07:00
Steve Gravrock
f7c160d716 Fixed HTML snippet in README
Closes #1366.
2017-06-06 15:57:45 -07:00
Gregg Van Hove
578f63b9bd Make sure the queue runner goes async for async specs
- Even if `done` is called synchronously.

See #1327 #1334 jasmine/gulp-jasmine-browser#48
2017-06-06 14:29:02 -07:00
Darío Hereñú
b1190eefb9 Minor fix 2017-06-04 00:40:53 -03:00
Beat Richartz
271908a2c4 Fail when one of the arguments passed into toBeCloseTo matcher is null 2017-06-02 15:36:39 +01:00
Gregg Van Hove
3372af1cf1 Merge branch 'seed-reporting' of https://github.com/sgravrock/jasmine into sgravrock-seed-reporting
- Merges #1348 from @sgravrock
2017-05-23 14:12:49 -07:00
Gregg Van Hove
d355fa0806 Don't tick extra. It's breaking IE 8 & 9 2017-05-22 14:27:08 -07:00
Steve Gravrock
56a79bef98 Use a less ancient Node on Travis CI 2017-05-19 15:47:54 -07:00
Gregg Van Hove
2ea4e9507c Merge branch 'map-support' of https://github.com/rmehlinger/jasmine into rmehlinger-map-support
- Merges #1340 from @rmehlinger
- Fixes #1257
2017-05-19 14:19:14 -07:00
Gregg Van Hove
c848a66faa Merge branch 'promises' of https://github.com/sgravrock/jasmine into sgravrock-promises
- Merges #1356 from @sgravrock
- Fixes #1336
- Fixes #1270
- Fixes #1350
- Fixes #1320
2017-05-19 14:03:21 -07:00
Gregg Van Hove
99fd7ddb88 Update python release instructions
- so I don't have search my bash history
2017-05-17 17:35:23 -07:00
Gregg Van Hove
5d1d19f494 Bump version to 2.6.2 2017-05-17 17:24:16 -07:00
Steve Gravrock
4f49288f31 Built distribution
- Fixes #1327
- Fixes jasmine/gulp-jasmine-browser#48
2017-05-16 14:36:35 -07:00
Steve Gravrock
b9adc76dc7 Clear the stack if onmessage is called before the previous invocation finishes 2017-05-16 14:08:58 -07:00
Steve Gravrock
9672689d40 Added support for ES2017 async functions 2017-05-12 14:42:14 -07:00
Steve Gravrock
a237ac5386 Added support for async before/it/after functions that return promises 2017-05-11 14:31:09 -07:00
Gregg Van Hove
5ac3e21abb Merge branch 'interleaved-suites' of https://github.com/sgravrock/jasmine into sgravrock-interleaved-suites
- Merges #1352 from @sgravrock
- Fixes #1344
- Fixes #1349
2017-05-10 13:49:05 -07:00
Steve Gravrock
88763012e4 Unified async and async queueable function running code 2017-05-10 10:14:47 -07:00
Steve Gravrock
b1e97cfb09 Correctly route errors that occur while a QueueRunner is clearing stack
Besides surfacing the error in the hopefully-correct place, this also
prevents the queue runners for sibling suites from interleaving, which
in turn prevents all kinds of internal state corruption.

Signed-off-by: Gregg Van Hove <gvanhove@pivotal.io>
2017-05-09 15:01:18 -07:00
Gregg Van Hove
8e5823c0d2 Merge branch 'sgravrock-global-errors-rethrow'
- Merges #1347 from @sgravrock
2017-05-08 14:15:34 -07:00
Steve Gravrock
e6a7932ec1 Report the random seed at the beginning and end of execution
This allows reporters to provide the seed to the user even in cases
where Jasmine crashes before completing.
2017-05-08 12:09:17 -07:00
Steve Gravrock
10f1220e55 Don't mask errors that occur when no handlers are installed
It's possible for async code to cause an error when Jasmine
doesn't have any listeners registered internally. This causes
Jasmine to crash (Node) or log to the console (browser)
because of trying to call the nonexistent handler. This change
doesn't fix the overall problem but it does ensure that the
original error is logged rather than Jasmine's internal error.
2017-05-08 11:09:32 -07:00
rmehlinger
5ee03f02ed Add ES6 map support to Jasmine 2017-05-01 00:12:48 -07:00
84 changed files with 4012 additions and 657 deletions

View File

@@ -1,16 +0,0 @@
languages:
JavaScript: true
ratings:
paths:
- "src/**/*.js"
exclude_paths:
- "lib/**"
- "dist/*"
- "grunt/**"
- "images/*"
- "**/*.md"
- "**/*.yml"
- "**/*.json"
- "**/*.scss"
- "**/*.erb"
- "*.sh"

View File

@@ -16,7 +16,8 @@ Please submit pull requests via feature branches using the semi-standard workflo
git clone git@github.com:yourUserName/jasmine.git # Clone your fork
cd jasmine # Change directory
git remote add upstream https://github.com/jasmine/jasmine.git # Assign original repository to a remote named 'upstream'
git fetch upstream # Pull in changes not present in your local repository
git fetch upstream # Fetch changes not present in your local repository
git merge upstream/master # Sync local master with upstream repository
git checkout -b my-new-feature # Create your feature branch
git commit -am 'Add some feature' # Commit your changes
git push origin my-new-feature # Push to the branch
@@ -40,9 +41,9 @@ Once you've pushed a feature branch to your forked repo, you're ready to open a
### Self-testing
Note that Jasmine tests itself. The files in `lib` are loaded first, defining the reference `jasmine`. Then the files in `src` are loaded, defining the reference `j$`. So there are two copies of the code loaded under test.
Note that Jasmine tests itself. The files in `lib` are loaded first, defining the reference `jasmine`. Then the files in `src` are loaded, defining the reference `jasmineUnderTest`. So there are two copies of the code loaded under test.
The tests should always use `j$` to refer to the objects and functions that are being tested. But the tests can use functions on `jasmine` as needed. _Be careful how you structure any new test code_. Copy the patterns you see in the existing code - this ensures that the code you're testing is not leaking into the `jasmine` reference and vice-versa.
The tests should always use `jasmineUnderTest` to refer to the objects and functions that are being tested. But the tests can use functions on `jasmine` as needed. _Be careful how you structure any new test code_. Copy the patterns you see in the existing code - this ensures that the code you're testing is not leaking into the `jasmine` reference and vice-versa.
### `boot.js`
@@ -129,11 +130,9 @@ The easiest way to run the tests in **Internet Explorer** is to run a VM that ha
1. Ensure all specs are green in browser *and* node
1. Ensure JSHint is green with `grunt jshint`
1. Build `jasmine.js` with `grunt buildDistribution` and run all specs again - this ensures that your changes self-test well
## Submitting a Pull Request
1. Revert your changes to `jasmine.js` and `jasmine-html.js`
* We do this because `jasmine.js` and `jasmine-html.js` are auto-generated (as you've seen in the previous steps) and accepting multiple pull requests when this auto-generated file changes causes lots of headaches
1. When we accept your pull request, we will generate these files as a separate commit and merge the entire branch into master
* We do this because `jasmine.js` and `jasmine-html.js` are auto-generated (as you've seen in the previous steps) and accepting multiple pull requests when this auto-generated file changes causes lots of headaches
* When we accept your pull request, we will generate these files as a separate commit and merge the entire branch into master
Note that we use Travis for Continuous Integration. We only accept green pull requests.

View File

@@ -1,17 +1,44 @@
### Are you creating an issue in the correct repository?
## Are you creating an issue in the correct repository?
- When in doubt, create an issue here.
- If you have an issue with the Jasmine docs, file an issue in the docs repo
here: https://github.com/jasmine/jasmine.github.io
- This repository is for the core Jasmine framework
- If you are using a test runner that wraps Jasmine (Jasmine npm, karma, etc),
consider filing an issue with that library if appropriate
- If you are using a test runner that wraps Jasmine, consider filing an issue with that library if appropriate:
- [Jasmine npm](https://github.com/jasmine/jasmine-npm/issues)
- [Jasmine gem](https://github.com/jasmine/jasmine-gem/issues)
- [Jasmine py](https://github.com/jasmine/jasmine-py/issues)
- [Gulp Jasmine Browser](https://github.com/jasmine/gulp-jasmine-browser/issues)
- [Karma](https://github.com/karma-runner/karma/issues)
- [Grunt Contrib Jasmine](https://github.com/gruntjs/grunt-contrib-jasmine/issues)
### When submitting an issue, please answer the following:
<!--- Provide a general summary of the issue in the Title above -->
- What version are you using?
- What environment are you running Jasmine in (node, browser, etc)?
- How are you running Jasmine (standalone, npm, karma, etc)?
- If possible, include an example spec that demonstrates your issue.
## Expected Behavior
<!--- If you're describing a bug, tell us what should happen -->
<!--- If you're suggesting a change/improvement, tell us how it should work -->
Thanks for using Jasmine!
## Current Behavior
<!--- If describing a bug, tell us what happens instead of the expected behavior -->
<!--- If suggesting a change/improvement, explain the difference from current behavior -->
## Possible Solution
<!--- Not obligatory, but suggest a fix/reason for the bug, -->
<!--- or ideas how to implement the addition or change -->
## Suite that reproduces the behavior (for bugs)
<!--- Provide a sample suite that reproduces the bug. -->
```javascript
describe("sample", function() {
});
```
## Context
<!--- How has this issue affected you? What are you trying to accomplish? -->
<!--- Providing context helps us come up with a solution that is most useful in the real world -->
## Your Environment
<!--- Include as many relevant details about the environment you experienced the bug in -->
* Version used:
* Environment name and version (e.g. Chrome 39, node.js 5.4):
* Operating System and version (desktop or mobile):
* Link to your project:

30
.github/PULL_REQUEST_TEMPLATE.md vendored Normal file
View File

@@ -0,0 +1,30 @@
<!--- Provide a general summary of your changes in the Title above -->
## Description
<!--- Describe your changes in detail -->
## Motivation and Context
<!--- Why is this change required? What problem does it solve? -->
<!--- If it fixes an open issue, please link to the issue here. -->
## How Has This Been Tested?
<!--- Please describe in detail how you tested your changes. -->
<!--- Include details of your testing environment, and the tests you ran to -->
<!--- see how your change affects other areas of the code, etc. -->
## Types of changes
<!--- What types of changes does your code introduce? Put an `x` in all the boxes that apply: -->
- [ ] Bug fix (non-breaking change which fixes an issue)
- [ ] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing functionality to change)
## Checklist:
<!--- Go over all the following points, and put an `x` in all the boxes that apply. -->
<!--- If you're unsure about any of these, don't hesitate to ask. We're here to help! -->
- [ ] My code follows the code style of this project.
- [ ] My change requires a change to the documentation.
- [ ] I have updated the documentation accordingly.
- [ ] I have read the **CONTRIBUTING** document.
- [ ] I have added tests to cover my changes.
- [ ] All new and existing tests passed.

1
.gitignore vendored
View File

@@ -11,6 +11,7 @@ site/
.bundle
tags
Gemfile.lock
package-lock.json
pkg/*
.sass-cache/*
src/html/.sass-cache/*

View File

@@ -24,7 +24,16 @@ matrix:
include:
- env:
- USE_SAUCE=false
- TEST_COMMAND="bash travis-node-script.sh"
- TEST_COMMAND="bash travis-node-script.sh v0.12.18"
- env:
- USE_SAUCE=false
- TEST_COMMAND="bash travis-node-script.sh v4"
- env:
- USE_SAUCE=false
- TEST_COMMAND="bash travis-node-script.sh v8"
- env:
- USE_SAUCE=false
- TEST_COMMAND="bash travis-node-script.sh v9"
- env:
- JASMINE_BROWSER="safari"
- SAUCE_OS="OS X 10.11"
@@ -37,10 +46,6 @@ matrix:
- JASMINE_BROWSER="safari"
- SAUCE_OS="OS X 10.9"
- SAUCE_BROWSER_VERSION=7
- env:
- JASMINE_BROWSER="safari"
- SAUCE_OS="OS X 10.8"
- SAUCE_BROWSER_VERSION=6
- env:
- JASMINE_BROWSER="internet explorer"
- SAUCE_OS="Windows 8.1"

46
CODE_OF_CONDUCT.md Normal file
View File

@@ -0,0 +1,46 @@
# Contributor Covenant Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.
## Our Standards
Examples of behavior that contributes to creating a positive environment include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
## Scope
This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at jasmine-maintainers@googlegroups.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]
[homepage]: http://contributor-covenant.org
[version]: http://contributor-covenant.org/version/1/4/

View File

@@ -1,7 +1,6 @@
<a name="README">[<img src="https://rawgithub.com/jasmine/jasmine/master/images/jasmine-horizontal.svg" width="400px" />](http://jasmine.github.io)</a>
[![Build Status](https://travis-ci.org/jasmine/jasmine.svg?branch=master)](https://travis-ci.org/jasmine/jasmine)
[![Code Climate](https://codeclimate.com/github/pivotal/jasmine.svg)](https://codeclimate.com/github/pivotal/jasmine)
=======
@@ -43,12 +42,12 @@ To install Jasmine standalone on your local box (where **_{#.#.#}_** below is su
Add the following to your HTML file:
```html
<link rel="shortcut icon" type="image/png" href="jasmine/lib/jasmine-core/jasmine_favicon.png">
<link rel="stylesheet" type="text/css" href="jasmine/lib/jasmine-core/jasmine.css">
<link rel="shortcut icon" type="image/png" href="jasmine/lib/jasmine-{#.#.#}/jasmine_favicon.png">
<link rel="stylesheet" type="text/css" href="jasmine/lib/jasmine-{#.#.#}/jasmine.css">
<script type="text/javascript" src="jasmine/lib/jasmine-core/jasmine.js"></script>
<script type="text/javascript" src="jasmine/lib/jasmine-core/jasmine-html.js"></script>
<script type="text/javascript" src="jasmine/lib/jasmine-core/boot.js"></script>
<script type="text/javascript" src="jasmine/lib/jasmine-{#.#.#}/jasmine.js"></script>
<script type="text/javascript" src="jasmine/lib/jasmine-{#.#.#}/jasmine-html.js"></script>
<script type="text/javascript" src="jasmine/lib/jasmine-{#.#.#}/boot.js"></script>
```
## Supported environments

View File

@@ -7,9 +7,7 @@ Follow the instructions in `CONTRIBUTING.md` during development.
### Git Rules
Please work on feature branches.
Please attempt to keep commits to `master` small, but cohesive. If a feature is contained in a bunch of small commits (e.g., it has several wip commits or small work), please squash them when merging back to `master`.
Please attempt to keep commits to `master` small, but cohesive. If a feature is contained in a bunch of small commits (e.g., it has several wip commits or small work), please squash them when pushing to `master`.
### Version
@@ -40,7 +38,10 @@ When ready to release - specs are all green and the stories are done:
### Release the Python egg
1. `python setup.py register sdist upload` You will need pypi credentials to upload the egg.
Install [twine](https://github.com/pypa/twine)
1. `python setup.py sdist`
1. `twine upload dist/jasmine-core-<version>.tar.gz` You will need pypi credentials to upload the egg.
### Release the Ruby gem

View File

@@ -1,6 +1,6 @@
{
"name": "jasmine-core",
"homepage": "http://jasmine.github.io",
"homepage": "https://jasmine.github.io",
"authors": [
"slackersoft <gregg@slackersoft.net>"
],

View File

@@ -1,5 +1,5 @@
/*
Copyright (c) 2008-2017 Pivotal Labs
Copyright (c) 2008-2018 Pivotal Labs
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the

View File

@@ -1,5 +1,5 @@
/*
Copyright (c) 2008-2017 Pivotal Labs
Copyright (c) 2008-2018 Pivotal Labs
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the

View File

@@ -1,5 +1,5 @@
/*
Copyright (c) 2008-2017 Pivotal Labs
Copyright (c) 2008-2018 Pivotal Labs
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
@@ -34,6 +34,46 @@ jasmineRequire.HtmlReporter = function(j$) {
elapsed: function() { return 0; }
};
function ResultsStateBuilder() {
this.topResults = new j$.ResultsNode({}, '', null);
this.currentParent = this.topResults;
this.specsExecuted = 0;
this.failureCount = 0;
this.pendingSpecCount = 0;
}
ResultsStateBuilder.prototype.suiteStarted = function(result) {
this.currentParent.addChild(result, 'suite');
this.currentParent = this.currentParent.last();
};
ResultsStateBuilder.prototype.suiteDone = function(result) {
if (this.currentParent !== this.topResults) {
this.currentParent = this.currentParent.parent;
}
};
ResultsStateBuilder.prototype.specStarted = function(result) {
};
ResultsStateBuilder.prototype.specDone = function(result) {
this.currentParent.addChild(result, 'spec');
if (result.status !== 'disabled') {
this.specsExecuted++;
}
if (result.status === 'failed') {
this.failureCount++;
}
if (result.status == 'pending') {
this.pendingSpecCount++;
}
};
function HtmlReporter(options) {
var env = options.env || {},
getContainer = options.getContainer,
@@ -46,9 +86,6 @@ jasmineRequire.HtmlReporter = function(j$) {
filterSpecs = options.filterSpecs,
timer = options.timer || noopTimer,
results = [],
specsExecuted = 0,
failureCount = 0,
pendingSpecCount = 0,
htmlReporterMain,
symbols,
failedSuites = [];
@@ -77,12 +114,10 @@ jasmineRequire.HtmlReporter = function(j$) {
var summary = createDom('div', {className: 'jasmine-summary'});
var topResults = new j$.ResultsNode({}, '', null),
currentParent = topResults;
var stateBuilder = new ResultsStateBuilder();
this.suiteStarted = function(result) {
currentParent.addChild(result, 'suite');
currentParent = currentParent.last();
stateBuilder.suiteStarted(result);
};
this.suiteDone = function(result) {
@@ -90,27 +125,21 @@ jasmineRequire.HtmlReporter = function(j$) {
failedSuites.push(result);
}
if (currentParent == topResults) {
return;
}
currentParent = currentParent.parent;
stateBuilder.suiteDone(result);
};
this.specStarted = function(result) {
currentParent.addChild(result, 'spec');
stateBuilder.specStarted(result);
};
var failures = [];
this.specDone = function(result) {
stateBuilder.specDone(result);
if(noExpectations(result) && typeof console !== 'undefined' && typeof console.error !== 'undefined') {
console.error('Spec \'' + result.fullName + '\' has no expectations.');
}
if (result.status != 'disabled') {
specsExecuted++;
}
if (!symbols){
symbols = find('.jasmine-symbol-summary');
}
@@ -123,8 +152,6 @@ jasmineRequire.HtmlReporter = function(j$) {
));
if (result.status == 'failed') {
failureCount++;
var failure =
createDom('div', {className: 'jasmine-spec-detail jasmine-failed'},
createDom('div', {className: 'jasmine-description'},
@@ -142,10 +169,6 @@ jasmineRequire.HtmlReporter = function(j$) {
failures.push(failure);
}
if (result.status == 'pending') {
pendingSpecCount++;
}
};
this.jasmineDone = function(doneResult) {
@@ -208,9 +231,9 @@ jasmineRequire.HtmlReporter = function(j$) {
}
};
if (specsExecuted < totalSpecsDefined) {
var skippedMessage = 'Ran ' + specsExecuted + ' of ' + totalSpecsDefined + ' specs - run all';
var skippedLink = order && order.random ? '?random=true' : '?';
if (stateBuilder.specsExecuted < totalSpecsDefined) {
var skippedMessage = 'Ran ' + stateBuilder.specsExecuted + ' of ' + totalSpecsDefined + ' specs - run all';
var skippedLink = addToExistingQueryString('spec', '');
alert.appendChild(
createDom('span', {className: 'jasmine-bar jasmine-skipped'},
createDom('a', {href: skippedLink, title: 'Run all specs'}, skippedMessage)
@@ -221,9 +244,9 @@ jasmineRequire.HtmlReporter = function(j$) {
var statusBarClassName = 'jasmine-bar ';
if (totalSpecsDefined > 0) {
statusBarMessage += pluralize('spec', specsExecuted) + ', ' + pluralize('failure', failureCount);
if (pendingSpecCount) { statusBarMessage += ', ' + pluralize('pending spec', pendingSpecCount); }
statusBarClassName += (failureCount > 0) ? 'jasmine-failed' : 'jasmine-passed';
statusBarMessage += pluralize('spec', stateBuilder.specsExecuted) + ', ' + pluralize('failure', stateBuilder.failureCount);
if (stateBuilder.pendingSpecCount) { statusBarMessage += ', ' + pluralize('pending spec', stateBuilder.pendingSpecCount); }
statusBarClassName += (stateBuilder.failureCount > 0) ? 'jasmine-failed' : 'jasmine-passed';
} else {
statusBarClassName += 'jasmine-skipped';
statusBarMessage += 'No specs found';
@@ -258,7 +281,7 @@ jasmineRequire.HtmlReporter = function(j$) {
var results = find('.jasmine-results');
results.appendChild(summary);
summaryList(topResults, summary);
summaryList(stateBuilder.topResults, summary);
function summaryList(resultsTree, domParent) {
var specListNode;

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +1,5 @@
/*
Copyright (c) 2008-2017 Pivotal Labs
Copyright (c) 2008-2018 Pivotal Labs
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the

View File

@@ -4,6 +4,6 @@
#
module Jasmine
module Core
VERSION = "2.6.1"
VERSION = "2.9.0"
end
end

View File

@@ -1,7 +1,7 @@
{
"name": "jasmine-core",
"license": "MIT",
"version": "2.6.1",
"version": "2.9.0",
"repository": {
"type": "git",
"url": "https://github.com/jasmine/jasmine.git"
@@ -19,7 +19,7 @@
"homepage": "http://jasmine.github.io",
"main": "./lib/jasmine-core.js",
"devDependencies": {
"glob": "~7.0.5",
"glob": "~7.1.2",
"grunt": "^1.0.1",
"grunt-cli": "^1.2.0",
"grunt-contrib-compass": "^1.1.1",

23
release_notes/2.6.2.md Normal file
View File

@@ -0,0 +1,23 @@
# Jasmine 2.6.2 Release Notes
## Summary
This is a patch release to fix some regressions and performance problems in the 2.6.0 release
## Changes
* Clear the stack if onmessage is called before the previous invocation finishes
- Fixes #1327
- Fixes jasmine/gulp-jasmine-browser#48
* Correctly route errors that occur while a QueueRunner is clearing stack
- Merges #1352 from @sgravrock
- Fixes #1344
- Fixes #1349
* Don't mask errors that occur when no handlers are installed
- Merges #1347 from @sgravrock
------
_Release Notes generated with _[Anchorman](http://github.com/infews/anchorman)_

17
release_notes/2.6.3.md Normal file
View File

@@ -0,0 +1,17 @@
# Jasmine 2.6.3 Release Notes
## Summary
This is a patch release to fix some regressions and performance problems in the 2.6.0 release
## Changes
* Make sure the queue runner goes async for async specs
- Fixes [#1327](https://github.com/jasmine/jasmine/issues/1327)
- Fixes [#1334](https://github.com/jasmine/jasmine/issues/1334)
- Fixes [jasmine/gulp-jasmine-browser#48](https://github.com/jasmine/gulp-jasmine-browser/issues/48)
------
_Release Notes generated with _[Anchorman](http://github.com/infews/anchorman)_

17
release_notes/2.6.4.md Normal file
View File

@@ -0,0 +1,17 @@
# Jasmine 2.6.4 Release Notes
## Summary
This is a patch release to fix some regressions and performance problems in the 2.6.0 release
## Changes
* Break into a `setTimeout` every once in a while allowing the CPU to run other things that used the real `setTimeout`
- Fixes [#1327](https://github.com/jasmine/jasmine/issues/1327)
- See [#1334](https://github.com/jasmine/jasmine/issues/1334)
- Fixes [jasmine/gulp-jasmine-browser#48](https://github.com/jasmine/gulp-jasmine-browser/issues/48)
------
_Release Notes generated with _[Anchorman](http://github.com/infews/anchorman)_

61
release_notes/2.7.0.md Normal file
View File

@@ -0,0 +1,61 @@
# Jasmine 2.7.0 Release Notes
## Summary
This release contains a number of fixes and pull requests.
## Pull Requests & Issues
* Add class UserContext
- Merges [#1400](https://github.com/jasmine/jasmine/issues/1400) from @darthjee
* Send unfocused tests through the same queue as focused tests
- Merges [#1399](https://github.com/jasmine/jasmine/issues/1399) from @jberney
* PrettyPrinter allows an object to have a `toString` that isn't a function
- Fixes [#1389](https://github.com/jasmine/jasmine/issues/1389)
* Fix rounding in toBeCloseTo
- Fixes [#1382](https://github.com/jasmine/jasmine/issues/1382)
* When stop on failure is enabled, skip subsequent it() and beforeEach(). Note: afterEach() functions are still run, because skipping them is highly likely to pollute specs that run after the failure.
- Fixes [#577](https://github.com/jasmine/jasmine/issues/577)
- Fixes [#807](https://github.com/jasmine/jasmine/issues/807)
* Only clear out the `spec` param for Run all link
- Fixes [#1369](https://github.com/jasmine/jasmine/issues/1369)
* Pretty printer will now use MAX_PRETTY_PRINT_ARRAY_LENGTH for objects
- Fixes [#1291](https://github.com/jasmine/jasmine/issues/1291)
- Fixes [#1360](https://github.com/jasmine/jasmine/issues/1360)
* updated package glob from 7.0.5 to 7.1.2
- Merges [#1368](https://github.com/jasmine/jasmine/issues/1368) from @EsrefDurna
* Fix bower.json url to be https instead of http
- Merges [#1365](https://github.com/jasmine/jasmine/issues/1365) from @kant
* Fail when one of the arguments passed into toBeCloseTo matcher is null
- Merges [#1362](https://github.com/jasmine/jasmine/issues/1362) from @beatrichartz
* Fixed HTML snippet in README
- Closes [#1366](https://github.com/jasmine/jasmine/issues/1366).
* Report the random seed at the beginning and end of execution. This allows reporters to provide the seed to the user even in cases where Jasmine crashes before completing.
- Merges [#1348](https://github.com/jasmine/jasmine/issues/1348) from @sgravrock
* Add ES6 map support to Jasmine
- Merges [#1340](https://github.com/jasmine/jasmine/issues/1340) from @rmehlinger
- Fixes [#1257](https://github.com/jasmine/jasmine/issues/1257)
* Added support for async before/it/after functions that return promises and added support for ES2017 async functions
- Merges [#1356](https://github.com/jasmine/jasmine/issues/1356) from @sgravrock
- Fixes [#1336](https://github.com/jasmine/jasmine/issues/1336)
- Fixes [#1270](https://github.com/jasmine/jasmine/issues/1270)
- Fixes [#1350](https://github.com/jasmine/jasmine/issues/1350)
- Fixes [#1320](https://github.com/jasmine/jasmine/issues/1320)
------
_Release Notes generated with _[Anchorman](http://github.com/infews/anchorman)_

52
release_notes/2.8.0.md Normal file
View File

@@ -0,0 +1,52 @@
# Jasmine 2.8.0 Release Notes
## Summary
This release contains a number of fixes and pull requests.
2.8 should be the last 2.x release of Jasmine, as we aim for 3.0.
## Changes
* Create CODE_OF_CONDUCT.md
* Add jsdocs for reporter events
* Update jsApiReporter docs to link to new suite and spec results
* Add explicit docs for the callback function passed to `it` etc.
* Update afterAll documentation
## Pull Requests & Issues
* Add 'nothing' matcher and tests
- Merges [#1412](https://github.com/jasmine/jasmine/issues/1412) from @ksvitkovsky
- Fixes [#1221](https://github.com/jasmine/jasmine/issues/1221)
* Make toEqual matcher report the difference between array elements when arrays have different length
- Closes [#1375](https://github.com/jasmine/jasmine/issues/1375) from @kiewic
* Add arrayWithExactContents asymmetric matcher
- Fixes [#817](https://github.com/jasmine/jasmine/issues/817)
* Ensure no message added on asym. match success
- Closes [#1408](https://github.com/jasmine/jasmine/issues/1408) from @ksvitkovsky
- Fixes [#1388](https://github.com/jasmine/jasmine/issues/1388)
* Better primitives detection for saveArgsByValue
- Closes [#1407](https://github.com/jasmine/jasmine/issues/1407) from @ksvitkovsky
- Fixes [#1403](https://github.com/jasmine/jasmine/issues/1403)
* Better pretty printing for typed arrays
- Closes [#1404](https://github.com/jasmine/jasmine/issues/1404) from @ksvitkovsky
- Fixes [#1180](https://github.com/jasmine/jasmine/issues/1180)
* Rewrite ES6 Set and Map comparison to ignore insertion order
- Merges [#1406](https://github.com/jasmine/jasmine/issues/1406) from @theefer
- Fixes [#1402](https://github.com/jasmine/jasmine/issues/1402)
------
_Release Notes generated with _[Anchorman](http://github.com/infews/anchorman)_

99
release_notes/2.9.0.md Normal file
View File

@@ -0,0 +1,99 @@
# Jasmine 2.9 Release Notes
## Summary
This release contains a number of fixes and pull requests.
## Changes
* Fixed DelayedFunctionScheduler IE 8 compatibility issue
* Fixed SPEC HAS NO EXPECTATIONS warning in HTML reporter
* Correctly remove spies of window.onerror on IE
* Truncate pretty printer output that is more than j$.MAX_PRETTY_PRINT_CHARS long
* Reduced pretty printer limits to much smaller values
* Update contributing for new naming of `jasmineUnderTest`
* Allowed async functions to be passed into spy#callFake
## Pull Requests & Issues
* Added complete support for Set also for IE11.
- Merges [#1478](https://github.com/jasmine/jasmine/issues/1478) from @Volox
- Fixes [#1355](https://github.com/jasmine/jasmine/issues/1355)
* Added complete support for Map also for IE11.
- Merges [#1477](https://github.com/jasmine/jasmine/issues/1477) from @Volox
- Fixes [#1472](https://github.com/jasmine/jasmine/issues/1472)
* Use timeout objects when in node
- Merges [#1470](https://github.com/jasmine/jasmine/issues/1470) from @chris--young
- Fixes [#1469](https://github.com/jasmine/jasmine/issues/1469)
* Fixed `pending()` for `async`/promise-returning specs
- Fixes [#1449](https://github.com/jasmine/jasmine/issues/1449)
- Fixes [#1450](https://github.com/jasmine/jasmine/issues/1450)
* Added test steps for other major node versions
- Merges [#1448](https://github.com/jasmine/jasmine/issues/1448) from @mrlannigan
* Fix equality computation for ES6 Sets.
- Merges [#1445](https://github.com/jasmine/jasmine/issues/1445) from @b-3-n
- Fixes [#1444](https://github.com/jasmine/jasmine/issues/1444)
* Add instruction to sync local master with upstream
- Merges [#1440](https://github.com/jasmine/jasmine/issues/1440) from aaronang
* Add some unit tests that exercise jasmine.anything() and Map matching.
- Merges [#1437](https://github.com/jasmine/jasmine/issues/1437) from @voithos
* Add special handling of asymmetric matcher objects as keys in Maps.
- Merges [#1436](https://github.com/jasmine/jasmine/issues/1436) from @voithos
- Fixes [#1432](https://github.com/jasmine/jasmine/issues/1432)
* Add support for jasmine.any(Symbol).
- Merge [#1435](https://github.com/jasmine/jasmine/issues/1435) from @voithos
- Fixes [#1431](https://github.com/jasmine/jasmine/issues/1431)
* Throw an error for invalid nesting of a suite functions
- Merges [#1411](https://github.com/jasmine/jasmine/issues/1411) from @ksvitkovsky
- Fixes [#1295](https://github.com/jasmine/jasmine/issues/1295)
* Deep clone args before passing them to reporters
- Merges [#1424](https://github.com/jasmine/jasmine/issues/1424) from @aj-dev
* Fix "Before Committing" section of CONTRIBUTING.md.
- Merges [#1429](https://github.com/jasmine/jasmine/issues/1429) from @voithos
* Fix lint warning in CallTracker.
- Merges [#1428](https://github.com/jasmine/jasmine/issues/1428) from @voithos
* clearTimeout should now correctly clear a timeout that is also scheduled to run at the same tick.
- Merges [#1427](https://github.com/jasmine/jasmine/issues/1427) from @leahciMic
- Fixes [#1426](https://github.com/jasmine/jasmine/issues/1426)
* Add a note about `defineProperty` for `spyOnProperty`
- Fixes [#1415](https://github.com/jasmine/jasmine/issues/1415)
* Add Promise checking to eq
- Merges [#1386](https://github.com/jasmine/jasmine/issues/1386) from @sderickson
- Fixes [#1314](https://github.com/jasmine/jasmine/issues/1314)
------
_Release Notes generated with _[Anchorman](http://github.com/infews/anchorman)_

View File

@@ -117,4 +117,14 @@ describe("CallTracker", function() {
expect(callTracker.mostRecent().args[1]).not.toBe(arrayArg);
expect(callTracker.mostRecent().args[1]).toEqual(arrayArg);
});
it('saves primitive arguments by value', function() {
var callTracker = new jasmineUnderTest.CallTracker(),
args = [undefined, null, false, '', /\s/, 0, 1.2, NaN];
callTracker.saveArgumentsByValue();
callTracker.track({ object: {}, args: args });
expect(callTracker.mostRecent().args).toEqual(args);
});
});

View File

@@ -21,6 +21,34 @@ describe("ClearStack", function() {
expect(setImmediate).toHaveBeenCalled();
});
it("uses setTimeout instead of setImmediate every 10 calls to make sure we release the CPU", function() {
var setImmediate = jasmine.createSpy('setImmediate'),
setTimeout = jasmine.createSpy('setTimeout'),
global = { setImmediate: setImmediate, setTimeout: setTimeout },
clearStack = jasmineUnderTest.getClearStack(global);
clearStack(function() { });
clearStack(function() { });
clearStack(function() { });
clearStack(function() { });
clearStack(function() { });
clearStack(function() { });
clearStack(function() { });
clearStack(function() { });
clearStack(function() { });
expect(setImmediate).toHaveBeenCalled();
expect(setTimeout).not.toHaveBeenCalled();
clearStack(function() { });
expect(setImmediate.calls.count()).toEqual(9);
expect(setTimeout.calls.count()).toEqual(1);
clearStack(function() { });
expect(setImmediate.calls.count()).toEqual(10);
expect(setTimeout.calls.count()).toEqual(1);
});
it("uses MessageChannels when available", function() {
var fakeChannel = {
port1: {},
@@ -37,6 +65,62 @@ describe("ClearStack", function() {
expect(called).toBe(true);
});
it("uses setTimeout instead of MessageChannel every 10 calls to make sure we release the CPU", function() {
var fakeChannel = {
port1: {},
port2: {
postMessage: jasmine.createSpy('postMessage').and.callFake(function() {
fakeChannel.port1.onmessage();
})
}
},
setTimeout = jasmine.createSpy('setTimeout'),
global = { MessageChannel: function() { return fakeChannel; }, setTimeout: setTimeout },
clearStack = jasmineUnderTest.getClearStack(global);
clearStack(function() { });
clearStack(function() { });
clearStack(function() { });
clearStack(function() { });
clearStack(function() { });
clearStack(function() { });
clearStack(function() { });
clearStack(function() { });
clearStack(function() { });
expect(fakeChannel.port2.postMessage).toHaveBeenCalled();
expect(setTimeout).not.toHaveBeenCalled();
clearStack(function() { });
expect(fakeChannel.port2.postMessage.calls.count()).toEqual(9);
expect(setTimeout.calls.count()).toEqual(1);
clearStack(function() { });
expect(fakeChannel.port2.postMessage.calls.count()).toEqual(10);
expect(setTimeout.calls.count()).toEqual(1);
});
it("calls setTimeout when onmessage is called recursively", function() {
var fakeChannel = {
port1: {},
port2: { postMessage: function() { fakeChannel.port1.onmessage(); } }
},
setTimeout = jasmine.createSpy('setTimeout'),
global = {
MessageChannel: function() { return fakeChannel; },
setTimeout: setTimeout,
},
clearStack = jasmineUnderTest.getClearStack(global),
fn = jasmine.createSpy("second clearStack function");
clearStack(function() {
clearStack(fn);
});
expect(fn).not.toHaveBeenCalled();
expect(setTimeout).toHaveBeenCalledWith(fn, 0);
});
it("falls back to setTimeout", function() {
var setTimeout = jasmine.createSpy('setTimeout').and.callFake(function(fn) { fn() }),
global = { setTimeout: setTimeout },

View File

@@ -1,5 +1,7 @@
describe("Clock", function() {
var NODE_JS = typeof process !== 'undefined' && process.versions && typeof process.versions.node === 'string';
it("does not replace setTimeout until it is installed", function() {
var fakeSetTimeout = jasmine.createSpy("global setTimeout"),
fakeGlobal = { setTimeout: fakeSetTimeout },
@@ -294,13 +296,19 @@ describe("Clock", function() {
fakeGlobal = { setTimeout: fakeSetTimeout },
delayedFn = jasmine.createSpy('delayedFn'),
mockDate = { install: function() {}, tick: function() {}, uninstall: function() {} },
clock = new jasmineUnderTest.Clock(fakeGlobal, function () { return delayedFunctionScheduler; }, mockDate);
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();
expect(delayedFunctionScheduler.scheduleFunction).toHaveBeenCalledWith(delayedFn, 0, ['a', 'b']);
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() {
@@ -312,12 +320,16 @@ describe("Clock", function() {
delayedFn = jasmine.createSpy('delayedFn'),
mockDate = { install: function() {}, tick: function() {}, uninstall: function() {} },
clock = new jasmineUnderTest.Clock(fakeGlobal, function () { return delayedFunctionScheduler; }, mockDate),
timeoutId;
timeout;
clock.install();
timeoutId = clock.setTimeout(delayedFn, 0);
timeout = clock.setTimeout(delayedFn, 0);
expect(timeoutId).toEqual(123);
if (!NODE_JS) {
expect(timeout).toEqual(123);
} else {
expect(timeout.constructor.name).toEqual('FakeTimeout');
}
});
it("clears the scheduled function with the scheduler", function() {
@@ -342,13 +354,19 @@ describe("Clock", function() {
fakeGlobal = { setInterval: fakeSetInterval },
delayedFn = jasmine.createSpy('delayedFn'),
mockDate = { install: function() {}, tick: function() {}, uninstall: function() {} },
clock = new jasmineUnderTest.Clock(fakeGlobal, function () { return delayedFunctionScheduler; }, mockDate);
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();
expect(delayedFunctionScheduler.scheduleFunction).toHaveBeenCalledWith(delayedFn, 0, ['a', 'b'], true);
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() {
@@ -360,12 +378,16 @@ describe("Clock", function() {
delayedFn = jasmine.createSpy('delayedFn'),
mockDate = { install: function() {}, tick: function() {}, uninstall: function() {} },
clock = new jasmineUnderTest.Clock(fakeGlobal, function () { return delayedFunctionScheduler; }, mockDate),
intervalId;
interval;
clock.install();
intervalId = clock.setInterval(delayedFn, 0);
interval = clock.setInterval(delayedFn, 0);
expect(intervalId).toEqual(123);
if (!NODE_JS) {
expect(interval).toEqual(123);
} else {
expect(interval.constructor.name).toEqual('FakeTimeout');
}
});
it("clears the scheduled function with the scheduler", function() {
@@ -401,7 +423,8 @@ describe("Clock", function() {
setInterval: fakeSetInterval
},
mockDate = { install: function() {}, tick: function() {}, uninstall: function() {} },
clock = new jasmineUnderTest.Clock(fakeGlobal, function () { return delayedFunctionScheduler; }, mockDate);
clock = new jasmineUnderTest.Clock(fakeGlobal, function () { return delayedFunctionScheduler; }, mockDate),
timeout = new clock.FakeTimeout();
fakeSetTimeout.apply = null;
fakeSetInterval.apply = null;
@@ -409,13 +432,25 @@ describe("Clock", function() {
clock.install();
clock.setTimeout(fn, 0);
expect(delayedFunctionScheduler.scheduleFunction).toHaveBeenCalledWith(fn, 0, []);
if (!NODE_JS) {
expect(delayedFunctionScheduler.scheduleFunction).toHaveBeenCalledWith(fn, 0, []);
} else {
expect(delayedFunctionScheduler.scheduleFunction).toHaveBeenCalledWith(fn, 0, [], false, timeout);
}
expect(function() {
clock.setTimeout(fn, 0, 'extra');
}).toThrow();
clock.setInterval(fn, 0);
expect(delayedFunctionScheduler.scheduleFunction).toHaveBeenCalledWith(fn, 0, [], true);
if (!NODE_JS) {
expect(delayedFunctionScheduler.scheduleFunction).toHaveBeenCalledWith(fn, 0, [], true);
} else {
expect(delayedFunctionScheduler.scheduleFunction).toHaveBeenCalledWith(fn, 0, [], true, timeout);
}
expect(function() {
clock.setInterval(fn, 0, 'extra');
}).toThrow();
@@ -679,4 +714,41 @@ describe("Clock (acceptance)", function() {
expect(actualTimes).toEqual([baseTime.getTime(), baseTime.getTime() + 1, baseTime.getTime() + 3]);
})
it('correctly clears a scheduled timeout while the Clock is advancing', function () {
var delayedFunctionScheduler = new jasmineUnderTest.DelayedFunctionScheduler(),
global = {Date: Date, setTimeout: undefined},
mockDate = new jasmineUnderTest.MockDate(global),
clock = new jasmineUnderTest.Clock(global, function () { return delayedFunctionScheduler; }, mockDate);
clock.install();
var timerId2;
global.setTimeout(function () {
global.clearTimeout(timerId2);
}, 100);
timerId2 = global.setTimeout(fail, 100);
clock.tick(100);
});
it('correctly clears a scheduled interval while the Clock is advancing', function () {
var delayedFunctionScheduler = new jasmineUnderTest.DelayedFunctionScheduler(),
global = {Date: Date, setTimeout: undefined},
mockDate = new jasmineUnderTest.MockDate(global),
clock = new jasmineUnderTest.Clock(global, function () { return delayedFunctionScheduler; }, mockDate);
clock.install();
var timerId2;
var timerId1 = global.setInterval(function () {
global.clearInterval(timerId2);
}, 100);
timerId2 = global.setInterval(fail, 100);
clock.tick(400);
});
});

View File

@@ -216,21 +216,23 @@ describe("DelayedFunctionScheduler", function() {
it("removes functions during a tick that runs the function", function() {
var scheduler = new jasmineUnderTest.DelayedFunctionScheduler(),
fn = jasmine.createSpy('fn'),
spy = jasmine.createSpy('fn'),
spyAndRemove = jasmine.createSpy('fn'),
fnDelay = 10,
timeoutKey;
timeoutKey = scheduler.scheduleFunction(fn, fnDelay, [], true);
scheduler.scheduleFunction(function () {
spyAndRemove.and.callFake(function() {
scheduler.removeFunctionWithId(timeoutKey);
}, 2 * fnDelay);
});
expect(fn).not.toHaveBeenCalled();
scheduler.scheduleFunction(spyAndRemove, fnDelay);
scheduler.tick(3 * fnDelay);
timeoutKey = scheduler.scheduleFunction(spy, fnDelay, [], true);
expect(fn).toHaveBeenCalled();
expect(fn.calls.count()).toBe(2);
scheduler.tick(2 * fnDelay);
expect(spy).not.toHaveBeenCalled();
expect(spyAndRemove).toHaveBeenCalled();
});
it("removes functions during the first tick that runs the function", function() {

View File

@@ -91,6 +91,13 @@ describe("Env", function() {
env.it('pending spec');
}).not.toThrow();
});
it('accepts an async function', function() {
jasmine.getEnv().requireAsyncAwait();
expect(function() {
env.it('async', jasmine.getEnv().makeAsyncAwaitFunction());
}).not.toThrow();
});
});
describe('#xit', function() {
@@ -114,6 +121,13 @@ describe("Env", function() {
env.xit('pending spec');
}).not.toThrow();
});
it('accepts an async function', function() {
jasmine.getEnv().requireAsyncAwait();
expect(function() {
env.xit('async', jasmine.getEnv().makeAsyncAwaitFunction());
}).not.toThrow();
});
});
describe('#fit', function () {
@@ -130,6 +144,13 @@ describe("Env", function() {
env.beforeEach(undefined);
}).toThrowError(/beforeEach expects a function argument; received \[object (Undefined|DOMWindow|Object)\]/);
});
it('accepts an async function', function() {
jasmine.getEnv().requireAsyncAwait();
expect(function() {
env.beforeEach(jasmine.getEnv().makeAsyncAwaitFunction());
}).not.toThrow();
});
});
describe('#beforeAll', function () {
@@ -138,6 +159,13 @@ describe("Env", function() {
env.beforeAll(undefined);
}).toThrowError(/beforeAll expects a function argument; received \[object (Undefined|DOMWindow|Object)\]/);
});
it('accepts an async function', function() {
jasmine.getEnv().requireAsyncAwait();
expect(function() {
env.beforeAll(jasmine.getEnv().makeAsyncAwaitFunction());
}).not.toThrow();
});
});
describe('#afterEach', function () {
@@ -146,6 +174,13 @@ describe("Env", function() {
env.afterEach(undefined);
}).toThrowError(/afterEach expects a function argument; received \[object (Undefined|DOMWindow|Object)\]/);
});
it('accepts an async function', function() {
jasmine.getEnv().requireAsyncAwait();
expect(function() {
env.afterEach(jasmine.getEnv().makeAsyncAwaitFunction());
}).not.toThrow();
});
});
describe('#afterAll', function () {
@@ -154,5 +189,12 @@ describe("Env", function() {
env.afterAll(undefined);
}).toThrowError(/afterAll expects a function argument; received \[object (Undefined|DOMWindow|Object)\]/);
});
it('accepts an async function', function() {
jasmine.getEnv().requireAsyncAwait();
expect(function() {
env.afterAll(jasmine.getEnv().makeAsyncAwaitFunction());
}).not.toThrow();
});
});
});

View File

@@ -62,6 +62,22 @@ describe("GlobalErrors", function() {
expect(fakeGlobal.onerror).toBe(originalCallback);
});
it("rethrows the original error when there is no handler", function() {
var fakeGlobal = { },
errors = new jasmineUnderTest.GlobalErrors(fakeGlobal),
originalError = new Error('nope');
errors.install();
try {
fakeGlobal.onerror(originalError);
} catch (e) {
expect(e).toBe(originalError);
}
errors.uninstall();
});
it("works in node.js", function() {
var fakeGlobal = {
process: {

View File

@@ -17,7 +17,10 @@ describe("jasmineUnderTest.pp", function () {
describe('stringify sets', function() {
it("should stringify sets properly", function() {
jasmine.getEnv().requireFunctioningSets();
expect(jasmineUnderTest.pp(new Set([1, 2]))).toEqual("Set( 1, 2 )");
var set = new Set();
set.add(1);
set.add(2);
expect(jasmineUnderTest.pp(set)).toEqual("Set( 1, 2 )");
});
it("should truncate sets with more elments than jasmineUnderTest.MAX_PRETTY_PRINT_ARRAY_LENGTH", function() {
@@ -26,13 +29,43 @@ describe("jasmineUnderTest.pp", function () {
try {
jasmineUnderTest.MAX_PRETTY_PRINT_ARRAY_LENGTH = 2;
expect(jasmineUnderTest.pp(new Set(["a", "b", "c"]))).toEqual("Set( 'a', 'b', ... )");
var set = new Set();
set.add('a');
set.add('b');
set.add('c');
expect(jasmineUnderTest.pp(set)).toEqual("Set( 'a', 'b', ... )");
} finally {
jasmineUnderTest.MAX_PRETTY_PRINT_ARRAY_LENGTH = originalMaxSize;
}
})
});
describe('stringify maps', function() {
it("should stringify maps properly", function() {
jasmine.getEnv().requireFunctioningMaps();
var map = new Map();
map.set(1,2);
expect(jasmineUnderTest.pp(map)).toEqual("Map( [ 1, 2 ] )");
});
it("should truncate maps with more elments than jasmineUnderTest.MAX_PRETTY_PRINT_ARRAY_LENGTH", function() {
jasmine.getEnv().requireFunctioningMaps();
var originalMaxSize = jasmineUnderTest.MAX_PRETTY_PRINT_ARRAY_LENGTH;
try {
jasmineUnderTest.MAX_PRETTY_PRINT_ARRAY_LENGTH = 2;
var map = new Map();
map.set("a",1);
map.set("b",2);
map.set("c",3);
expect(jasmineUnderTest.pp(map)).toEqual("Map( [ 'a', 1 ], [ 'b', 2 ], ... )");
} finally {
jasmineUnderTest.MAX_PRETTY_PRINT_ARRAY_LENGTH = originalMaxSize;
}
})
});
describe('stringify arrays', function() {
it("should stringify arrays properly", function() {
expect(jasmineUnderTest.pp([1, 2])).toEqual("[ 1, 2 ]");
@@ -99,6 +132,53 @@ describe("jasmineUnderTest.pp", function () {
}, bar: [1, 2, 3]})).toEqual("Object({ foo: Function, bar: [ 1, 2, 3 ] })");
});
it("should truncate objects with too many keys", function () {
var originalMaxLength = jasmineUnderTest.MAX_PRETTY_PRINT_ARRAY_LENGTH;
var long = {a: 1, b: 2, c: 3};
try {
jasmineUnderTest.MAX_PRETTY_PRINT_ARRAY_LENGTH = 2;
expect(jasmineUnderTest.pp(long)).toEqual("Object({ a: 1, b: 2, ... })");
} finally {
jasmineUnderTest.MAX_PRETTY_PRINT_ARRAY_LENGTH = originalMaxLength;
}
});
function withMaxChars(maxChars, fn) {
var originalMaxChars = jasmineUnderTest.MAX_PRETTY_PRINT_CHARS;
try {
jasmineUnderTest.MAX_PRETTY_PRINT_CHARS = maxChars;
fn();
} finally {
jasmineUnderTest.MAX_PRETTY_PRINT_CHARS = originalMaxChars;
}
}
it("should truncate outputs that are too long", function() {
var big = [
{ a: 1, b: "a long string" },
{}
];
withMaxChars(34, function() {
expect(jasmineUnderTest.pp(big)).toEqual("[ Object({ a: 1, b: 'a long st ...");
});
});
it("should not serialize more objects after hitting MAX_PRETTY_PRINT_CHARS", function() {
var a = { jasmineToString: function() { return 'object a'; } },
b = { jasmineToString: function() { return 'object b'; } },
c = { jasmineToString: jasmine.createSpy('c jasmineToString').and.returnValue('') },
d = { jasmineToString: jasmine.createSpy('d jasmineToString').and.returnValue('') };
withMaxChars(30, function() {
jasmineUnderTest.pp([{a: a, b: b, c: c}, d]);
expect(c.jasmineToString).not.toHaveBeenCalled();
expect(d.jasmineToString).not.toHaveBeenCalled();
});
});
it("should print 'null' as the constructor of an object with its own constructor property", function() {
expect(jasmineUnderTest.pp({constructor: function() {}})).toContain("null({");
expect(jasmineUnderTest.pp({constructor: 'foo'})).toContain("null({");
@@ -184,9 +264,9 @@ describe("jasmineUnderTest.pp", function () {
it("should stringify spy objects properly", function() {
var TestObject = {
someFunction: function() {}
},
env = new jasmineUnderTest.Env();
someFunction: function() {}
},
env = new jasmineUnderTest.Env();
var spyRegistry = new jasmineUnderTest.SpyRegistry({currentSpies: function() {return [];}});
@@ -225,6 +305,18 @@ describe("jasmineUnderTest.pp", function () {
}
});
it("should stringify objects have have a toString that isn't a function", function() {
var obj = {
toString: "foo"
};
if (jasmine.getEnv().ieVersion < 9) {
expect(jasmineUnderTest.pp(obj)).toEqual("Object({ })");
} else {
expect(jasmineUnderTest.pp(obj)).toEqual("Object({ toString: 'foo' })");
}
});
it("should stringify objects from anonymous constructors with custom toString", function () {
var MyAnonymousConstructor = (function() { return function () {}; })();
MyAnonymousConstructor.toString = function () { return ''; };

View File

@@ -19,6 +19,26 @@ describe("QueueRunner", function() {
expect(calls).toEqual(['fn1', 'fn2']);
});
it("runs cleanup functions after the others", function() {
var calls = [],
queueableFn1 = { fn: jasmine.createSpy('fn1') },
queueableFn2 = { fn: jasmine.createSpy('fn2') },
queueRunner = new jasmineUnderTest.QueueRunner({
queueableFns: [queueableFn1],
cleanupFns: [queueableFn2]
});
queueableFn1.fn.and.callFake(function() {
calls.push('fn1');
});
queueableFn2.fn.and.callFake(function() {
calls.push('fn2');
});
queueRunner.execute();
expect(calls).toEqual(['fn1', 'fn2']);
});
it("calls each function with a consistent 'this'-- an empty object", function() {
var queueableFn1 = { fn: jasmine.createSpy('fn1') },
queueableFn2 = { fn: jasmine.createSpy('fn2') },
@@ -31,7 +51,7 @@ describe("QueueRunner", function() {
queueRunner.execute();
var context = queueableFn1.fn.calls.first().object;
expect(context).toEqual({});
expect(context).toEqual(new jasmineUnderTest.UserContext());
expect(queueableFn2.fn.calls.first().object).toBe(context);
expect(asyncContext).toBe(context);
});
@@ -189,6 +209,7 @@ describe("QueueRunner", function() {
queueRunner.execute();
jasmine.clock().tick(1);
expect(onComplete).toHaveBeenCalled();
jasmine.clock().tick(jasmineUnderTest.DEFAULT_TIMEOUT_INTERVAL);
@@ -197,12 +218,13 @@ describe("QueueRunner", function() {
it("only moves to the next spec the first time you call done", function() {
var queueableFn = { fn: function(done) {done(); done();} },
nextQueueableFn = { fn: jasmine.createSpy('nextFn') };
queueRunner = new jasmineUnderTest.QueueRunner({
queueableFns: [queueableFn, nextQueueableFn]
});
nextQueueableFn = { fn: jasmine.createSpy('nextFn') },
queueRunner = new jasmineUnderTest.QueueRunner({
queueableFns: [queueableFn, nextQueueableFn]
});
queueRunner.execute();
jasmine.clock().tick(1);
expect(nextQueueableFn.fn.calls.count()).toEqual(1);
});
@@ -211,10 +233,10 @@ describe("QueueRunner", function() {
setTimeout(done, 1);
throw new Error('error!');
} },
nextQueueableFn = { fn: jasmine.createSpy('nextFn') };
queueRunner = new jasmineUnderTest.QueueRunner({
queueableFns: [queueableFn, nextQueueableFn]
});
nextQueueableFn = { fn: jasmine.createSpy('nextFn') },
queueRunner = new jasmineUnderTest.QueueRunner({
queueableFns: [queueableFn, nextQueueableFn]
});
queueRunner.execute();
jasmine.clock().tick(1);
@@ -252,7 +274,7 @@ describe("QueueRunner", function() {
nextQueueableFn.fn.and.callFake(function() {
// should remove the same function that was added
expect(globalErrors.popListener).toHaveBeenCalledWith(globalErrors.pushListener.calls.argsFor(0)[0]);
expect(globalErrors.popListener).toHaveBeenCalledWith(globalErrors.pushListener.calls.argsFor(1)[0]);
});
queueRunner.execute();
@@ -271,6 +293,114 @@ describe("QueueRunner", function() {
expect(onException).toHaveBeenCalledWith(errorWithMessage(/^foo$/));
expect(nextQueueableFn.fn).toHaveBeenCalled();
});
it("handles exceptions thrown while waiting for the stack to clear", function() {
var queueableFn = { fn: function(done) { done() } },
global = {},
errorListeners = [],
globalErrors = {
pushListener: function(f) { errorListeners.push(f); },
popListener: function() { errorListeners.pop(); }
},
clearStack = jasmine.createSpy('clearStack'),
onException = jasmine.createSpy('onException'),
queueRunner = new jasmineUnderTest.QueueRunner({
queueableFns: [queueableFn],
globalErrors: globalErrors,
clearStack: clearStack,
onException: onException
}),
error = new Error('nope');
queueRunner.execute();
jasmine.clock().tick();
expect(clearStack).toHaveBeenCalled();
expect(errorListeners.length).toEqual(1);
errorListeners[0](error);
clearStack.calls.argsFor(0)[0]();
expect(onException).toHaveBeenCalledWith(error);
});
});
describe("with a function that returns a promise", function() {
function StubPromise() {}
StubPromise.prototype.then = function(resolve, reject) {
this.resolveHandler = resolve;
this.rejectHandler = reject;
};
beforeEach(function() {
jasmine.clock().install();
});
afterEach(function() {
jasmine.clock().uninstall();
});
it("runs the function asynchronously, advancing once the promise is settled", function() {
var onComplete = jasmine.createSpy('onComplete'),
fnCallback = jasmine.createSpy('fnCallback'),
p1 = new StubPromise(),
p2 = new StubPromise(),
queueableFn1 = { fn: function() {
setTimeout(function() {
p1.resolveHandler();
}, 100);
return p1;
} };
queueableFn2 = { fn: function() {
fnCallback();
setTimeout(function() {
p2.resolveHandler();
}, 100);
return p2;
} },
queueRunner = new jasmineUnderTest.QueueRunner({
queueableFns: [queueableFn1, queueableFn2],
onComplete: onComplete
});
queueRunner.execute();
expect(fnCallback).not.toHaveBeenCalled();
expect(onComplete).not.toHaveBeenCalled();
jasmine.clock().tick(100);
expect(fnCallback).toHaveBeenCalled();
expect(onComplete).not.toHaveBeenCalled();
jasmine.clock().tick(100);
expect(onComplete).toHaveBeenCalled();
});
it("handles a rejected promise like an unhandled exception", function() {
var promise = new StubPromise(),
queueableFn1 = { fn: function() {
setTimeout(function() {
promise.rejectHandler('foo')
}, 100);
return promise;
} },
queueableFn2 = { fn: jasmine.createSpy('fn2') },
failFn = jasmine.createSpy('fail'),
onExceptionCallback = jasmine.createSpy('on exception callback'),
queueRunner = new jasmineUnderTest.QueueRunner({
queueableFns: [queueableFn1, queueableFn2],
onException: onExceptionCallback
});
queueRunner.execute();
expect(onExceptionCallback).not.toHaveBeenCalled();
expect(queueableFn2.fn).not.toHaveBeenCalled();
jasmine.clock().tick(100);
expect(onExceptionCallback).toHaveBeenCalledWith('foo');
expect(queueableFn2.fn).toHaveBeenCalled();
});
});
it("calls exception handlers when an exception is thrown in a fn", function() {
@@ -314,6 +444,87 @@ describe("QueueRunner", function() {
expect(nextQueueableFn.fn).toHaveBeenCalled();
});
describe("When configured to complete on first error", function() {
it("skips to cleanup functions on the first exception", function() {
var queueableFn = { fn: function() { throw new Error("error"); } },
nextQueueableFn = { fn: jasmine.createSpy("nextFunction") },
cleanupFn = { fn: jasmine.createSpy("cleanup") },
queueRunner = new jasmineUnderTest.QueueRunner({
queueableFns: [queueableFn, nextQueueableFn],
cleanupFns: [cleanupFn],
completeOnFirstError: true
});
queueRunner.execute();
expect(nextQueueableFn.fn).not.toHaveBeenCalled();
expect(cleanupFn.fn).toHaveBeenCalled();
});
it("does not skip when a cleanup function throws", function() {
var queueableFn = { fn: function() { } },
cleanupFn1 = { fn: function() { throw new Error("error"); } },
cleanupFn2 = { fn: jasmine.createSpy("cleanupFn2") },
queueRunner = new jasmineUnderTest.QueueRunner({
queueableFns: [queueableFn],
cleanupFns: [cleanupFn1, cleanupFn2],
completeOnFirstError: true
});
queueRunner.execute();
expect(cleanupFn2.fn).toHaveBeenCalled();
});
describe("with an asynchronous function", function() {
beforeEach(function() {
jasmine.clock().install();
});
afterEach(function() {
jasmine.clock().uninstall();
});
it("skips to cleanup functions on the first exception", function() {
var errorListeners = [],
queueableFn = { fn: function(done) {} },
nextQueueableFn = { fn: jasmine.createSpy('nextFunction') },
cleanupFn = { fn: jasmine.createSpy('cleanup') },
queueRunner = new jasmineUnderTest.QueueRunner({
globalErrors: {
pushListener: function(f) { errorListeners.push(f); },
popListener: function() { errorListeners.pop(); },
},
queueableFns: [queueableFn, nextQueueableFn],
cleanupFns: [cleanupFn],
completeOnFirstError: true,
});
queueRunner.execute();
errorListeners[errorListeners.length - 1](new Error('error'));
expect(nextQueueableFn.fn).not.toHaveBeenCalled();
expect(cleanupFn.fn).toHaveBeenCalled();
});
it("skips to cleanup functions when next.fail is called", function() {
var queueableFn = { fn: function(done) {
done.fail('nope');
} },
nextQueueableFn = { fn: jasmine.createSpy('nextFunction') },
cleanupFn = { fn: jasmine.createSpy('cleanup') },
queueRunner = new jasmineUnderTest.QueueRunner({
queueableFns: [queueableFn, nextQueueableFn],
cleanupFns: [cleanupFn],
completeOnFirstError: true,
});
queueRunner.execute();
jasmine.clock().tick();
expect(nextQueueableFn.fn).not.toHaveBeenCalled();
expect(cleanupFn.fn).toHaveBeenCalled();
});
});
});
it("calls a provided complete callback when done", function() {
var queueableFn = { fn: jasmine.createSpy('fn') },
completeCallback = jasmine.createSpy('completeCallback'),
@@ -327,21 +538,83 @@ describe("QueueRunner", function() {
expect(completeCallback).toHaveBeenCalled();
});
it("calls a provided stack clearing function when done", function() {
var asyncFn = { fn: function(done) { done() } },
afterFn = { fn: jasmine.createSpy('afterFn') },
completeCallback = jasmine.createSpy('completeCallback'),
clearStack = jasmine.createSpy('clearStack'),
queueRunner = new jasmineUnderTest.QueueRunner({
queueableFns: [asyncFn, afterFn],
clearStack: clearStack,
onComplete: completeCallback
});
describe("clearing the stack", function() {
beforeEach(function() {
jasmine.clock().install();
});
clearStack.and.callFake(function(fn) { fn(); });
afterEach(function() {
jasmine.clock().uninstall();
});
queueRunner.execute();
expect(afterFn.fn).toHaveBeenCalled();
expect(clearStack).toHaveBeenCalledWith(completeCallback);
it("calls a provided stack clearing function when done", function() {
var asyncFn = { fn: function(done) { done() } },
afterFn = { fn: jasmine.createSpy('afterFn') },
completeCallback = jasmine.createSpy('completeCallback'),
clearStack = jasmine.createSpy('clearStack'),
queueRunner = new jasmineUnderTest.QueueRunner({
queueableFns: [asyncFn, afterFn],
clearStack: clearStack,
onComplete: completeCallback
});
clearStack.and.callFake(function(fn) { fn(); });
queueRunner.execute();
jasmine.clock().tick();
expect(afterFn.fn).toHaveBeenCalled();
expect(clearStack).toHaveBeenCalled();
clearStack.calls.argsFor(0)[0]();
expect(completeCallback).toHaveBeenCalled();
});
});
describe('when user context has not been defined', function() {
beforeEach(function() {
var fn;
this.fn = fn = jasmine.createSpy('fn1');
this.queueRunner = new jasmineUnderTest.QueueRunner({
queueableFns: [{ fn: fn }]
});
});
it('runs the functions on the scope of a UserContext', function() {
var calls = [],
context;
this.fn.and.callFake(function() {
context = this;
});
this.queueRunner.execute();
expect(context.constructor).toBe(jasmineUnderTest.UserContext);
});
});
describe('when user context has been defined', function() {
beforeEach(function() {
var fn, context;
this.fn = fn = jasmine.createSpy('fn1');
this.context = context = new jasmineUnderTest.UserContext();
this.queueRunner = new jasmineUnderTest.QueueRunner({
queueableFns: [{ fn: fn }],
userContext: context
});
});
it('runs the functions on the scope of a UserContext', function() {
var calls = [],
context;
this.fn.and.callFake(function() {
context = this;
});
this.queueRunner.execute();
expect(context).toBe(this.context);
});
});
});

View File

@@ -103,8 +103,26 @@ describe("Spec", function() {
spec.execute();
var allSpecFns = fakeQueueRunner.calls.mostRecent().args[0].queueableFns;
expect(allSpecFns).toEqual([before, queueableFn, after]);
var options = fakeQueueRunner.calls.mostRecent().args[0];
expect(options.queueableFns).toEqual([before, queueableFn]);
expect(options.cleanupFns).toEqual([after]);
});
it("tells the queue runner that it's a leaf node", function() {
var fakeQueueRunner = jasmine.createSpy('fakeQueueRunner'),
spec = new jasmineUnderTest.Spec({
queueableFn: { fn: function() {} },
beforeAndAfterFns: function() {
return {befores: [], afters: []}
},
queueRunnerFactory: fakeQueueRunner
});
spec.execute();
expect(fakeQueueRunner).toHaveBeenCalledWith(jasmine.objectContaining({
isLeaf: true
}));
});
it("is marked pending if created without a function body", function() {
@@ -123,7 +141,8 @@ describe("Spec", function() {
});
it("can be disabled, but still calls callbacks", function() {
var fakeQueueRunner = jasmine.createSpy('fakeQueueRunner'),
var fakeQueueRunner = jasmine.createSpy('fakeQueueRunner')
.and.callFake(function(attrs) { attrs.onComplete(); }),
startCallback = jasmine.createSpy('startCallback'),
specBody = jasmine.createSpy('specBody'),
resultCallback = jasmine.createSpy('resultCallback'),
@@ -140,7 +159,7 @@ describe("Spec", function() {
spec.execute();
expect(fakeQueueRunner).not.toHaveBeenCalled();
expect(fakeQueueRunner).toHaveBeenCalled();
expect(specBody).not.toHaveBeenCalled();
expect(startCallback).toHaveBeenCalled();
@@ -148,7 +167,8 @@ describe("Spec", function() {
});
it("can be disabled at execution time by a parent", function() {
var fakeQueueRunner = jasmine.createSpy('fakeQueueRunner'),
var fakeQueueRunner = jasmine.createSpy('fakeQueueRunner')
.and.callFake(function(attrs) { attrs.onComplete(); }),
startCallback = jasmine.createSpy('startCallback'),
specBody = jasmine.createSpy('specBody'),
resultCallback = jasmine.createSpy('resultCallback'),
@@ -163,7 +183,7 @@ describe("Spec", function() {
expect(spec.result.status).toBe('disabled');
expect(fakeQueueRunner).not.toHaveBeenCalled();
expect(fakeQueueRunner).toHaveBeenCalled();
expect(specBody).not.toHaveBeenCalled();
expect(startCallback).toHaveBeenCalled();
@@ -171,7 +191,8 @@ describe("Spec", function() {
});
it("can be marked pending, but still calls callbacks when executed", function() {
var fakeQueueRunner = jasmine.createSpy('fakeQueueRunner'),
var fakeQueueRunner = jasmine.createSpy('fakeQueueRunner')
.and.callFake(function(attrs) { attrs.onComplete(); }),
startCallback = jasmine.createSpy('startCallback'),
resultCallback = jasmine.createSpy('resultCallback'),
spec = new jasmineUnderTest.Spec({
@@ -191,7 +212,7 @@ describe("Spec", function() {
spec.execute();
expect(fakeQueueRunner).not.toHaveBeenCalled();
expect(fakeQueueRunner).toHaveBeenCalled();
expect(startCallback).toHaveBeenCalled();
expect(resultCallback).toHaveBeenCalledWith({

View File

@@ -287,6 +287,24 @@ describe("SpyRegistry", function() {
expect(jasmineUnderTest.isSpy(subject.spiedFunc)).toBe(false);
});
it("restores window.onerror by overwriting, not deleting", function() {
function FakeWindow() {
}
FakeWindow.prototype.onerror = function() {};
var spies = [],
global = new FakeWindow(),
spyRegistry = new jasmineUnderTest.SpyRegistry({
currentSpies: function() { return spies; },
global: global
});
spyRegistry.spyOn(global, 'onerror');
spyRegistry.clearSpies();
expect(global.onerror).toBe(FakeWindow.prototype.onerror);
expect(global.hasOwnProperty('onerror')).toBe(true);
});
});
describe('spying on properties', function() {

View File

@@ -92,12 +92,35 @@ describe("SpyStrategy", function() {
expect(returnValue).toEqual(67);
});
it("allows a fake async function to be called instead", function(done) {
jasmine.getEnv().requireAsyncAwait();
var originalFn = jasmine.createSpy("original"),
fakeFn = jasmine.createSpy("fake").and.callFake(eval("async () => { return 67; }")),
spyStrategy = new jasmineUnderTest.SpyStrategy({fn: originalFn}),
returnValue;
spyStrategy.callFake(fakeFn);
spyStrategy.exec().then(function (returnValue) {
expect(originalFn).not.toHaveBeenCalled();
expect(fakeFn).toHaveBeenCalled();
expect(returnValue).toEqual(67);
done();
}).catch(function (err) {
done.fail(err);
})
});
it('throws an error when a non-function is passed to callFake strategy', function() {
var originalFn = jasmine.createSpy('original'),
spyStrategy = new jasmineUnderTest.SpyStrategy({fn: originalFn}),
invalidFakes = [5, 'foo', {}, true, false, null, void 0, new Date(), /.*/];
spyOn(jasmineUnderTest, 'isFunction_').and.returnValue(false);
spyOn(jasmineUnderTest, 'isAsyncFunction_').and.returnValue(false);
expect(function () {
spyStrategy.callFake(function() {});
}).toThrowError(/^Argument passed to callFake should be a function, got/);
expect(function () {
spyStrategy.callFake(function() {});

View File

@@ -141,5 +141,15 @@ describe("Suite", function() {
suite.onException(new jasmineUnderTest.errors.ExpectationFailed());
expect(suite.getResult().failedExpectations).toEqual([]);
})
});
describe('#sharedUserContext', function() {
beforeEach(function() {
this.suite = new jasmineUnderTest.Suite({});
});
it('returns a UserContext', function() {
expect(this.suite.sharedUserContext().constructor).toBe(jasmineUnderTest.UserContext);
});
});
});

View File

@@ -0,0 +1,54 @@
describe("UserContext", function() {
it("Behaves just like an plain object", function() {
var context = new jasmineUnderTest.UserContext(),
properties = [];
for (var prop in context) {
if (obj.hasOwnProperty(prop)) {
properties.push(prop);
}
}
expect(properties).toEqual([]);
});
describe('.fromExisting', function() {
describe('when using an already built context as model', function() {
beforeEach(function() {
this.context = new jasmineUnderTest.UserContext();
this.context.key = 'value';
this.cloned = jasmineUnderTest.UserContext.fromExisting(this.context);
});
it('returns a cloned object', function() {
expect(this.cloned).toEqual(this.context);
});
it('does not return the same object', function() {
expect(this.cloned).not.toBe(this.context);
});
});
describe('when using a regular object as parameter', function() {
beforeEach(function() {
this.context = {};
this.value = 'value'
this.context.key = this.value;
this.cloned = jasmineUnderTest.UserContext.fromExisting(this.context);
});
it('returns an object with the same attributes', function() {
expect(this.cloned.key).toEqual(this.value);
});
it('does not return the same object', function() {
expect(this.cloned).not.toBe(this.context);
});
it('returns an UserContext', function() {
expect(this.cloned.constructor).toBe(jasmineUnderTest.UserContext);
});
});
});
});

View File

@@ -29,6 +29,38 @@ describe("Any", function() {
expect(any.asymmetricMatch(true)).toBe(true);
});
it("matches a Map", function() {
jasmine.getEnv().requireFunctioningMaps();
var any = new jasmineUnderTest.Any(Map);
expect(any.asymmetricMatch(new Map())).toBe(true);
});
it("matches a Set", function() {
jasmine.getEnv().requireFunctioningSets();
var any = new jasmineUnderTest.Any(Set);
expect(any.asymmetricMatch(new Set())).toBe(true);
});
it("matches a TypedArray", function() {
jasmine.getEnv().requireFunctioningTypedArrays();
var any = new jasmineUnderTest.Any(Uint32Array);
expect(any.asymmetricMatch(new Uint32Array([]))).toBe(true);
});
it("matches a Symbol", function() {
jasmine.getEnv().requireFunctioningSymbols();
var any = new jasmineUnderTest.Any(Symbol);
expect(any.asymmetricMatch(Symbol())).toBe(true);
});
it("matches another constructed object", function() {
var Thing = function() {},
any = new jasmineUnderTest.Any(Thing);

View File

@@ -23,6 +23,38 @@ describe("Anything", function() {
expect(anything.asymmetricMatch([1,2,3])).toBe(true);
});
it("matches a Map", function() {
jasmine.getEnv().requireFunctioningMaps();
var anything = new jasmineUnderTest.Anything();
expect(anything.asymmetricMatch(new Map())).toBe(true);
});
it("matches a Set", function() {
jasmine.getEnv().requireFunctioningSets();
var anything = new jasmineUnderTest.Anything();
expect(anything.asymmetricMatch(new Set())).toBe(true);
});
it("matches a TypedArray", function() {
jasmine.getEnv().requireFunctioningTypedArrays();
var anything = new jasmineUnderTest.Anything();
expect(anything.asymmetricMatch(new Uint32Array([]))).toBe(true);
});
it("matches a Symbol", function() {
jasmine.getEnv().requireFunctioningSymbols();
var anything = new jasmineUnderTest.Anything();
expect(anything.asymmetricMatch(Symbol())).toBe(true);
});
it("doesn't match undefined", function() {
var anything = new jasmineUnderTest.Anything();

View File

@@ -0,0 +1,47 @@
describe("ArrayWithExactContents", function() {
it("matches an array with the same items in a different order", function() {
var matcher = new jasmineUnderTest.ArrayWithExactContents(['a', 2, /a/]);
expect(matcher.asymmetricMatch([2, 'a', /a/])).toBe(true);
});
it("does not work when not passed an array", function() {
var matcher = new jasmineUnderTest.ArrayWithExactContents("foo");
expect(function() {
matcher.asymmetricMatch([]);
}).toThrowError(/not 'foo'/);
});
it("does not match when an item is missing", function() {
var matcher = new jasmineUnderTest.ArrayWithExactContents(['a', 2, /a/]);
expect(matcher.asymmetricMatch(['a', 2])).toBe(false);
expect(matcher.asymmetricMatch(['a', 2, undefined])).toBe(false);
});
it("does not match when there is an extra item", function() {
var matcher = new jasmineUnderTest.ArrayWithExactContents(['a']);
expect(matcher.asymmetricMatch(['a', 2])).toBe(false);
});
it("jasmineToStrings itself", function() {
var matcher = new jasmineUnderTest.ArrayWithExactContents([]);
expect(matcher.jasmineToString()).toMatch("<jasmine.arrayWithExactContents");
});
it("uses custom equality testers", function() {
var tester = function(a, b) {
// All "foo*" strings match each other.
if (typeof a == "string" && typeof b == "string" &&
a.substr(0, 3) == "foo" && b.substr(0, 3) == "foo") {
return true;
}
};
var matcher = new jasmineUnderTest.ArrayWithExactContents(["fooVal"]);
expect(matcher.asymmetricMatch(["fooBar"], [tester])).toBe(true);
});
});

View File

@@ -67,8 +67,9 @@ describe("Custom Matchers (Integration)", function() {
};
env.addCustomEqualityTester(customEqualityFn);
env.expect({foo: 'fooValue'}).toEqual(jasmine.objectContaining({foo: 'fooBar'}));
env.expect(['fooValue']).toEqual(jasmine.arrayContaining(['fooBar']));
env.expect({foo: 'fooValue'}).toEqual(jasmineUnderTest.objectContaining({foo: 'fooBar'}));
env.expect(['fooValue', 'things']).toEqual(jasmineUnderTest.arrayContaining(['fooBar']));
env.expect(['fooValue']).toEqual(jasmineUnderTest.arrayWithExactContents(['fooBar']));
});
var specExpectations = function(result) {

View File

@@ -243,7 +243,7 @@ describe("Env integration", function() {
} else {
secondSpecContext = this;
}
expect(this).toEqual({});
expect(this).toEqual(new jasmineUnderTest.UserContext());
});
env.it("sync spec", function() {
@@ -277,7 +277,7 @@ describe("Env integration", function() {
env.beforeEach(function() {
specContext = this;
expect(this).toEqual({});
expect(this).toEqual(new jasmineUnderTest.UserContext());
});
env.it("sync spec", function(underTestCallback) {
@@ -456,6 +456,41 @@ describe("Env integration", function() {
env.execute();
});
it("copes with async failures after done has been called", function(done) {
var global = {
setTimeout: function(fn, delay) { setTimeout(fn, delay) },
clearTimeout: function(fn, delay) { clearTimeout(fn, delay) },
};
spyOn(jasmineUnderTest, 'getGlobal').and.returnValue(global);
var env = new jasmineUnderTest.Env(),
reporter = jasmine.createSpyObj('fakeReporter', [ "specDone", "jasmineDone", "suiteDone" ]);
reporter.jasmineDone.and.callFake(function() {
expect(reporter.specDone).not.toHaveFailedExpecationsForRunnable('A suite fails', ['fail thrown']);
expect(reporter.suiteDone).toHaveFailedExpecationsForRunnable('A suite', ['fail thrown']);
done();
});
env.addReporter(reporter);
env.fdescribe('A suite', function() {
env.it('fails', function(specDone) {
setTimeout(function() {
specDone();
setTimeout(function() {
global.onerror('fail');
});
});
});
});
env.describe('Ignored', function() {
env.it('is not run', function() {});
});
env.execute();
});
describe('suiteDone reporting', function(){
it("reports when an afterAll fails an expectation", function(done) {
var env = new jasmineUnderTest.Env(),
@@ -751,7 +786,7 @@ describe("Env integration", function() {
env.execute();
});
it('can be configured to allow respying on functions', function () {
it('can be configured to allow respying on functions', function (done) {
var env = new jasmineUnderTest.Env(),
foo = {
bar: function () {
@@ -760,6 +795,7 @@ describe("Env integration", function() {
};
env.allowRespy(true);
env.addReporter({ jasmineDone: done });
env.describe('test suite', function(){
env.it('spec 0', function(){
@@ -893,7 +929,14 @@ describe("Env integration", function() {
});
it("should run async specs in order, waiting for them to complete", function(done) {
var env = new jasmineUnderTest.Env(), mutatedVar;
var env = new jasmineUnderTest.Env(),
reporter = jasmine.createSpyObj('reporter', ['jasmineDone']),
mutatedVar;
reporter.jasmineDone.and.callFake(function() {
done();
});
env.addReporter(reporter);
env.describe("tests", function() {
env.beforeEach(function() {
@@ -904,7 +947,6 @@ describe("Env integration", function() {
setTimeout(function() {
expect(mutatedVar).toEqual(2);
underTestCallback();
done();
}, 0);
});
@@ -917,16 +959,15 @@ describe("Env integration", function() {
});
describe("with a mock clock", function() {
var originalTimeout;
beforeEach(function() {
originalTimeout = jasmineUnderTest.DEFAULT_TIMEOUT_INTERVAL;
this.originalTimeout = jasmineUnderTest.DEFAULT_TIMEOUT_INTERVAL;
this.realSetTimeout = setTimeout;
jasmine.clock().install();
});
afterEach(function() {
jasmine.clock().uninstall();
jasmineUnderTest.DEFAULT_TIMEOUT_INTERVAL = originalTimeout;
jasmineUnderTest.DEFAULT_TIMEOUT_INTERVAL = this.originalTimeout;
});
it("should wait a specified interval before failing specs haven't called done yet", function(done) {
@@ -1035,7 +1076,6 @@ describe("Env integration", function() {
env.afterAll(function(innerDone) {
jasmine.clock().tick(3001);
innerDone();
jasmine.clock().tick(1);
});
});
@@ -1045,7 +1085,8 @@ describe("Env integration", function() {
it('should wait a custom interval before reporting async functions that fail to call done', function(done) {
var env = new jasmineUnderTest.Env(),
reporter = jasmine.createSpyObj('fakeReport', ['jasmineDone', 'suiteDone', 'specDone']);
reporter = jasmine.createSpyObj('fakeReport', ['jasmineDone', 'suiteDone', 'specDone']),
realSetTimeout = this.realSetTimeout;
reporter.jasmineDone.and.callFake(function() {
expect(reporter.specDone).toHaveFailedExpecationsForRunnable('suite beforeAll times out', [
@@ -1075,10 +1116,19 @@ describe("Env integration", function() {
jasmineUnderTest.DEFAULT_TIMEOUT_INTERVAL = 10000;
env.describe('suite', function() {
env.afterAll(function() {
if (jasmine.getEnv().ieVersion < 9) {
} else {
realSetTimeout(function() {
jasmine.clock().tick(10);
}, 100);
}
});
env.describe('beforeAll', function() {
env.beforeAll(function(innerDone) {
jasmine.clock().tick(5001);
innerDone();
realSetTimeout(function() {
jasmine.clock().tick(5001);
}, 0);
}, 5000);
env.it('times out', function() {});
@@ -1086,8 +1136,9 @@ describe("Env integration", function() {
env.describe('afterAll', function() {
env.afterAll(function(innerDone) {
jasmine.clock().tick(2001);
innerDone();
realSetTimeout(function() {
jasmine.clock().tick(2001);
}, 0);
}, 2000);
env.it('times out', function() {});
@@ -1095,8 +1146,9 @@ describe("Env integration", function() {
env.describe('beforeEach', function() {
env.beforeEach(function(innerDone) {
jasmine.clock().tick(1001);
innerDone();
realSetTimeout(function() {
jasmine.clock().tick(1001);
}, 0);
}, 1000);
env.it('times out', function() {});
@@ -1104,16 +1156,18 @@ describe("Env integration", function() {
env.describe('afterEach', function() {
env.afterEach(function(innerDone) {
jasmine.clock().tick(4001);
innerDone();
realSetTimeout(function() {
jasmine.clock().tick(4001);
}, 0);
}, 4000);
env.it('times out', function() {});
});
env.it('it times out', function(innerDone) {
jasmine.clock().tick(6001);
innerDone();
realSetTimeout(function() {
jasmine.clock().tick(6001);
}, 0);
}, 6000);
});
@@ -1260,7 +1314,8 @@ describe("Env integration", function() {
reporter.jasmineDone.and.callFake(function() {
expect(reporter.jasmineStarted).toHaveBeenCalledWith({
totalSpecsDefined: 1
totalSpecsDefined: 1,
order: jasmine.any(jasmineUnderTest.Order)
});
expect(reporter.specDone).toHaveBeenCalledWith(jasmine.objectContaining({
@@ -1295,7 +1350,8 @@ describe("Env integration", function() {
reporter.jasmineDone.and.callFake(function() {
expect(reporter.jasmineStarted).toHaveBeenCalledWith({
totalSpecsDefined: 1
totalSpecsDefined: 1,
order: jasmine.any(jasmineUnderTest.Order)
});
expect(reporter.specDone).toHaveBeenCalledWith(jasmine.objectContaining({
@@ -1333,7 +1389,8 @@ describe("Env integration", function() {
reporter.jasmineDone.and.callFake(function() {
expect(reporter.jasmineStarted).toHaveBeenCalledWith({
totalSpecsDefined: 5
totalSpecsDefined: 5,
order: jasmine.any(jasmineUnderTest.Order)
});
expect(reporter.specDone.calls.count()).toBe(5);
@@ -1390,6 +1447,34 @@ describe("Env integration", function() {
env.execute();
});
it("should report the random seed at the beginning and end of execution", function(done) {
var env = new jasmineUnderTest.Env(),
reporter = jasmine.createSpyObj('fakeReporter', [
"jasmineStarted",
"jasmineDone",
"suiteStarted",
"suiteDone",
"specStarted",
"specDone"
]);
env.randomizeTests(true);
env.seed('123456');
reporter.jasmineDone.and.callFake(function(doneArg) {
expect(reporter.jasmineStarted).toHaveBeenCalled();
var startedArg = reporter.jasmineStarted.calls.argsFor(0)[0];
expect(startedArg.order.random).toEqual(true);
expect(startedArg.order.seed).toEqual('123456');
expect(doneArg.order.random).toEqual(true);
expect(doneArg.order.seed).toEqual('123456');
done();
});
env.addReporter(reporter);
env.execute();
});
it('should report pending spec messages', function(done) {
var env = new jasmineUnderTest.Env(),
reporter = jasmine.createSpyObj('fakeReporter', [
@@ -1400,6 +1485,7 @@ describe("Env integration", function() {
reporter.jasmineDone.and.callFake(function() {
var specStatus = reporter.specDone.calls.argsFor(0)[0];
expect(specStatus.status).toBe('pending');
expect(specStatus.pendingReason).toBe('with a message');
done();
@@ -1414,6 +1500,45 @@ describe("Env integration", function() {
env.execute();
});
it('should report pending spec messages from promise-returning functions', function(done) {
function StubPromise(fn) {
try {
fn();
} catch (e) {
this.exception = e;
}
}
StubPromise.prototype.then = function(resolve, reject) {
reject(this.exception);
};
var env = new jasmineUnderTest.Env(),
reporter = jasmine.createSpyObj('fakeReporter', [
'specDone',
'jasmineDone'
]);
reporter.jasmineDone.and.callFake(function() {
var specStatus = reporter.specDone.calls.argsFor(0)[0];
expect(specStatus.status).toBe('pending');
expect(specStatus.pendingReason).toBe('with a message');
done();
});
env.addReporter(reporter);
env.it('will be pending', function() {
return new StubPromise(function() {
env.pending('with a message');
});
});
env.execute();
});
it('should report using fallback reporter', function(done) {
var env = new jasmineUnderTest.Env(),
reporter = jasmine.createSpyObj('fakeReporter', [
@@ -1449,7 +1574,8 @@ describe("Env integration", function() {
reporter.jasmineDone.and.callFake(function() {
expect(reporter.jasmineStarted).toHaveBeenCalledWith({
totalSpecsDefined: 1
totalSpecsDefined: 1,
order: jasmine.any(jasmineUnderTest.Order)
});
expect(reporter.specDone).toHaveBeenCalledWith(jasmine.objectContaining({ status: 'disabled' }));
@@ -1817,4 +1943,65 @@ describe("Env integration", function() {
env.execute();
});
it('should throw on suites/specs/befores/afters nested in methods other than \'describe\'', function(done) {
var env = new jasmineUnderTest.Env(),
reporter = jasmine.createSpyObj('reporter', ['jasmineDone', 'suiteDone', 'specDone']);
reporter.jasmineDone.and.callFake(function() {
var msg = /\'.*\' should only be used in \'describe\' function/;
expect(reporter.specDone).toHaveFailedExpecationsForRunnable('suite describe', [msg]);
expect(reporter.specDone).toHaveFailedExpecationsForRunnable('suite xdescribe', [msg]);
expect(reporter.specDone).toHaveFailedExpecationsForRunnable('suite fdescribe', [msg]);
expect(reporter.specDone).toHaveFailedExpecationsForRunnable('spec it', [msg]);
expect(reporter.specDone).toHaveFailedExpecationsForRunnable('spec xit', [msg]);
expect(reporter.specDone).toHaveFailedExpecationsForRunnable('spec fit', [msg]);
expect(reporter.specDone).toHaveFailedExpecationsForRunnable('beforeAll spec', [msg]);
expect(reporter.specDone).toHaveFailedExpecationsForRunnable('beforeEach spec', [msg]);
expect(reporter.suiteDone).toHaveFailedExpecationsForRunnable('afterAll', [msg]);
expect(reporter.specDone).toHaveFailedExpecationsForRunnable('afterEach spec', [msg]);
done();
});
env.addReporter(reporter);
env.describe('suite', function() {
env.it('describe', function() { env.describe('inner suite', function() {}); });
env.it('xdescribe', function() { env.xdescribe('inner suite', function() {}); });
env.it('fdescribe', function() { env.fdescribe('inner suite', function() {}); });
});
env.describe('spec', function() {
env.it('it', function() { env.it('inner spec', function() {}); });
env.it('xit', function() { env.xit('inner spec', function() {}); });
env.it('fit', function() { env.fit('inner spec', function() {}); });
});
env.describe('beforeAll', function() {
env.beforeAll(function() { env.beforeAll(function() {}); });
env.it('spec', function() {});
});
env.describe('beforeEach', function() {
env.beforeEach(function() { env.beforeEach(function() {}); });
env.it('spec', function() {});
});
env.describe('afterAll', function() {
env.afterAll(function() { env.afterAll(function() {}); });
env.it('spec', function() {});
});
env.describe('afterEach', function() {
env.afterEach(function() { env.afterEach(function() {}); });
env.it('spec', function() {});
});
env.execute();
});
});

View File

@@ -836,4 +836,115 @@ describe("jasmine spec running", function () {
env.execute();
});
describe("When throwOnExpectationFailure is set", function() {
it("skips to cleanup functions after an error", function(done) {
var actions = [];
env.describe('Something', function() {
env.beforeEach(function() {
actions.push('outer beforeEach');
throw new Error("error");
});
env.afterEach(function() {
actions.push('outer afterEach');
});
env.describe('Inner', function() {
env.beforeEach(function() {
actions.push('inner beforeEach');
});
env.afterEach(function() {
actions.push('inner afterEach');
});
env.it('does it' , function() {
actions.push('inner it');
});
});
});
env.throwOnExpectationFailure(true);
var assertions = function() {
expect(actions).toEqual([
'outer beforeEach',
'inner afterEach',
'outer afterEach'
]);
done();
};
env.addReporter({jasmineDone: assertions});
env.execute();
});
it("skips to cleanup functions after done.fail is called", function(done) {
var actions = [];
env.describe('Something', function() {
env.beforeEach(function(done) {
actions.push('beforeEach');
done.fail('error');
actions.push('after done.fail');
});
env.afterEach(function() {
actions.push('afterEach');
});
env.it('does it' , function() {
actions.push('it');
});
});
env.throwOnExpectationFailure(true);
var assertions = function() {
expect(actions).toEqual([
'beforeEach',
'afterEach'
]);
done();
};
env.addReporter({jasmineDone: assertions});
env.execute();
});
it("skips to cleanup functions when an async function times out", function(done) {
var actions = [];
env.describe('Something', function() {
env.beforeEach(function(innerDone) {
actions.push('beforeEach');
}, 1);
env.afterEach(function() {
actions.push('afterEach');
});
env.it('does it' , function() {
actions.push('it');
});
});
env.throwOnExpectationFailure(true);
var assertions = function() {
expect(actions).toEqual([
'beforeEach',
'afterEach'
]);
done();
};
env.addReporter({jasmineDone: assertions});
env.execute();
});
});
});

View File

@@ -71,7 +71,7 @@ describe("matchersUtil", function() {
it("fails for Arrays whose contents are equivalent, but have differing properties", function() {
var one = [1,2,3],
two = [1,2,3];
two = [1,2,3];
one.foo = 'bar';
two.foo = 'baz';
@@ -81,7 +81,7 @@ describe("matchersUtil", function() {
it("passes for Arrays with equivalent contents and properties", function() {
var one = [1,2,3],
two = [1,2,3];
two = [1,2,3];
one.foo = 'bar';
two.foo = 'bar';
@@ -122,7 +122,7 @@ describe("matchersUtil", function() {
it("passes for Objects that are equivalent (with cycles)", function() {
var actual = { a: "foo" },
expected = { a: "foo" };
expected = { a: "foo" };
actual.b = actual;
expected.b = actual;
@@ -166,6 +166,16 @@ describe("matchersUtil", function() {
expect(jasmineUnderTest.matchersUtil.equals(a,b)).toBe(true);
});
it("passes for equivalent Promises (GitHub issue #1314)", function() {
if (typeof Promise === 'undefined') { return; }
var p1 = new Promise(function () {}),
p2 = new Promise(function () {});
expect(jasmineUnderTest.matchersUtil.equals(p1, p1)).toBe(true);
expect(jasmineUnderTest.matchersUtil.equals(p1, p2)).toBe(false);
});
describe("when running in a browser", function() {
function isNotRunningInBrowser() {
@@ -332,7 +342,7 @@ describe("matchersUtil", function() {
it("passes for an asymmetric equality tester that returns true when a custom equality tester return false", function() {
var asymmetricTester = { asymmetricMatch: function(other) { return true; } },
symmetricTester = function(a, b) { return false; };
symmetricTester = function(a, b) { return false; };
expect(jasmineUnderTest.matchersUtil.equals(asymmetricTester, true, [symmetricTester])).toBe(true);
expect(jasmineUnderTest.matchersUtil.equals(true, asymmetricTester, [symmetricTester])).toBe(true);
@@ -350,7 +360,7 @@ describe("matchersUtil", function() {
it("passes when an Any is compared to an Any that checks for the same type", function() {
var any1 = new jasmineUnderTest.Any(Function),
any2 = new jasmineUnderTest.Any(Function);
any2 = new jasmineUnderTest.Any(Function);
expect(jasmineUnderTest.matchersUtil.equals(any1, any2)).toBe(true);
});
@@ -359,7 +369,7 @@ describe("matchersUtil", function() {
if (jasmine.getEnv().ieVersion < 9) { return; }
var objA = Object.create(null),
objB = Object.create(null);
objB = Object.create(null);
objA.name = 'test';
objB.name = 'test';
@@ -371,7 +381,7 @@ describe("matchersUtil", function() {
if (jasmine.getEnv().ieVersion < 9) { return; }
var objA = Object.create(null),
objB = Object.create(null);
objB = Object.create(null);
objA.name = 'test';
objB.test = 'name';
@@ -386,32 +396,142 @@ describe("matchersUtil", function() {
it("passes when comparing identical sets", function() {
jasmine.getEnv().requireFunctioningSets();
var setA = new Set([6, 5]);
var setA = new Set();
setA.add(6);
setA.add(5);
var setB = new Set();
setB.add(6);
setB.add(5);
expect(jasmineUnderTest.matchersUtil.equals(setA, setB)).toBe(true);
});
it("passes when comparing identical sets with different insertion order and simple elements", function() {
jasmine.getEnv().requireFunctioningSets();
var setA = new Set();
setA.add(3);
setA.add(6);
var setB = new Set();
setB.add(6);
setB.add(3);
expect(jasmineUnderTest.matchersUtil.equals(setA, setB)).toBe(true);
});
it("passes when comparing identical sets with different insertion order and complex elements 1", function() {
jasmine.getEnv().requireFunctioningSets();
var setA1 = new Set();
setA1.add(['a',3]);
setA1.add([6,1]);
var setA2 = new Set();
setA1.add(['y',3]);
setA1.add([6,1]);
var setA = new Set();
setA.add(setA1);
setA.add(setA2);
var setB1 = new Set();
setB1.add([6,1]);
setB1.add(['a',3]);
var setB2 = new Set();
setB1.add([6,1]);
setB1.add(['y',3]);
var setB = new Set();
setB.add(setB1);
setB.add(setB2);
expect(jasmineUnderTest.matchersUtil.equals(setA, setB)).toBe(true);
});
it("passes when comparing identical sets with different insertion order and complex elements 2", function() {
jasmine.getEnv().requireFunctioningSets();
var setA = new Set();
setA.add([[1,2], [3,4]]);
setA.add([[5,6], [7,8]]);
var setB = new Set();
setB.add([[5,6], [7,8]]);
setB.add([[1,2], [3,4]]);
expect(jasmineUnderTest.matchersUtil.equals(setA, setB)).toBe(true);
});
it("fails for sets with different elements", function() {
jasmine.getEnv().requireFunctioningSets();
var setA = new Set([6, 3, 5]);
var setB = new Set([6, 4, 5]);
var setA = new Set();
setA.add(6);
setA.add(3);
setA.add(5);
var setB = new Set();
setB.add(6);
setB.add(4);
setB.add(5);
expect(jasmineUnderTest.matchersUtil.equals(setA, setB)).toBe(false);
});
it("fails for sets of different size", function() {
jasmine.getEnv().requireFunctioningSets();
var setA = new Set([6, 3]);
var setB = new Set([6, 4, 5]);
var setA = new Set();
setA.add(6);
setA.add(3);
var setB = new Set();
setB.add(6);
setB.add(4);
setB.add(5);
expect(jasmineUnderTest.matchersUtil.equals(setA, setB)).toBe(false);
});
it("fails for sets with different insertion order", function() {
jasmine.getEnv().requireFunctioningSets();
var setA = new Set([3, 6]);
var setB = new Set([6, 3]);
expect(jasmineUnderTest.matchersUtil.equals(setA, setB)).toBe(false);
it("passes when comparing two empty maps", function() {
jasmine.getEnv().requireFunctioningMaps();
expect(jasmineUnderTest.matchersUtil.equals(new Map(), new Map())).toBe(true);
});
it("passes when comparing identical maps", function() {
jasmine.getEnv().requireFunctioningMaps();
var mapA = new Map();
mapA.set(6, 5);
var mapB = new Map();
mapB.set(6, 5);
expect(jasmineUnderTest.matchersUtil.equals(mapA, mapB)).toBe(true);
});
it("passes when comparing identical maps with different insertion order", function() {
jasmine.getEnv().requireFunctioningMaps();
var mapA = new Map();
mapA.set("a", 3);
mapA.set(6, 1);
var mapB = new Map();
mapB.set(6, 1);
mapB.set("a", 3);
expect(jasmineUnderTest.matchersUtil.equals(mapA, mapB)).toBe(true);
});
it("fails for maps with different elements", function() {
jasmine.getEnv().requireFunctioningMaps();
var mapA = new Map();
mapA.set(6, 3);
mapA.set(5, 1);
var mapB = new Map();
mapB.set(6, 4);
mapB.set(5, 1);
expect(jasmineUnderTest.matchersUtil.equals(mapA, mapB)).toBe(false);
});
it("fails for maps of different size", function() {
jasmine.getEnv().requireFunctioningMaps();
var mapA = new Map();
mapA.set(6, 3);
var mapB = new Map();
mapB.set(6, 4);
mapB.set(5, 1);
expect(jasmineUnderTest.matchersUtil.equals(mapA, mapB)).toBe(false);
});
describe("when running in an environment with array polyfills", function() {
@@ -427,28 +547,28 @@ describe("matchersUtil", function() {
Object.defineProperty(Array.prototype, 'findIndex', {
enumerable: true,
value: function (predicate) {
if (this === null) {
throw new TypeError('Array.prototype.findIndex called on null or undefined');
}
if (this === null) {
throw new TypeError('Array.prototype.findIndex called on null or undefined');
}
if (typeof predicate !== 'function') {
throw new TypeError('predicate must be a function');
}
if (typeof predicate !== 'function') {
throw new TypeError('predicate must be a function');
}
var list = Object(this);
var length = list.length >>> 0;
var thisArg = arguments[1];
var value;
var list = Object(this);
var length = list.length >>> 0;
var thisArg = arguments[1];
var value;
for (var i = 0; i < length; i++) {
value = list[i];
if (predicate.call(thisArg, value, i, list)) {
return i;
}
}
for (var i = 0; i < length; i++) {
value = list[i];
if (predicate.call(thisArg, value, i, list)) {
return i;
}
}
return -1;
}
return -1;
}
});
});

View File

@@ -0,0 +1,8 @@
describe('nothing', function() {
it('should pass', function() {
var matcher = jasmineUnderTest.matchers.nothing(),
result = matcher.compare();
expect(result.pass).toBe(true);
});
});

View File

@@ -8,6 +8,9 @@ describe("toBeCloseTo", function() {
result = matcher.compare(0, 0.001);
expect(result.pass).toBe(true);
result = matcher.compare(0, 0.005);
expect(result.pass).toBe(true);
});
it("fails when not within two decimal places by default", function() {
@@ -16,6 +19,9 @@ describe("toBeCloseTo", function() {
result = matcher.compare(0, 0.01);
expect(result.pass).toBe(false);
result = matcher.compare(0, 0.05);
expect(result.pass).toBe(false);
});
it("accepts an optional precision argument", function() {
@@ -25,8 +31,36 @@ describe("toBeCloseTo", function() {
result = matcher.compare(0, 0.1, 0);
expect(result.pass).toBe(true);
result = matcher.compare(0, 0.5, 0);
expect(result.pass).toBe(true);
result = matcher.compare(0, 0.0001, 3);
expect(result.pass).toBe(true);
result = matcher.compare(0, 0.0005, 3);
expect(result.pass).toBe(true);
result = matcher.compare(0, 0.00001, 4);
expect(result.pass).toBe(true);
result = matcher.compare(0, 0.00005, 4);
expect(result.pass).toBe(true);
});
it("fails when one of the arguments is null", function() {
var matcher = jasmineUnderTest.matchers.toBeCloseTo();
expect(function() {
matcher.compare(null, null);
}).toThrowError('Cannot use toBeCloseTo with null. Arguments evaluated to: expect(null).toBeCloseTo(null).');
expect(function() {
matcher.compare(0, null);
}).toThrowError('Cannot use toBeCloseTo with null. Arguments evaluated to: expect(0).toBeCloseTo(null).');
expect(function() {
matcher.compare(null, 0);
}).toThrowError('Cannot use toBeCloseTo with null. Arguments evaluated to: expect(null).toBeCloseTo(0).');
});
it("rounds expected values", function() {
@@ -42,7 +76,15 @@ describe("toBeCloseTo", function() {
result = matcher.compare(1.23, 1.225);
expect(result.pass).toBe(true);
result = matcher.compare(1.23, 1.235);
expect(result.pass).toBe(true);
// 1.2249999 will be rounded to 1.225
result = matcher.compare(1.23, 1.2249999);
expect(result.pass).toBe(true);
// 1.2249999 will be rounded to 1.224
result = matcher.compare(1.23, 1.2244999);
expect(result.pass).toBe(false);
result = matcher.compare(1.23, 1.234);

View File

@@ -161,8 +161,8 @@ describe("toEqual", function() {
it("uses the default failure message given arrays with different lengths", function() {
var actual = [1, 2],
expected = [1, 2, 3],
message =
"Expected [ 1, 2 ] to equal [ 1, 2, 3 ].";
message = 'Expected $.length = 2 to equal 3.\n' +
'Expected $[2] = undefined to equal 3.';
expect(compareEquals(actual, expected).message).toEqual(message);
});
@@ -211,6 +211,16 @@ describe("toEqual", function() {
expect(compareEquals(actual, expected).message).toEqual(message);
});
it("reports mismatches between arrays of different types", function() {
jasmine.getEnv().requireFunctioningTypedArrays();
var actual = new Uint32Array([1, 2, 3]),
expected = new Uint16Array([1, 2, 3]),
message = "Expected Uint32Array [ 1, 2, 3 ] to equal Uint16Array [ 1, 2, 3 ].";
expect(compareEquals(actual, expected).message).toEqual(message);
});
it("reports mismatches involving NaN", function() {
var actual = {x: 0},
expected = {x: 0/0},
@@ -359,13 +369,28 @@ describe("toEqual", function() {
expect(compareEquals(actual, expected).message).toEqual(message);
});
it("does not report deep mismatches within Sets", function() {
// TODO: implement deep comparison of set elements
// == Sets ==
it("reports mismatches between Sets", function() {
jasmine.getEnv().requireFunctioningSets();
var actual = new Set([1]),
expected = new Set([2]),
message = 'Expected Set( 1 ) to equal Set( 2 ).';
var actual = new Set();
actual.add(1);
var expected = new Set();
expected.add(2);
var message = 'Expected Set( 1 ) to equal Set( 2 ).';
expect(compareEquals(actual, expected).message).toEqual(message);
});
it("reports deep mismatches within Sets", function() {
jasmine.getEnv().requireFunctioningSets();
var actual = new Set();
actual.add({x: 1});
var expected = new Set();
expected.add({x: 2});
var message = 'Expected Set( Object({ x: 1 }) ) to equal Set( Object({ x: 2 }) ).';
expect(compareEquals(actual, expected).message).toEqual(message);
});
@@ -373,9 +398,14 @@ describe("toEqual", function() {
it("reports mismatches between Sets nested in objects", function() {
jasmine.getEnv().requireFunctioningSets();
var actual = {sets: [new Set([1])]},
expected = {sets: [new Set([2])]},
message = "Expected $.sets[0] = Set( 1 ) to equal Set( 2 ).";
var actualSet = new Set();
actualSet.add(1);
var expectedSet = new Set();
expectedSet.add(2);
var actual = { sets: [actualSet] };
var expected = { sets: [expectedSet] };
var message = "Expected $.sets[0] = Set( 1 ) to equal Set( 2 ).";
expect(compareEquals(actual, expected).message).toEqual(message);
});
@@ -383,13 +413,182 @@ describe("toEqual", function() {
it("reports mismatches between Sets of different lengths", function() {
jasmine.getEnv().requireFunctioningSets();
var actual = new Set([1, 2]),
expected = new Set([2]),
message = 'Expected Set( 1, 2 ) to equal Set( 2 ).';
var actual = new Set();
actual.add(1);
actual.add(2);
var expected = new Set();
expected.add(2);
var message = 'Expected Set( 1, 2 ) to equal Set( 2 ).';
expect(compareEquals(actual, expected).message).toEqual(message);
});
it("reports mismatches between Sets where actual is missing a value from expected", function() {
jasmine.getEnv().requireFunctioningSets();
// Use 'duplicate' object in actual so sizes match
var actual = new Set();
actual.add({x: 1});
actual.add({x: 1});
var expected = new Set();
expected.add({x: 1});
expected.add({x: 2});
var message = 'Expected Set( Object({ x: 1 }), Object({ x: 1 }) ) to equal Set( Object({ x: 1 }), Object({ x: 2 }) ).';
expect(compareEquals(actual, expected).message).toEqual(message);
});
it("reports mismatches between Sets where actual has a value missing from expected", function() {
jasmine.getEnv().requireFunctioningSets();
// Use 'duplicate' object in expected so sizes match
var actual = new Set();
actual.add({x: 1});
actual.add({x: 2});
var expected = new Set();
expected.add({x: 1});
expected.add({x: 1});
var message = 'Expected Set( Object({ x: 1 }), Object({ x: 2 }) ) to equal Set( Object({ x: 1 }), Object({ x: 1 }) ).';
expect(compareEquals(actual, expected).message).toEqual(message);
});
// == Maps ==
it("does not report mismatches between deep equal Maps", function() {
jasmine.getEnv().requireFunctioningMaps();
// values are the same but with different object identity
var actual = new Map();
actual.set('a',{x:1});
var expected = new Map();
expected.set('a',{x:1});
expect(compareEquals(actual, expected).pass).toBe(true);
});
it("reports deep mismatches within Maps", function() {
jasmine.getEnv().requireFunctioningMaps();
var actual = new Map();
actual.set('a',{x:1});
var expected = new Map();
expected.set('a',{x:2});
var message = "Expected Map( [ 'a', Object({ x: 1 }) ] ) to equal Map( [ 'a', Object({ x: 2 }) ] ).";
expect(compareEquals(actual, expected).message).toEqual(message);
});
it("reports mismatches between Maps nested in objects", function() {
jasmine.getEnv().requireFunctioningMaps();
var actual = {Maps:[new Map()]};
actual.Maps[0].set('a',1);
var expected = {Maps:[new Map()]};
expected.Maps[0].set('a',2);
var message = "Expected $.Maps[0] = Map( [ 'a', 1 ] ) to equal Map( [ 'a', 2 ] ).";
expect(compareEquals(actual, expected).message).toEqual(message);
});
it("reports mismatches between Maps of different lengths", function() {
jasmine.getEnv().requireFunctioningMaps();
var actual = new Map();
actual.set('a',1);
var expected = new Map();
expected.set('a',2);
expected.set('b',1);
var message = "Expected Map( [ 'a', 1 ] ) to equal Map( [ 'a', 2 ], [ 'b', 1 ] ).";
expect(compareEquals(actual, expected).message).toEqual(message);
});
it("reports mismatches between Maps with equal values but differing keys", function() {
jasmine.getEnv().requireFunctioningMaps();
var actual = new Map();
actual.set('a',1);
var expected = new Map();
expected.set('b',1);
var message = "Expected Map( [ 'a', 1 ] ) to equal Map( [ 'b', 1 ] ).";
expect(compareEquals(actual, expected).message).toEqual(message);
});
it("does not report mismatches between Maps with keys with same object identity", function() {
jasmine.getEnv().requireFunctioningMaps();
var key = {x: 1};
var actual = new Map();
actual.set(key,2);
var expected = new Map();
expected.set(key,2);
expect(compareEquals(actual, expected).pass).toBe(true);
});
it("reports mismatches between Maps with identical keys with different object identity", function() {
jasmine.getEnv().requireFunctioningMaps();
var actual = new Map();
actual.set({x:1},2);
var expected = new Map();
expected.set({x:1},2);
var message = "Expected Map( [ Object({ x: 1 }), 2 ] ) to equal Map( [ Object({ x: 1 }), 2 ] ).";
expect(compareEquals(actual, expected).message).toEqual(message);
});
it("does not report mismatches when comparing Map key to jasmine.anything()", function() {
jasmine.getEnv().requireFunctioningMaps();
var actual = new Map();
actual.set('a',1);
var expected = new Map();
expected.set(jasmineUnderTest.anything(),1);
expect(compareEquals(actual, expected).pass).toBe(true);
});
it("does not report mismatches when comparing Maps with the same symbol keys", function() {
jasmine.getEnv().requireFunctioningMaps();
jasmine.getEnv().requireFunctioningSymbols();
var key = Symbol();
var actual = new Map();
actual.set(key,1);
var expected = new Map();
expected.set(key,1);
expect(compareEquals(actual, expected).pass).toBe(true);
});
it("reports mismatches between Maps with different symbol keys", function() {
jasmine.getEnv().requireFunctioningMaps();
jasmine.getEnv().requireFunctioningSymbols();
var actual = new Map();
actual.set(Symbol(),1);
var expected = new Map();
expected.set(Symbol(),1);
var message = "Expected Map( [ Symbol(), 1 ] ) to equal Map( [ Symbol(), 1 ] ).";
expect(compareEquals(actual, expected).message).toBe(message);
});
it("does not report mismatches when comparing Map symbol key to jasmine.anything()", function() {
jasmine.getEnv().requireFunctioningMaps();
jasmine.getEnv().requireFunctioningSymbols();
var actual = new Map();
actual.set(Symbol(),1);
var expected = new Map();
expected.set(jasmineUnderTest.anything(),1);
expect(compareEquals(actual, expected).pass).toBe(true);
});
function isNotRunningInBrowser() {
return typeof document === 'undefined'
}
@@ -456,9 +655,9 @@ describe("toEqual", function() {
it("does not report a mismatch when asymmetric matchers are satisfied", function() {
var actual = {a: 'a'},
expected = {a: jasmineUnderTest.any(String)},
message = 'Expected $.a = 1 to equal <jasmine.any(String)>.';
expected = {a: jasmineUnderTest.any(String)};
expect(compareEquals(actual, expected).message).toEqual('');
expect(compareEquals(actual, expected).pass).toBe(true)
});
@@ -499,7 +698,8 @@ describe("toEqual", function() {
var message =
'Expected $.foo[0].bar = 1 to equal 2.\n' +
"Expected $.foo[0].things = [ 'a', 'b' ] to equal [ 'a', 'b', 'c' ].\n" +
'Expected $.foo[0].things.length = 2 to equal 3.\n' +
"Expected $.foo[0].things[2] = undefined to equal 'c'.\n" +
"Expected $.foo[1].things[1] = 'b' to equal 'd'.\n" +
'Expected $.baz[0].a to have properties\n' +
' c: 1\n' +
@@ -513,4 +713,92 @@ describe("toEqual", function() {
expect(compareEquals(actual, expected).message).toEqual(message);
})
describe("different length arrays", function() {
it("actual array is longer", function() {
var actual = [1, 1, 2, 3, 5],
expected = [1, 1, 2, 3],
message = 'Expected $.length = 5 to equal 4.\n' +
'Expected $[4] = 5 to equal undefined.';
expect(compareEquals(actual, expected).pass).toBe(false)
expect(compareEquals(actual, expected).message).toEqual(message);
});
it("expected array is longer", function() {
var actual = [1, 1, 2, 3],
expected = [1, 1, 2, 3, 5],
message = 'Expected $.length = 4 to equal 5.\n' +
'Expected $[4] = undefined to equal 5.';
expect(compareEquals(actual, expected).pass).toBe(false)
expect(compareEquals(actual, expected).message).toEqual(message);
});
it("expected array is longer by 4 elements", function() {
var actual = [1, 1, 2],
expected = [1, 1, 2, 3, 5, 8, 13],
message = 'Expected $.length = 3 to equal 7.\n' +
'Expected $[3] = undefined to equal 3.\n' +
'Expected $[4] = undefined to equal 5.\n' +
'Expected $[5] = undefined to equal 8.\n' +
'Expected $[6] = undefined to equal 13.';
expect(compareEquals(actual, expected).pass).toBe(false)
expect(compareEquals(actual, expected).message).toEqual(message);
});
it("different length and different elements", function() {
var actual = [1],
expected = [2, 3],
message = 'Expected $.length = 1 to equal 2.\n' +
'Expected $[0] = 1 to equal 2.\n' +
'Expected $[1] = undefined to equal 3.';
expect(compareEquals(actual, expected).pass).toBe(false)
expect(compareEquals(actual, expected).message).toEqual(message);
});
it("object with nested array", function() {
var actual = { values: [1, 1, 2, 3] },
expected = { values: [1, 1, 2] },
message = 'Expected $.values.length = 4 to equal 3.\n' +
'Expected $.values[3] = 3 to equal undefined.';
expect(compareEquals(actual, expected).pass).toBe(false)
expect(compareEquals(actual, expected).message).toEqual(message);
});
it("array with nested object", function() {
var actual = [1, 1, 2, { value: 3 }],
expected = [1, 1, 2],
message = 'Expected $.length = 4 to equal 3.\n' +
'Expected $[3] = Object({ value: 3 }) to equal undefined.';
expect(compareEquals(actual, expected).pass).toBe(false)
expect(compareEquals(actual, expected).message).toEqual(message);
});
it("array with nested different length array", function() {
var actual = [[1], [1, 2]],
expected = [[1, 1], [2]],
message = 'Expected $[0].length = 1 to equal 2.\n' +
'Expected $[0][1] = undefined to equal 1.\n' +
'Expected $[1].length = 2 to equal 1.\n' +
'Expected $[1][0] = 1 to equal 2.\n' +
'Expected $[1][1] = 2 to equal undefined.';
expect(compareEquals(actual, expected).pass).toBe(false)
expect(compareEquals(actual, expected).message).toEqual(message);
});
it("last element of longer array is undefined", function() {
var actual = [1, 2],
expected = [1, 2, void 0],
message = 'Expected $.length = 2 to equal 3.';
expect(compareEquals(actual, expected).pass).toBe(false)
expect(compareEquals(actual, expected).message).toEqual(message);
});
})
});

View File

@@ -0,0 +1,27 @@
(function(env) {
function getAsyncCtor() {
try {
eval("var func = async function(){};");
} catch (e) {
return null;
}
return Object.getPrototypeOf(func).constructor;
}
function hasAsyncAwaitSupport() {
return getAsyncCtor() !== null;
}
env.makeAsyncAwaitFunction = function() {
var AsyncFunction = getAsyncCtor();
return new AsyncFunction("");
};
env.requireAsyncAwait = function() {
if (!hasAsyncAwaitSupport()) {
env.pending("Environment does not support async/await functions");
}
};
})(jasmine.getEnv());

View File

@@ -0,0 +1,37 @@
(function(env) {
function hasFunctioningMaps() {
if (typeof Map === 'undefined') { return false; }
try {
var s = new Map();
s.set('a',1);
s.set('b',2);
if (s.size !== 2) { return false; }
if (s.has('a') !== true) { return false; }
var iterations = 0;
var ifForEachWorking = true;
s.forEach(function(value, key, map) {
ifForEachWorking = ifForEachWorking && map === s;
if (key==='a') {
ifForEachWorking = ifForEachWorking && value===1;
}
iterations++;
});
if (iterations !== 2) { return false; }
if (ifForEachWorking !== true) { return false; }
return true;
} catch(e) {
return false;
}
}
env.requireFunctioningMaps = function() {
if (!hasFunctioningMaps()) {
env.pending("Browser has incomplete or missing support for Maps");
}
};
})(jasmine.getEnv());

View File

@@ -3,9 +3,29 @@
if (typeof Set === 'undefined') { return false; }
try {
var s = new Set([4]);
if (s.size !== 1) { return false; }
if (s.values().next().value !== 4) { return false; }
var s = new Set();
s.add(1);
s.add(2);
if (s.size !== 2) { return false; }
if (s.has(1) !== true) { return false; }
var iterations = 0;
var isForEachWorking = true;
s.forEach(function(value, key, set) {
isForEachWorking = isForEachWorking && set === s;
if (iterations===0) {
isForEachWorking = isForEachWorking && (key===value) && value===1;
} else if (iterations===1) {
isForEachWorking = isForEachWorking && (key===value) && value===2;
}
iterations++;
});
if (iterations !== 2) { return false; }
if (isForEachWorking !== true) { return false; }
return true;
} catch(e) {
return false;

View File

@@ -0,0 +1,28 @@
(function(env) {
function hasFunctioningSymbols() {
if (typeof Symbol === 'undefined') {
return false;
}
try {
var s1 = Symbol();
var s2 = Symbol();
if (typeof s1 !== 'symbol') {
return false;
}
if (s1 === s2) {
return false;
}
return true;
} catch (e) {
return false;
}
}
env.requireFunctioningSymbols = function() {
if (!hasFunctioningSymbols()) {
env.pending("Browser has incomplete or missing support for Symbols");
}
};
})(jasmine.getEnv());

View File

@@ -0,0 +1,20 @@
(function(env) {
function hasFunctioningTypedArrays() {
if (typeof Uint32Array === 'undefined') { return false; }
try {
var a = new Uint32Array([1, 2, 3]);
if (a.length !== 3) { return false; }
return true;
} catch(e) {
return false;
}
}
env.requireFunctioningTypedArrays = function() {
if (!hasFunctioningTypedArrays()) {
env.pending("Browser has incomplete or missing support for typed arrays");
}
};
})(jasmine.getEnv());

View File

@@ -209,7 +209,7 @@ describe("New HtmlReporter", function() {
});
describe("when Jasmine is done", function() {
it("adds EMPTY to the link title of specs that have no expectations", function() {
it("adds a warning to the link title of specs that have no expectations", function() {
if (!window.console) {
window.console = { error: function(){} };
}
@@ -228,7 +228,7 @@ describe("New HtmlReporter", function() {
reporter.initialize();
reporter.jasmineStarted({});
reporter.suiteStarted({id: 1});
reporter.specStarted({id: 1, status: 'passed', passedExpectations: [], failedExpectations: []});
reporter.specStarted({id: 1, passedExpectations: [], failedExpectations: []});
reporter.specDone({
id: 1,
status: 'passed',
@@ -691,14 +691,15 @@ describe("New HtmlReporter", function() {
expect(seedBar).toBeNull();
});
it("should include the random query param in the jasmine-skipped link when randomizing", function(){
it("should include non-spec query params in the jasmine-skipped link when present", function(){
var env = new jasmineUnderTest.Env(),
container = document.createElement("div"),
reporter = new jasmineUnderTest.HtmlReporter({
env: env,
getContainer: function() { return container; },
createElement: function() { return document.createElement.apply(document, arguments); },
createTextNode: function() { return document.createTextNode.apply(document, arguments); }
createTextNode: function() { return document.createTextNode.apply(document, arguments); },
addToExistingQueryString: function(key, value) { return "?foo=bar&" + key + "=" + value; }
});
reporter.initialize();
@@ -706,25 +707,7 @@ describe("New HtmlReporter", function() {
reporter.jasmineDone({ order: { random: true } });
var skippedLink = container.querySelector(".jasmine-skipped a");
expect(skippedLink.getAttribute('href')).toEqual('?random=true');
});
it("should not include the random query param in the jasmine-skipped link when not randomizing", function(){
var env = new jasmineUnderTest.Env(),
container = document.createElement("div"),
reporter = new jasmineUnderTest.HtmlReporter({
env: env,
getContainer: function() { return container; },
createElement: function() { return document.createElement.apply(document, arguments); },
createTextNode: function() { return document.createTextNode.apply(document, arguments); }
});
reporter.initialize();
reporter.jasmineStarted({ totalSpecsDefined: 1 });
reporter.jasmineDone();
var skippedLink = container.querySelector(".jasmine-skipped a");
expect(skippedLink.getAttribute('href')).toEqual('?');
expect(skippedLink.getAttribute('href')).toEqual('?foo=bar&spec=');
});
});

View File

@@ -0,0 +1,34 @@
describe('Spy Registry browser-specific behavior', function() {
it('can spy on and unspy window.onerror', function() {
requireWriteableOnerror();
var spies = [],
spyRegistry = new jasmineUnderTest.SpyRegistry({
currentSpies: function() { return spies; },
global: window
}),
originalHandler = window.onerror;
try {
spyRegistry.spyOn(window, 'onerror');
spyRegistry.clearSpies();
expect(window.onerror).toBe(originalHandler);
} finally {
window.onerror = originalHandler;
}
});
function requireWriteableOnerror() {
var descriptor;
try {
descriptor = Object.getOwnPropertyDescriptor(window, 'onerror');
} catch(e) {
// IE 8 doesn't support `definePropery` on non-DOM nodes
}
if (descriptor && !(descriptor.writable || descriptor.set)) {
pending('Browser declares window.onerror to be readonly');
}
}
});

View File

@@ -6,7 +6,11 @@
"npmPackage/**/*.js"
],
"helpers": [
"helpers/asyncAwait.js",
"helpers/checkForMap.js",
"helpers/checkForSet.js",
"helpers/checkForSymbol.js",
"helpers/checkForTypedArrays.js",
"helpers/nodeDefineJasmineUnderTest.js"
],
"random": true

View File

@@ -3,6 +3,7 @@
src_dir:
- 'src'
src_files:
- 'core/requireCore.js'
- 'core/base.js'
- 'core/util.js'
#end of known dependencies
@@ -16,8 +17,12 @@ src_files:
- '**/*.js'
stylesheets:
helpers:
- 'helpers/asyncAwait.js'
- 'helpers/BrowserFlags.js'
- 'helpers/checkForMap.js'
- 'helpers/checkForSet.js'
- 'helpers/checkForSymbol.js'
- 'helpers/checkForTypedArrays.js'
- 'helpers/defineJasmineUnderTest.js'
spec_files:
- '**/*[Ss]pec.js'

View File

@@ -7,22 +7,9 @@ getJasmineRequireObj().CallTracker = function(j$) {
var calls = [];
var opts = {};
function argCloner(context) {
var clonedArgs = [];
var argsAsArray = j$.util.argsToArray(context.args);
for(var i = 0; i < argsAsArray.length; i++) {
if(Object.prototype.toString.apply(argsAsArray[i]).match(/^\[object/)) {
clonedArgs.push(j$.util.clone(argsAsArray[i]));
} else {
clonedArgs.push(argsAsArray[i]);
}
}
context.args = clonedArgs;
}
this.track = function(context) {
if(opts.cloneArgs) {
argCloner(context);
context.args = j$.util.cloneArgs(context.args);
}
calls.push(context);
};

View File

@@ -1,35 +1,67 @@
getJasmineRequireObj().clearStack = function(j$) {
function messageChannelImpl(global) {
var maxInlineCallCount = 10;
function messageChannelImpl(global, setTimeout) {
var channel = new global.MessageChannel(),
head = {},
tail = head;
var taskRunning = false;
channel.port1.onmessage = function() {
head = head.next;
var task = head.task;
delete head.task;
task();
if (taskRunning) {
global.setTimeout(task, 0);
} else {
try {
taskRunning = true;
task();
} finally {
taskRunning = false;
}
}
};
var currentCallCount = 0;
return function clearStack(fn) {
tail = tail.next = { task: fn };
channel.port2.postMessage(0);
currentCallCount++;
if (currentCallCount < maxInlineCallCount) {
tail = tail.next = { task: fn };
channel.port2.postMessage(0);
} else {
currentCallCount = 0;
setTimeout(fn);
}
};
}
function getClearStack(global) {
var currentCallCount = 0;
var realSetTimeout = global.setTimeout;
var setTimeoutImpl = function clearStack(fn) {
Function.prototype.apply.apply(realSetTimeout, [global, [fn, 0]]);
};
if (j$.isFunction_(global.setImmediate)) {
var realSetImmediate = global.setImmediate;
return function(fn) {
realSetImmediate(fn);
currentCallCount++;
if (currentCallCount < maxInlineCallCount) {
realSetImmediate(fn);
} else {
currentCallCount = 0;
setTimeoutImpl(fn);
}
};
} else if (!j$.util.isUndefined(global.MessageChannel)) {
return messageChannelImpl(global);
return messageChannelImpl(global, setTimeoutImpl);
} else {
var realSetTimeout = global.setTimeout;
return function clearStack(fn) {
Function.prototype.apply.apply(realSetTimeout, [global, [fn, 0]]);
};
return setTimeoutImpl;
}
}

View File

@@ -1,4 +1,7 @@
getJasmineRequireObj().Clock = function() {
var NODE_JS = typeof process !== 'undefined' && process.versions && typeof process.versions.node === 'string';
/**
* _Note:_ Do not construct this directly, Jasmine will make one during booting. You can get the current clock with {@link jasmine.clock}.
* @class Clock
@@ -22,6 +25,7 @@ getJasmineRequireObj().Clock = function() {
delayedFunctionScheduler,
timer;
self.FakeTimeout = FakeTimeout;
/**
* Install the mock clock over the built-in methods.
@@ -145,7 +149,15 @@ getJasmineRequireObj().Clock = function() {
}
function setTimeout(fn, delay) {
return delayedFunctionScheduler.scheduleFunction(fn, delay, argSlice(arguments, 2));
if (!NODE_JS) {
return delayedFunctionScheduler.scheduleFunction(fn, delay, argSlice(arguments, 2));
}
var timeout = new FakeTimeout();
delayedFunctionScheduler.scheduleFunction(fn, delay, argSlice(arguments, 2), false, timeout);
return timeout;
}
function clearTimeout(id) {
@@ -153,7 +165,15 @@ getJasmineRequireObj().Clock = function() {
}
function setInterval(fn, interval) {
return delayedFunctionScheduler.scheduleFunction(fn, interval, argSlice(arguments, 2), true);
if (!NODE_JS) {
return delayedFunctionScheduler.scheduleFunction(fn, interval, argSlice(arguments, 2), true);
}
var timeout = new FakeTimeout();
delayedFunctionScheduler.scheduleFunction(fn, interval, argSlice(arguments, 2), true, timeout);
return timeout;
}
function clearInterval(id) {
@@ -165,5 +185,18 @@ getJasmineRequireObj().Clock = function() {
}
}
/**
* Mocks Node.js Timeout class
*/
function FakeTimeout() {}
FakeTimeout.prototype.ref = function () {
return this;
};
FakeTimeout.prototype.unref = function () {
return this;
};
return Clock;
};

View File

@@ -1,10 +1,11 @@
getJasmineRequireObj().DelayedFunctionScheduler = function() {
getJasmineRequireObj().DelayedFunctionScheduler = function(j$) {
function DelayedFunctionScheduler() {
var self = this;
var scheduledLookup = [];
var scheduledFunctions = {};
var currentTime = 0;
var delayedFnCount = 0;
var deletedKeys = [];
self.tick = function(millis, tickDate) {
millis = millis || 0;
@@ -51,6 +52,8 @@ getJasmineRequireObj().DelayedFunctionScheduler = function() {
};
self.removeFunctionWithId = function(timeoutKey) {
deletedKeys.push(timeoutKey);
for (var runAtMillis in scheduledFunctions) {
var funcs = scheduledFunctions[runAtMillis];
var i = indexOfFirstToPass(funcs, function (func) {
@@ -127,6 +130,7 @@ getJasmineRequireObj().DelayedFunctionScheduler = function() {
currentTime = newCurrentTime;
var funcsToRun = scheduledFunctions[currentTime];
delete scheduledFunctions[currentTime];
forEachFunction(funcsToRun, function(funcToRun) {
@@ -136,8 +140,13 @@ getJasmineRequireObj().DelayedFunctionScheduler = function() {
});
forEachFunction(funcsToRun, function(funcToRun) {
if (j$.util.arrayContains(deletedKeys, funcToRun.timeoutKey)) {
// skip a timeoutKey deleted whilst we were running
return;
}
funcToRun.funcToCall.apply(null, funcToRun.params || []);
});
deletedKeys = [];
} while (scheduledLookup.length > 0 &&
// checking first if we're out of time prevents setTimeout(0)
// scheduled in a funcToRun from forcing an extra iteration

View File

@@ -37,12 +37,56 @@ getJasmineRequireObj().Env = function(j$) {
return currentSpec || currentSuite();
};
/**
* This represents the available reporter callback for an object passed to {@link Env#addReporter}.
* @interface Reporter
*/
var reporter = new j$.ReportDispatcher([
/**
* `jasmineStarted` is called after all of the specs have been loaded, but just before execution starts.
* @function
* @name Reporter#jasmineStarted
* @param {JasmineStartedInfo} suiteInfo Information about the full Jasmine suite that is being run
*/
'jasmineStarted',
/**
* When the entire suite has finished execution `jasmineDone` is called
* @function
* @name Reporter#jasmineDone
* @param {JasmineDoneInfo} suiteInfo Information about the full Jasmine suite that just finished running.
*/
'jasmineDone',
/**
* `suiteStarted` is invoked when a `describe` starts to run
* @function
* @name Reporter#suiteStarted
* @param {SuiteResult} result Information about the individual {@link describe} being run
*/
'suiteStarted',
/**
* `suiteDone` is invoked when all of the child specs and suites for a given suite have been run
*
* While jasmine doesn't require any specific functions, not defining a `suiteDone` will make it impossible for a reporter to know when a suite has failures in an `afterAll`.
* @function
* @name Reporter#suiteDone
* @param {SuiteResult} result
*/
'suiteDone',
/**
* `specStarted` is invoked when an `it` starts to run (including associated `beforeEach` functions)
* @function
* @name Reporter#specStarted
* @param {SpecResult} result Information about the individual {@link it} being run
*/
'specStarted',
/**
* `specDone` is invoked when an `it` and its associated `beforeEach` and `afterEach` functions have been run.
*
* While jasmine doesn't require any specific functions, not defining a `specDone` will make it impossible for a reporter to know when a spec has failed.
* @function
* @name Reporter#specDone
* @param {SpecResult} result
*/
'specDone'
]);
@@ -196,6 +240,7 @@ getJasmineRequireObj().Env = function(j$) {
options.timeout = {setTimeout: realSetTimeout, clearTimeout: realClearTimeout};
options.fail = self.fail;
options.globalErrors = globalErrors;
options.completeOnFirstError = throwOnExpectationFailure && options.isLeaf;
new j$.QueueRunner(options).execute();
};
@@ -238,6 +283,10 @@ getJasmineRequireObj().Env = function(j$) {
reporter.suiteStarted(suite.result);
},
nodeComplete: function(suite, result) {
if (suite !== currentSuite()) {
throw new Error('Tried to complete the wrong suite');
}
if (!suite.markedPending) {
clearResourcesForRunnable(suite.id);
}
@@ -253,8 +302,15 @@ getJasmineRequireObj().Env = function(j$) {
throw new Error('Invalid order: would cause a beforeAll or afterAll to be run multiple times');
}
/**
* Information passed to the {@link Reporter#jasmineStarted} event.
* @typedef JasmineStartedInfo
* @property {Int} totalSpecsDefined - The total number of specs defined in this suite.
* @property {Order} order - Information about the ordering (random or not) of this execution of the suite.
*/
reporter.jasmineStarted({
totalSpecsDefined: totalSpecsDefined
totalSpecsDefined: totalSpecsDefined,
order: order
});
currentlyExecutingSuites.push(topSuite);
@@ -265,6 +321,12 @@ getJasmineRequireObj().Env = function(j$) {
currentlyExecutingSuites.pop();
globalErrors.uninstall();
/**
* Information passed to the {@link Reporter#jasmineDone} event.
* @typedef JasmineDoneInfo
* @property {Order} order - Information about the ordering (random or not) of this execution of the suite.
* @property {Expectation[]} failedExpectations - List of expectations that failed in an {@link afterAll} at the global level.
*/
reporter.jasmineDone({
order: order,
failedExpectations: topSuite.result.failedExpectations
@@ -276,6 +338,7 @@ getJasmineRequireObj().Env = function(j$) {
* Add a custom reporter to the Jasmine environment.
* @name Env#addReporter
* @function
* @param {Reporter} reporterToAdd The reporter to be added.
* @see custom_reporter
*/
this.addReporter = function(reporterToAdd) {
@@ -315,6 +378,19 @@ getJasmineRequireObj().Env = function(j$) {
}
};
var ensureIsFunctionOrAsync = function(fn, caller) {
if (!j$.isFunction_(fn) && !j$.isAsyncFunction_(fn)) {
throw new Error(caller + ' expects a function argument; received ' + j$.getType_(fn));
}
};
function ensureIsNotNested(method) {
var runnable = currentRunnable();
if (runnable !== null && runnable !== undefined) {
throw new Error('\'' + method + '\' should only be used in \'describe\' function');
}
}
var suiteFactory = function(description) {
var suite = new j$.Suite({
env: self,
@@ -330,6 +406,7 @@ getJasmineRequireObj().Env = function(j$) {
};
this.describe = function(description, specDefinitions) {
ensureIsNotNested('describe');
ensureIsFunction(specDefinitions, 'describe');
var suite = suiteFactory(description);
if (specDefinitions.length > 0) {
@@ -343,6 +420,7 @@ getJasmineRequireObj().Env = function(j$) {
};
this.xdescribe = function(description, specDefinitions) {
ensureIsNotNested('xdescribe');
ensureIsFunction(specDefinitions, 'xdescribe');
var suite = suiteFactory(description);
suite.pend();
@@ -353,6 +431,7 @@ getJasmineRequireObj().Env = function(j$) {
var focusedRunnables = [];
this.fdescribe = function(description, specDefinitions) {
ensureIsNotNested('fdescribe');
ensureIsFunction(specDefinitions, 'fdescribe');
var suite = suiteFactory(description);
suite.isFocused = true;
@@ -450,10 +529,11 @@ getJasmineRequireObj().Env = function(j$) {
};
this.it = function(description, fn, timeout) {
ensureIsNotNested('it');
// it() sometimes doesn't have a fn argument, so only check the type if
// it's given.
if (arguments.length > 1 && typeof fn !== 'undefined') {
ensureIsFunction(fn, 'it');
ensureIsFunctionOrAsync(fn, 'it');
}
var spec = specFactory(description, fn, currentDeclarationSuite, timeout);
if (currentDeclarationSuite.markedPending) {
@@ -464,10 +544,11 @@ getJasmineRequireObj().Env = function(j$) {
};
this.xit = function(description, fn, timeout) {
ensureIsNotNested('xit');
// xit(), like it(), doesn't always have a fn argument, so only check the
// type when needed.
if (arguments.length > 1 && typeof fn !== 'undefined') {
ensureIsFunction(fn, 'xit');
ensureIsFunctionOrAsync(fn, 'xit');
}
var spec = this.it.apply(this, arguments);
spec.pend('Temporarily disabled with xit');
@@ -475,7 +556,8 @@ getJasmineRequireObj().Env = function(j$) {
};
this.fit = function(description, fn, timeout){
ensureIsFunction(fn, 'fit');
ensureIsNotNested('fit');
ensureIsFunctionOrAsync(fn, 'fit');
var spec = specFactory(description, fn, currentDeclarationSuite, timeout);
currentDeclarationSuite.addChild(spec);
focusedRunnables.push(spec.id);
@@ -492,7 +574,8 @@ getJasmineRequireObj().Env = function(j$) {
};
this.beforeEach = function(beforeEachFunction, timeout) {
ensureIsFunction(beforeEachFunction, 'beforeEach');
ensureIsNotNested('beforeEach');
ensureIsFunctionOrAsync(beforeEachFunction, 'beforeEach');
currentDeclarationSuite.beforeEach({
fn: beforeEachFunction,
timeout: function() { return timeout || j$.DEFAULT_TIMEOUT_INTERVAL; }
@@ -500,7 +583,8 @@ getJasmineRequireObj().Env = function(j$) {
};
this.beforeAll = function(beforeAllFunction, timeout) {
ensureIsFunction(beforeAllFunction, 'beforeAll');
ensureIsNotNested('beforeAll');
ensureIsFunctionOrAsync(beforeAllFunction, 'beforeAll');
currentDeclarationSuite.beforeAll({
fn: beforeAllFunction,
timeout: function() { return timeout || j$.DEFAULT_TIMEOUT_INTERVAL; }
@@ -508,7 +592,9 @@ getJasmineRequireObj().Env = function(j$) {
};
this.afterEach = function(afterEachFunction, timeout) {
ensureIsFunction(afterEachFunction, 'afterEach');
ensureIsNotNested('afterEach');
ensureIsFunctionOrAsync(afterEachFunction, 'afterEach');
afterEachFunction.isCleanup = true;
currentDeclarationSuite.afterEach({
fn: afterEachFunction,
timeout: function() { return timeout || j$.DEFAULT_TIMEOUT_INTERVAL; }
@@ -516,7 +602,8 @@ getJasmineRequireObj().Env = function(j$) {
};
this.afterAll = function(afterAllFunction, timeout) {
ensureIsFunction(afterAllFunction, 'afterAll');
ensureIsNotNested('afterAll');
ensureIsFunctionOrAsync(afterAllFunction, 'afterAll');
currentDeclarationSuite.afterAll({
fn: afterAllFunction,
timeout: function() { return timeout || j$.DEFAULT_TIMEOUT_INTERVAL; }
@@ -557,6 +644,10 @@ getJasmineRequireObj().Env = function(j$) {
message: message,
error: error && error.message ? error : null
});
if (self.throwingExpectationFailures()) {
throw new Error(message);
}
};
}

View File

@@ -4,6 +4,15 @@ getJasmineRequireObj().buildExpectationResult = function() {
var messageFormatter = options.messageFormatter || function() {},
stackFormatter = options.stackFormatter || function() {};
/**
* @typedef Expectation
* @property {String} matcherName - The name of the matcher that was executed for this expectation.
* @property {String} message - The failure message for the expectation.
* @property {String} stack - The stack trace for the failure if available.
* @property {Boolean} passed - Whether the expectation passed or failed.
* @property {Object} expected - If the expectation failed, what was the expected value.
* @property {Object} actual - If the expectation failed, what actual value was produced.
*/
var result = {
matcherName: options.matcherName,
message: message(),

View File

@@ -5,7 +5,12 @@ getJasmineRequireObj().GlobalErrors = function(j$) {
var onerror = function onerror() {
var handler = handlers[handlers.length - 1];
handler.apply(null, Array.prototype.slice.call(arguments, 0));
if (handler) {
handler.apply(null, Array.prototype.slice.call(arguments, 0));
} else {
throw arguments[0];
}
};
this.uninstall = function noop() {};

View File

@@ -6,11 +6,10 @@ getJasmineRequireObj().JsApiReporter = function() {
};
/**
* _Note:_ Do not construct this directly, use the global `jsApiReporter` to retrieve the instantiated object.
*
* @name jsApiReporter
* @classdesc Reporter added by default in `boot.js` to record results for retrieval in javascript code.
* @classdesc {@link Reporter} added by default in `boot.js` to record results for retrieval in javascript code. An instance is made available as `jsApiReporter` on the global object.
* @class
* @hideconstructor
*/
function JsApiReporter(options) {
var timer = options.timer || noopTimer,
@@ -64,7 +63,7 @@ getJasmineRequireObj().JsApiReporter = function() {
* @function
* @param {Number} index - The position in the suites list to start from.
* @param {Number} length - Maximum number of suite results to return.
* @return {Object[]}
* @return {SuiteResult[]}
*/
this.suiteResults = function(index, length) {
return suites.slice(index, index + length);
@@ -79,7 +78,7 @@ getJasmineRequireObj().JsApiReporter = function() {
* Get all of the suites in a single object, with their `id` as the key.
* @name jsApiReporter#suites
* @function
* @return {Object}
* @return {Object} - Map of suite id to {@link SuiteResult}
*/
this.suites = function() {
return suites_hash;
@@ -99,7 +98,7 @@ getJasmineRequireObj().JsApiReporter = function() {
* @function
* @param {Number} index - The position in the specs list to start from.
* @param {Number} length - Maximum number of specs results to return.
* @return {Object[]}
* @return {SpecResult[]}
*/
this.specResults = function(index, length) {
return specs.slice(index, index + length);
@@ -109,7 +108,7 @@ getJasmineRequireObj().JsApiReporter = function() {
* Get all spec results.
* @name jsApiReporter#specs
* @function
* @return {Object[]}
* @return {SpecResult[]}
*/
this.specs = function() {
return specs;

View File

@@ -3,12 +3,14 @@ getJasmineRequireObj().pp = function(j$) {
function PrettyPrinter() {
this.ppNestLevel_ = 0;
this.seen = [];
this.length = 0;
this.stringParts = [];
}
function hasCustomToString(value) {
// value.toString !== Object.prototype.toString if value has no custom toString but is from another context (e.g.
// iframe, web worker)
return value.toString !== Object.prototype.toString && (value.toString() !== Object.prototype.toString.call(value));
return j$.isFunction_(value.toString) && value.toString !== Object.prototype.toString && (value.toString() !== Object.prototype.toString.call(value));
}
PrettyPrinter.prototype.format = function(value) {
@@ -36,8 +38,12 @@ getJasmineRequireObj().pp = function(j$) {
this.emitScalar('HTMLNode');
} else if (value instanceof Date) {
this.emitScalar('Date(' + value + ')');
} else if (value.toString && value.toString() == '[object Set]') {
} else if (j$.isSet(value)) {
this.emitSet(value);
} else if (j$.isMap(value)) {
this.emitMap(value);
} else if (j$.isTypedArray_(value)) {
this.emitTypedArray(value);
} else if (value.toString && typeof value === 'object' && !j$.isArray_(value) && hasCustomToString(value)) {
this.emitScalar(value.toString());
} else if (j$.util.arrayContains(this.seen, value)) {
@@ -53,42 +59,44 @@ getJasmineRequireObj().pp = function(j$) {
} else {
this.emitScalar(value.toString());
}
} catch (e) {
if (this.ppNestLevel_ > 1 || !(e instanceof MaxCharsReachedError)) {
throw e;
}
} finally {
this.ppNestLevel_--;
}
};
PrettyPrinter.prototype.iterateObject = function(obj, fn) {
for (var property in obj) {
if (!Object.prototype.hasOwnProperty.call(obj, property)) { continue; }
fn(property, obj.__lookupGetter__ ? (!j$.util.isUndefined(obj.__lookupGetter__(property)) &&
obj.__lookupGetter__(property) !== null) : false);
var objKeys = keys(obj, j$.isArray_(obj));
var isGetter = function isGetter(prop) {};
if (obj.__lookupGetter__) {
isGetter = function isGetter(prop) {
var getter = obj.__lookupGetter__(prop);
return !j$.util.isUndefined(getter) && getter !== null;
};
}
var length = Math.min(objKeys.length, j$.MAX_PRETTY_PRINT_ARRAY_LENGTH);
for (var i = 0; i < length; i++) {
var property = objKeys[i];
fn(property, isGetter(property));
}
return objKeys.length > length;
};
PrettyPrinter.prototype.emitArray = j$.unimplementedMethod_;
PrettyPrinter.prototype.emitSet = j$.unimplementedMethod_;
PrettyPrinter.prototype.emitObject = j$.unimplementedMethod_;
PrettyPrinter.prototype.emitScalar = j$.unimplementedMethod_;
PrettyPrinter.prototype.emitString = j$.unimplementedMethod_;
function StringPrettyPrinter() {
PrettyPrinter.call(this);
this.string = '';
}
j$.util.inherit(StringPrettyPrinter, PrettyPrinter);
StringPrettyPrinter.prototype.emitScalar = function(value) {
PrettyPrinter.prototype.emitScalar = function(value) {
this.append(value);
};
StringPrettyPrinter.prototype.emitString = function(value) {
PrettyPrinter.prototype.emitString = function(value) {
this.append('\'' + value + '\'');
};
StringPrettyPrinter.prototype.emitArray = function(array) {
PrettyPrinter.prototype.emitArray = function(array) {
if (this.ppNestLevel_ > j$.MAX_PRETTY_PRINT_DEPTH) {
this.append('Array');
return;
@@ -107,11 +115,7 @@ getJasmineRequireObj().pp = function(j$) {
var self = this;
var first = array.length === 0;
this.iterateObject(array, function(property, isGetter) {
if (property.match(/^\d+$/)) {
return;
}
var truncated = this.iterateObject(array, function(property, isGetter) {
if (first) {
first = false;
} else {
@@ -121,30 +125,62 @@ getJasmineRequireObj().pp = function(j$) {
self.formatProperty(array, property, isGetter);
});
if (truncated) { this.append(', ...'); }
this.append(' ]');
};
StringPrettyPrinter.prototype.emitSet = function(set) {
PrettyPrinter.prototype.emitSet = function(set) {
if (this.ppNestLevel_ > j$.MAX_PRETTY_PRINT_DEPTH) {
this.append('Set');
return;
}
this.append('Set( ');
var size = Math.min(set.size, j$.MAX_PRETTY_PRINT_ARRAY_LENGTH);
var iter = set.values();
for (var i = 0; i < size; i++) {
var i = 0;
set.forEach( function( value, key ) {
if (i >= size) {
return;
}
if (i > 0) {
this.append(', ');
}
this.format(iter.next().value);
}
this.format(value);
i++;
}, this );
if (set.size > size){
this.append(', ...');
}
this.append(' )');
};
StringPrettyPrinter.prototype.emitObject = function(obj) {
PrettyPrinter.prototype.emitMap = function(map) {
if (this.ppNestLevel_ > j$.MAX_PRETTY_PRINT_DEPTH) {
this.append('Map');
return;
}
this.append('Map( ');
var size = Math.min(map.size, j$.MAX_PRETTY_PRINT_ARRAY_LENGTH);
var i = 0;
map.forEach( function( value, key ) {
if (i >= size) {
return;
}
if (i > 0) {
this.append(', ');
}
this.format([key,value]);
i++;
}, this );
if (map.size > size){
this.append(', ...');
}
this.append(' )');
};
PrettyPrinter.prototype.emitObject = function(obj) {
var ctor = obj.constructor,
constructorName;
@@ -162,7 +198,7 @@ getJasmineRequireObj().pp = function(j$) {
this.append('({ ');
var first = true;
this.iterateObject(obj, function(property, isGetter) {
var truncated = this.iterateObject(obj, function(property, isGetter) {
if (first) {
first = false;
} else {
@@ -172,10 +208,24 @@ getJasmineRequireObj().pp = function(j$) {
self.formatProperty(obj, property, isGetter);
});
if (truncated) { this.append(', ...'); }
this.append(' })');
};
StringPrettyPrinter.prototype.formatProperty = function(obj, property, isGetter) {
PrettyPrinter.prototype.emitTypedArray = function(arr) {
var constructorName = j$.fnNameFor(arr.constructor),
limitedArray = Array.prototype.slice.call(arr, 0, j$.MAX_PRETTY_PRINT_ARRAY_LENGTH),
itemsString = Array.prototype.join.call(limitedArray, ', ');
if (limitedArray.length !== arr.length) {
itemsString += ', ...';
}
this.append(constructorName + ' [ ' + itemsString + ' ]');
};
PrettyPrinter.prototype.formatProperty = function(obj, property, isGetter) {
this.append(property);
this.append(': ');
if (isGetter) {
@@ -185,13 +235,65 @@ getJasmineRequireObj().pp = function(j$) {
}
};
StringPrettyPrinter.prototype.append = function(value) {
this.string += value;
PrettyPrinter.prototype.append = function(value) {
var result = truncate(value, j$.MAX_PRETTY_PRINT_CHARS - this.length);
this.length += result.value.length;
this.stringParts.push(result.value);
if (result.truncated) {
throw new MaxCharsReachedError();
}
};
function truncate(s, maxlen) {
if (s.length <= maxlen) {
return { value: s, truncated: false };
}
s = s.substring(0, maxlen - 4) + ' ...';
return { value: s, truncated: true };
}
function MaxCharsReachedError() {
this.message = 'Exceeded ' + j$.MAX_PRETTY_PRINT_CHARS +
' characters while pretty-printing a value';
}
MaxCharsReachedError.prototype = new Error();
function keys(obj, isArray) {
var allKeys = Object.keys ? Object.keys(obj) :
(function(o) {
var keys = [];
for (var key in o) {
if (j$.util.has(o, key)) {
keys.push(key);
}
}
return keys;
})(obj);
if (!isArray) {
return allKeys;
}
if (allKeys.length === 0) {
return allKeys;
}
var extraKeys = [];
for (var i = 0; i < allKeys.length; i++) {
if (!/^[0-9]+$/.test(allKeys[i])) {
extraKeys.push(allKeys[i]);
}
}
return extraKeys;
}
return function(value) {
var stringPrettyPrinter = new StringPrettyPrinter();
stringPrettyPrinter.format(value);
return stringPrettyPrinter.string;
var prettyPrinter = new PrettyPrinter();
prettyPrinter.format(value);
return prettyPrinter.stringParts.join('');
};
};

View File

@@ -12,95 +12,153 @@ getJasmineRequireObj().QueueRunner = function(j$) {
}
function QueueRunner(attrs) {
this.queueableFns = attrs.queueableFns || [];
var queueableFns = attrs.queueableFns || [];
this.queueableFns = queueableFns.concat(attrs.cleanupFns || []);
this.firstCleanupIx = queueableFns.length;
this.onComplete = attrs.onComplete || function() {};
this.clearStack = attrs.clearStack || function(fn) {fn();};
this.onException = attrs.onException || function() {};
this.catchException = attrs.catchException || function() { return true; };
this.userContext = attrs.userContext || {};
this.userContext = attrs.userContext || new j$.UserContext();
this.timeout = attrs.timeout || {setTimeout: setTimeout, clearTimeout: clearTimeout};
this.fail = attrs.fail || function() {};
this.globalErrors = attrs.globalErrors || { pushListener: function() {}, popListener: function() {} };
this.completeOnFirstError = !!attrs.completeOnFirstError;
}
QueueRunner.prototype.execute = function() {
this.run(this.queueableFns, 0);
var self = this;
this.handleFinalError = function(error) {
self.onException(error);
};
this.globalErrors.pushListener(this.handleFinalError);
this.run(0);
};
QueueRunner.prototype.run = function(queueableFns, recursiveIndex) {
var length = queueableFns.length,
QueueRunner.prototype.skipToCleanup = function(lastRanIndex) {
if (lastRanIndex < this.firstCleanupIx) {
this.run(this.firstCleanupIx);
} else {
this.run(lastRanIndex + 1);
}
};
QueueRunner.prototype.run = function(recursiveIndex) {
var length = this.queueableFns.length,
self = this,
iterativeIndex;
for(iterativeIndex = recursiveIndex; iterativeIndex < length; iterativeIndex++) {
var queueableFn = queueableFns[iterativeIndex];
if (queueableFn.fn.length > 0) {
attemptAsync(queueableFn);
var result = attempt(iterativeIndex);
if (!result.completedSynchronously) {
return;
}
if (this.completeOnFirstError && result.errored) {
this.skipToCleanup(iterativeIndex);
return;
} else {
attemptSync(queueableFn);
}
}
this.clearStack(this.onComplete);
this.clearStack(function() {
self.globalErrors.popListener(self.handleFinalError);
self.onComplete();
});
function attemptSync(queueableFn) {
try {
queueableFn.fn.call(self.userContext);
} catch (e) {
handleException(e, queueableFn);
}
}
function attemptAsync(queueableFn) {
function attempt() {
var clearTimeout = function () {
Function.prototype.apply.apply(self.timeout.clearTimeout, [j$.getGlobal(), [timeoutId]]);
},
setTimeout = function(delayedFn, delay) {
return Function.prototype.apply.apply(self.timeout.setTimeout, [j$.getGlobal(), [delayedFn, delay]]);
},
completedSynchronously = true,
handleError = function(error) {
onException(error);
next();
},
next = once(function () {
cleanup = once(function() {
clearTimeout(timeoutId);
self.globalErrors.popListener(handleError);
self.run(queueableFns, iterativeIndex + 1);
}),
next = once(function () {
cleanup();
function runNext() {
if (self.completeOnFirstError && errored) {
self.skipToCleanup(iterativeIndex);
} else {
self.run(iterativeIndex + 1);
}
}
if (completedSynchronously) {
setTimeout(runNext);
} else {
runNext();
}
}),
errored = false,
queueableFn = self.queueableFns[iterativeIndex],
timeoutId;
next.fail = function() {
self.fail.apply(null, arguments);
errored = true;
next();
};
self.globalErrors.pushListener(handleError);
if (queueableFn.timeout) {
timeoutId = Function.prototype.apply.apply(self.timeout.setTimeout, [j$.getGlobal(), [function() {
timeoutId = setTimeout(function() {
var error = new Error('Timeout - Async callback was not invoked within timeout specified by jasmine.DEFAULT_TIMEOUT_INTERVAL.');
onException(error);
next();
}, queueableFn.timeout()]]);
}, queueableFn.timeout());
}
try {
queueableFn.fn.call(self.userContext, next);
if (queueableFn.fn.length === 0) {
var maybeThenable = queueableFn.fn.call(self.userContext);
if (maybeThenable && j$.isFunction_(maybeThenable.then)) {
maybeThenable.then(next, onPromiseRejection);
completedSynchronously = false;
return { completedSynchronously: false };
}
} else {
queueableFn.fn.call(self.userContext, next);
completedSynchronously = false;
return { completedSynchronously: false };
}
} catch (e) {
handleException(e, queueableFn);
errored = true;
}
cleanup();
return { completedSynchronously: true, errored: errored };
function onException(e) {
self.onException(e);
errored = true;
}
function onPromiseRejection(e) {
onException(e);
next();
}
}
function onException(e) {
self.onException(e);
}
function handleException(e, queueableFn) {
onException(e);
if (!self.catchException(e)) {
//TODO: set a var when we catch an exception and
//use a finally block to close the loop in a nice way..
throw e;
function handleException(e, queueableFn) {
onException(e);
if (!self.catchException(e)) {
//TODO: set a var when we catch an exception and
//use a finally block to close the loop in a nice way..
throw e;
}
}
}
};

View File

@@ -1,4 +1,4 @@
getJasmineRequireObj().ReportDispatcher = function() {
getJasmineRequireObj().ReportDispatcher = function(j$) {
function ReportDispatcher(methods) {
var dispatchedMethods = methods || [];
@@ -36,7 +36,7 @@ getJasmineRequireObj().ReportDispatcher = function() {
for (var i = 0; i < reporters.length; i++) {
var reporter = reporters[i];
if (reporter[method]) {
reporter[method].apply(reporter, args);
reporter[method].apply(reporter, j$.util.cloneArgs(args));
}
}
}

View File

@@ -18,6 +18,16 @@ getJasmineRequireObj().Spec = function(j$) {
this.pend();
}
/**
* @typedef SpecResult
* @property {Int} id - The unique id of this spec.
* @property {String} description - The description passed to the {@link it} that created this spec.
* @property {String} fullName - The full description including all ancestors of this spec.
* @property {Expectation[]} failedExpectations - The list of expectations that failed during execution of this spec.
* @property {Expectation[]} passedExpectations - The list of expectations that passed during execution of this spec.
* @property {String} pendingReason - If the spec is {@link pending}, this will be the reason.
* @property {String} status - Once the spec has completed, this string represents the pass/fail status of this spec.
*/
this.result = {
id: this.id,
description: this.description,
@@ -50,20 +60,25 @@ getJasmineRequireObj().Spec = function(j$) {
this.onStart(this);
if (!this.isExecutable() || this.markedPending || enabled === false) {
complete(enabled);
return;
}
var fns = this.beforeAndAfterFns();
var allFns = fns.befores.concat(this.queueableFn).concat(fns.afters);
var regularFns = fns.befores.concat(this.queueableFn);
this.queueRunnerFactory({
queueableFns: allFns,
var runnerConfig = {
isLeaf: true,
queueableFns: regularFns,
cleanupFns: fns.afters,
onException: function() { self.onException.apply(self, arguments); },
onComplete: complete,
userContext: this.userContext()
});
};
if (!this.isExecutable() || this.markedPending || enabled === false) {
runnerConfig.queueableFns = [];
runnerConfig.cleanupFns = [];
runnerConfig.onComplete = function() { complete(enabled); };
}
this.queueRunnerFactory(runnerConfig);
function complete(enabledAgain) {
self.result.status = self.status(enabledAgain);

View File

@@ -4,6 +4,7 @@ getJasmineRequireObj().SpyRegistry = function(j$) {
function SpyRegistry(options) {
options = options || {};
var global = options.global || j$.getGlobal();
var currentSpies = options.currentSpies || function() { return []; };
this.allowRespy = function(allow){
@@ -47,7 +48,7 @@ getJasmineRequireObj().SpyRegistry = function(j$) {
spiedMethod = j$.createSpy(methodName, originalMethod),
restoreStrategy;
if (Object.prototype.hasOwnProperty.call(obj, methodName)) {
if (Object.prototype.hasOwnProperty.call(obj, methodName) || (obj === global && methodName === 'onerror')) {
restoreStrategy = function() {
obj[methodName] = originalMethod;
};

View File

@@ -88,7 +88,7 @@ getJasmineRequireObj().SpyStrategy = function(j$) {
* @param {Function} fn The function to invoke with the passed parameters.
*/
this.callFake = function(fn) {
if(!j$.isFunction_(fn)) {
if(!(j$.isFunction_(fn) || j$.isAsyncFunction_(fn))) {
throw new Error('Argument passed to callFake should be a function, got ' + fn);
}
plan = fn;

View File

@@ -15,6 +15,14 @@ getJasmineRequireObj().Suite = function(j$) {
this.children = [];
/**
* @typedef SuiteResult
* @property {Int} id - The unique id of this suite.
* @property {String} description - The description text passed to the {@link describe} that made this suite.
* @property {String} fullName - The full description including all ancestors of this suite.
* @property {Expectation[]} failedExpectations - The list of expectations that failed in an {@link afterAll} for this suite.
* @property {String} status - Once the suite has completed, this string represents the pass/fail status of this suite.
*/
this.result = {
id: this.id,
description: this.description,
@@ -88,14 +96,14 @@ getJasmineRequireObj().Suite = function(j$) {
Suite.prototype.sharedUserContext = function() {
if (!this.sharedContext) {
this.sharedContext = this.parentSuite ? clone(this.parentSuite.sharedUserContext()) : {};
this.sharedContext = this.parentSuite ? this.parentSuite.clonedSharedUserContext() : new j$.UserContext();
}
return this.sharedContext;
};
Suite.prototype.clonedSharedUserContext = function() {
return clone(this.sharedUserContext());
return j$.UserContext.fromExisting(this.sharedUserContext());
};
Suite.prototype.onException = function() {
@@ -147,17 +155,6 @@ getJasmineRequireObj().Suite = function(j$) {
return !args[0];
}
function clone(obj) {
var clonedObj = {};
for (var prop in obj) {
if (obj.hasOwnProperty(prop)) {
clonedObj[prop] = obj[prop];
}
}
return clonedObj;
}
return Suite;
};

18
src/core/UserContext.js Normal file
View File

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

View File

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

View File

@@ -4,8 +4,9 @@ getJasmineRequireObj().ArrayContaining = function(j$) {
}
ArrayContaining.prototype.asymmetricMatch = function(other, customTesters) {
var className = Object.prototype.toString.call(this.sample);
if (className !== '[object Array]') { throw new Error('You must provide an array to arrayContaining, not \'' + this.sample + '\'.'); }
if (!j$.isArray_(this.sample)) {
throw new Error('You must provide an array to arrayContaining, not ' + j$.pp(this.sample) + '.');
}
for (var i = 0; i < this.sample.length; i++) {
var item = this.sample[i];

View File

@@ -0,0 +1,31 @@
getJasmineRequireObj().ArrayWithExactContents = function(j$) {
function ArrayWithExactContents(sample) {
this.sample = sample;
}
ArrayWithExactContents.prototype.asymmetricMatch = function(other, customTesters) {
if (!j$.isArray_(this.sample)) {
throw new Error('You must provide an array to arrayWithExactContents, not ' + j$.pp(this.sample) + '.');
}
if (this.sample.length !== other.length) {
return false;
}
for (var i = 0; i < this.sample.length; i++) {
var item = this.sample[i];
if (!j$.matchersUtil.contains(other, item, customTesters)) {
return false;
}
}
return true;
};
ArrayWithExactContents.prototype.jasmineToString = function() {
return '<jasmine.arrayWithExactContents ' + j$.pp(this.sample) + '>';
};
return ArrayWithExactContents;
};

View File

@@ -8,13 +8,20 @@ getJasmineRequireObj().base = function(j$, jasmineGlobal) {
* Set this to a lower value to speed up pretty printing if you have large objects.
* @name jasmine.MAX_PRETTY_PRINT_DEPTH
*/
j$.MAX_PRETTY_PRINT_DEPTH = 40;
j$.MAX_PRETTY_PRINT_DEPTH = 8;
/**
* Maximum number of array elements to display when pretty printing objects.
* This will also limit the number of keys and values displayed for an object.
* Elements past this number will be ellipised.
* @name jasmine.MAX_PRETTY_PRINT_ARRAY_LENGTH
*/
j$.MAX_PRETTY_PRINT_ARRAY_LENGTH = 100;
j$.MAX_PRETTY_PRINT_ARRAY_LENGTH = 50;
/**
* Maximum number of charasters to display when pretty printing objects.
* Characters past this number will be ellipised.
* @name jasmine.MAX_PRETTY_PRINT_CHARS
*/
j$.MAX_PRETTY_PRINT_CHARS = 1000;
/**
* Default number of milliseconds Jasmine will wait for an asynchronous spec to complete.
* @name jasmine.DEFAULT_TIMEOUT_INTERVAL
@@ -58,6 +65,22 @@ getJasmineRequireObj().base = function(j$, jasmineGlobal) {
return j$.isA_('Function', value);
};
j$.isAsyncFunction_ = function(value) {
return j$.isA_('AsyncFunction', value);
};
j$.isTypedArray_ = function(value) {
return j$.isA_('Float32Array', value) ||
j$.isA_('Float64Array', value) ||
j$.isA_('Int16Array', value) ||
j$.isA_('Int32Array', value) ||
j$.isA_('Int8Array', value) ||
j$.isA_('Uint16Array', value) ||
j$.isA_('Uint32Array', value) ||
j$.isA_('Uint8Array', value) ||
j$.isA_('Uint8ClampedArray', value);
};
j$.isA_ = function(typeName, value) {
return j$.getType_(value) === '[object ' + typeName + ']';
};
@@ -70,12 +93,26 @@ getJasmineRequireObj().base = function(j$, jasmineGlobal) {
return obj.nodeType > 0;
};
j$.isMap = function(obj) {
return typeof jasmineGlobal.Map !== 'undefined' && obj.constructor === jasmineGlobal.Map;
};
j$.isSet = function(obj) {
return typeof jasmineGlobal.Set !== 'undefined' && obj.constructor === jasmineGlobal.Set;
};
j$.isPromise = function(obj) {
return typeof jasmineGlobal.Promise !== 'undefined' && obj.constructor === jasmineGlobal.Promise;
};
j$.fnNameFor = function(func) {
if (func.name) {
return func.name;
}
var matches = func.toString().match(/^\s*function\s*(\w*)\s*\(/);
var matches = func.toString().match(/^\s*function\s*(\w*)\s*\(/) ||
func.toString().match(/^\s*\[object\s*(\w*)Constructor\]/);
return matches ? matches[1] : '<anonymous>';
};
@@ -133,6 +170,17 @@ getJasmineRequireObj().base = function(j$, jasmineGlobal) {
return new j$.ArrayContaining(sample);
};
/**
* Get a matcher, usable in any {@link matchers|matcher} that uses Jasmine's equality (e.g. {@link matchers#toEqual|toEqual}, {@link matchers#toContain|toContain}, or {@link matchers#toHaveBeenCalledWith|toHaveBeenCalledWith}),
* that will succeed if the actual value is an `Array` that contains all of the elements in the sample in any order.
* @name jasmine.arrayWithExactContents
* @function
* @param {Array} sample
*/
j$.arrayWithExactContents = function(sample) {
return new j$.ArrayWithExactContents(sample);
};
/**
* Create a bare {@link Spy} object. This won't be installed anywhere and will not have any implementation behind it.
* @name jasmine.createSpy

View File

@@ -66,13 +66,17 @@ getJasmineRequireObj().matchersUtil = function(j$) {
if (asymmetricA) {
result = a.asymmetricMatch(b, customTesters);
diffBuilder.record(a, b);
if (!result) {
diffBuilder.record(a, b);
}
return result;
}
if (asymmetricB) {
result = b.asymmetricMatch(a, customTesters);
diffBuilder.record(a, b);
if (!result) {
diffBuilder.record(a, b);
}
return result;
}
}
@@ -210,6 +214,12 @@ getJasmineRequireObj().matchersUtil = function(j$) {
return false;
}
var aIsPromise = j$.isPromise(a);
var bIsPromise = j$.isPromise(b);
if (aIsPromise && bIsPromise) {
return a === b;
}
// Assume equality for cyclic structures. The algorithm for detecting cyclic
// structures is adapted from ES 5.1 section 15.12.3, abstract operation `JO`.
var length = aStack.length;
@@ -225,35 +235,121 @@ getJasmineRequireObj().matchersUtil = function(j$) {
// Recursively compare objects and arrays.
// Compare array lengths to determine if a deep comparison is necessary.
if (className == '[object Array]') {
size = a.length;
if (size !== b.length) {
diffBuilder.record(a, b);
return false;
}
var aLength = a.length;
var bLength = b.length;
for (i = 0; i < size; i++) {
diffBuilder.withPath('length', function() {
if (aLength !== bLength) {
diffBuilder.record(aLength, bLength);
result = false;
}
});
for (i = 0; i < aLength || i < bLength; i++) {
diffBuilder.withPath(i, function() {
result = eq(a[i], b[i], aStack, bStack, customTesters, diffBuilder) && result;
result = eq(i < aLength ? a[i] : void 0, i < bLength ? b[i] : void 0, aStack, bStack, customTesters, diffBuilder) && result;
});
}
if (!result) {
return false;
}
} else if (className == '[object Set]') {
} else if (j$.isMap(a) && j$.isMap(b)) {
if (a.size != b.size) {
diffBuilder.record(a, b);
return false;
}
var iterA = a.values(), iterB = b.values();
var valA, valB;
do {
valA = iterA.next();
valB = iterB.next();
if (!eq(valA.value, valB.value, aStack, bStack, customTesters, j$.NullDiffBuilder())) {
diffBuilder.record(a, b);
return false;
var keysA = [];
var keysB = [];
a.forEach( function( valueA, keyA ) {
keysA.push( keyA );
});
b.forEach( function( valueB, keyB ) {
keysB.push( keyB );
});
// For both sets of keys, check they map to equal values in both maps.
// Keep track of corresponding keys (in insertion order) in order to handle asymmetric obj keys.
var mapKeys = [keysA, keysB];
var cmpKeys = [keysB, keysA];
var mapIter, mapKey, mapValueA, mapValueB;
var cmpIter, cmpKey;
for (i = 0; result && i < mapKeys.length; i++) {
mapIter = mapKeys[i];
cmpIter = cmpKeys[i];
for (var j = 0; result && j < mapIter.length; j++) {
mapKey = mapIter[j];
cmpKey = cmpIter[j];
mapValueA = a.get(mapKey);
// Only use the cmpKey when one of the keys is asymmetric and the corresponding key matches,
// otherwise explicitly look up the mapKey in the other Map since we want keys with unique
// obj identity (that are otherwise equal) to not match.
if (isAsymmetric(mapKey) || isAsymmetric(cmpKey) &&
eq(mapKey, cmpKey, aStack, bStack, customTesters, j$.NullDiffBuilder())) {
mapValueB = b.get(cmpKey);
} else {
mapValueB = b.get(mapKey);
}
result = eq(mapValueA, mapValueB, aStack, bStack, customTesters, j$.NullDiffBuilder());
}
} while (!valA.done && !valB.done);
}
if (!result) {
diffBuilder.record(a, b);
return false;
}
} else if (j$.isSet(a) && j$.isSet(b)) {
if (a.size != b.size) {
diffBuilder.record(a, b);
return false;
}
var valuesA = [];
a.forEach( function( valueA ) {
valuesA.push( valueA );
});
var valuesB = [];
b.forEach( function( valueB ) {
valuesB.push( valueB );
});
// For both sets, check they are all contained in the other set
var setPairs = [[valuesA, valuesB], [valuesB, valuesA]];
var stackPairs = [[aStack, bStack], [bStack, aStack]];
var baseValues, baseValue, baseStack;
var otherValues, otherValue, otherStack;
var found;
var prevStackSize;
for (i = 0; result && i < setPairs.length; i++) {
baseValues = setPairs[i][0];
otherValues = setPairs[i][1];
baseStack = stackPairs[i][0];
otherStack = stackPairs[i][1];
// For each value in the base set...
for (var k = 0; result && k < baseValues.length; k++) {
baseValue = baseValues[k];
found = false;
// ... test that it is present in the other set
for (var l = 0; !found && l < otherValues.length; l++) {
otherValue = otherValues[l];
prevStackSize = baseStack.length;
// compare by value equality
found = eq(baseValue, otherValue, baseStack, otherStack, customTesters, j$.NullDiffBuilder());
if (!found && prevStackSize !== baseStack.length) {
baseStack.splice(prevStackSize);
otherStack.splice(prevStackSize);
}
}
result = result && found;
}
}
if (!result) {
diffBuilder.record(a, b);
return false;
}
} else {
// Objects with different constructors are not equivalent, but `Object`s

View File

@@ -0,0 +1,20 @@
getJasmineRequireObj().nothing = function() {
/**
* {@link expect} nothing explicitly.
* @function
* @name matchers#nothing
* @example
* expect().nothing();
*/
function nothing() {
return {
compare: function() {
return {
pass: true
};
}
};
}
return nothing;
};

View File

@@ -1,5 +1,6 @@
getJasmineRequireObj().requireMatchers = function(jRequire, j$) {
var availableMatchers = [
'nothing',
'toBe',
'toBeCloseTo',
'toBeDefined',

View File

@@ -15,8 +15,18 @@ getJasmineRequireObj().toBeCloseTo = function() {
precision = precision || 2;
}
if (expected === null || actual === null) {
throw new Error('Cannot use toBeCloseTo with null. Arguments evaluated to: ' +
'expect(' + actual + ').toBeCloseTo(' + expected + ').'
);
}
var pow = Math.pow(10, precision + 1);
var delta = Math.abs(expected - actual);
var maxDelta = Math.pow(10, -precision) / 2;
return {
pass: Math.abs(expected - actual) < (Math.pow(10, -precision) / 2)
pass: Math.round(delta * pow) / pow <= maxDelta
};
}
};

View File

@@ -12,7 +12,7 @@ var getJasmineRequireObj = (function (jasmineGlobal) {
if (typeof window !== 'undefined' && typeof window.toString === 'function' && window.toString() === '[object GjsGlobal]') {
jasmineGlobal = window;
}
jasmineRequire = jasmineGlobal.jasmineRequire = jasmineGlobal.jasmineRequire || {};
jasmineRequire = jasmineGlobal.jasmineRequire = {};
}
function getJasmineRequire() {
@@ -23,7 +23,7 @@ var getJasmineRequireObj = (function (jasmineGlobal) {
var j$ = {};
jRequire.base(j$, jasmineGlobal);
j$.util = jRequire.util();
j$.util = jRequire.util(j$);
j$.errors = jRequire.errors();
j$.formatErrorMsg = jRequire.formatErrorMsg();
j$.Any = jRequire.Any(j$);
@@ -32,7 +32,7 @@ var getJasmineRequireObj = (function (jasmineGlobal) {
j$.MockDate = jRequire.MockDate();
j$.getClearStack = jRequire.clearStack(j$);
j$.Clock = jRequire.Clock();
j$.DelayedFunctionScheduler = jRequire.DelayedFunctionScheduler();
j$.DelayedFunctionScheduler = jRequire.DelayedFunctionScheduler(j$);
j$.Env = jRequire.Env(j$);
j$.ExceptionFormatter = jRequire.ExceptionFormatter();
j$.Expectation = jRequire.Expectation();
@@ -41,14 +41,16 @@ var getJasmineRequireObj = (function (jasmineGlobal) {
j$.matchersUtil = jRequire.matchersUtil(j$);
j$.ObjectContaining = jRequire.ObjectContaining(j$);
j$.ArrayContaining = jRequire.ArrayContaining(j$);
j$.ArrayWithExactContents = jRequire.ArrayWithExactContents(j$);
j$.pp = jRequire.pp(j$);
j$.QueueRunner = jRequire.QueueRunner(j$);
j$.ReportDispatcher = jRequire.ReportDispatcher();
j$.ReportDispatcher = jRequire.ReportDispatcher(j$);
j$.Spec = jRequire.Spec(j$);
j$.Spy = jRequire.Spy(j$);
j$.SpyRegistry = jRequire.SpyRegistry(j$);
j$.SpyStrategy = jRequire.SpyStrategy(j$);
j$.StringMatching = jRequire.StringMatching(j$);
j$.UserContext = jRequire.UserContext(j$);
j$.Suite = jRequire.Suite(j$);
j$.Timer = jRequire.Timer();
j$.TreeProcessor = jRequire.TreeProcessor();

View File

@@ -1,5 +1,15 @@
getJasmineRequireObj().interface = function(jasmine, env) {
var jasmineInterface = {
/**
* Callback passed to parts of the Jasmine base interface.
*
* By default Jasmine assumes this function completes synchronously.
* If you have code that you need to test asynchronously, you can declare that you receive a `done` callback, return a Promise, or use the `async` keyword if it is supported in your environment.
* @callback implementationCallback
* @param {Function} [done] Used to specify to Jasmine that this callback is asynchronous and Jasmine should wait until it has been called before moving on.
* @returns {} Optionally return a Promise instead of using `done` to cause Jasmine to wait for completion.
*/
/**
* Create a group of specs (often called a suite).
*
@@ -8,7 +18,7 @@ getJasmineRequireObj().interface = function(jasmine, env) {
* @function
* @global
* @param {String} description Textual description of the group
* @param {Function} specDefinitions Function for Jasmine to invoke that will define inner suites a specs
* @param {Function} specDefinitions Function for Jasmine to invoke that will define inner suites and specs
*/
describe: function(description, specDefinitions) {
return env.describe(description, specDefinitions);
@@ -22,7 +32,7 @@ getJasmineRequireObj().interface = function(jasmine, env) {
* @function
* @global
* @param {String} description Textual description of the group
* @param {Function} specDefinitions Function for Jasmine to invoke that will define inner suites a specs
* @param {Function} specDefinitions Function for Jasmine to invoke that will define inner suites and specs
*/
xdescribe: function(description, specDefinitions) {
return env.xdescribe(description, specDefinitions);
@@ -37,7 +47,7 @@ getJasmineRequireObj().interface = function(jasmine, env) {
* @function
* @global
* @param {String} description Textual description of the group
* @param {Function} specDefinitions Function for Jasmine to invoke that will define inner suites a specs
* @param {Function} specDefinitions Function for Jasmine to invoke that will define inner suites and specs
*/
fdescribe: function(description, specDefinitions) {
return env.fdescribe(description, specDefinitions);
@@ -51,7 +61,7 @@ getJasmineRequireObj().interface = function(jasmine, env) {
* @function
* @global
* @param {String} description Textual description of what this spec is checking
* @param {Function} [testFunction] Function that contains the code of your test. If not provided the test will be `pending`.
* @param {implementationCallback} [testFunction] Function that contains the code of your test. If not provided the test will be `pending`.
* @param {Int} [timeout={@link jasmine.DEFAULT_TIMEOUT_INTERVAL}] Custom timeout for an async spec.
*/
it: function() {
@@ -66,7 +76,7 @@ getJasmineRequireObj().interface = function(jasmine, env) {
* @function
* @global
* @param {String} description Textual description of what this spec is checking.
* @param {Function} [testFunction] Function that contains the code of your test. Will not be executed.
* @param {implementationCallback} [testFunction] Function that contains the code of your test. Will not be executed.
*/
xit: function() {
return env.xit.apply(env, arguments);
@@ -80,7 +90,7 @@ getJasmineRequireObj().interface = function(jasmine, env) {
* @function
* @global
* @param {String} description Textual description of what this spec is checking.
* @param {Function} testFunction Function that contains the code of your test.
* @param {implementationCallback} testFunction Function that contains the code of your test.
* @param {Int} [timeout={@link jasmine.DEFAULT_TIMEOUT_INTERVAL}] Custom timeout for an async spec.
*/
fit: function() {
@@ -92,7 +102,7 @@ getJasmineRequireObj().interface = function(jasmine, env) {
* @name beforeEach
* @function
* @global
* @param {Function} [function] Function that contains the code to setup your specs.
* @param {implementationCallback} [function] Function that contains the code to setup your specs.
* @param {Int} [timeout={@link jasmine.DEFAULT_TIMEOUT_INTERVAL}] Custom timeout for an async beforeEach.
*/
beforeEach: function() {
@@ -104,7 +114,7 @@ getJasmineRequireObj().interface = function(jasmine, env) {
* @name afterEach
* @function
* @global
* @param {Function} [function] Function that contains the code to teardown your specs.
* @param {implementationCallback} [function] Function that contains the code to teardown your specs.
* @param {Int} [timeout={@link jasmine.DEFAULT_TIMEOUT_INTERVAL}] Custom timeout for an async afterEach.
*/
afterEach: function() {
@@ -118,7 +128,7 @@ getJasmineRequireObj().interface = function(jasmine, env) {
* @name beforeAll
* @function
* @global
* @param {Function} [function] Function that contains the code to setup your specs.
* @param {implementationCallback} [function] Function that contains the code to setup your specs.
* @param {Int} [timeout={@link jasmine.DEFAULT_TIMEOUT_INTERVAL}] Custom timeout for an async beforeAll.
*/
beforeAll: function() {
@@ -126,13 +136,13 @@ getJasmineRequireObj().interface = function(jasmine, env) {
},
/**
* Run some shared teardown once before all of the specs in the {@link describe} are run.
* Run some shared teardown once after all of the specs in the {@link describe} are run.
*
* _Note:_ Be careful, sharing the teardown from a afterAll makes it easy to accidentally leak state between your specs so that they erroneously pass or fail.
* @name afterAll
* @function
* @global
* @param {Function} [function] Function that contains the code to teardown your specs.
* @param {implementationCallback} [function] Function that contains the code to teardown your specs.
* @param {Int} [timeout={@link jasmine.DEFAULT_TIMEOUT_INTERVAL}] Custom timeout for an async afterAll.
*/
afterAll: function() {
@@ -187,7 +197,7 @@ getJasmineRequireObj().interface = function(jasmine, env) {
},
/**
* Install a spy on a property onto an existing object.
* Install a spy on a property installed with `Object.defineProperty` onto an existing object.
* @name spyOnProperty
* @function
* @global

View File

@@ -1,4 +1,4 @@
getJasmineRequireObj().util = function() {
getJasmineRequireObj().util = function(j$) {
var util = {};
@@ -55,6 +55,23 @@ getJasmineRequireObj().util = function() {
return cloned;
};
util.cloneArgs = function(args) {
var clonedArgs = [];
var argsAsArray = j$.util.argsToArray(args);
for(var i = 0; i < argsAsArray.length; i++) {
var str = Object.prototype.toString.apply(argsAsArray[i]),
primitives = /^\[object (Boolean|String|RegExp|Number)/;
// All falsey values are either primitives, `null`, or `undefined.
if (!argsAsArray[i] || str.match(primitives)) {
clonedArgs.push(argsAsArray[i]);
} else {
clonedArgs.push(j$.util.clone(argsAsArray[i]));
}
}
return clonedArgs;
};
util.getPropertyDescriptor = function(obj, methodName) {
var descriptor,
proto = obj;

View File

@@ -5,6 +5,46 @@ jasmineRequire.HtmlReporter = function(j$) {
elapsed: function() { return 0; }
};
function ResultsStateBuilder() {
this.topResults = new j$.ResultsNode({}, '', null);
this.currentParent = this.topResults;
this.specsExecuted = 0;
this.failureCount = 0;
this.pendingSpecCount = 0;
}
ResultsStateBuilder.prototype.suiteStarted = function(result) {
this.currentParent.addChild(result, 'suite');
this.currentParent = this.currentParent.last();
};
ResultsStateBuilder.prototype.suiteDone = function(result) {
if (this.currentParent !== this.topResults) {
this.currentParent = this.currentParent.parent;
}
};
ResultsStateBuilder.prototype.specStarted = function(result) {
};
ResultsStateBuilder.prototype.specDone = function(result) {
this.currentParent.addChild(result, 'spec');
if (result.status !== 'disabled') {
this.specsExecuted++;
}
if (result.status === 'failed') {
this.failureCount++;
}
if (result.status == 'pending') {
this.pendingSpecCount++;
}
};
function HtmlReporter(options) {
var env = options.env || {},
getContainer = options.getContainer,
@@ -17,9 +57,6 @@ jasmineRequire.HtmlReporter = function(j$) {
filterSpecs = options.filterSpecs,
timer = options.timer || noopTimer,
results = [],
specsExecuted = 0,
failureCount = 0,
pendingSpecCount = 0,
htmlReporterMain,
symbols,
failedSuites = [];
@@ -48,12 +85,10 @@ jasmineRequire.HtmlReporter = function(j$) {
var summary = createDom('div', {className: 'jasmine-summary'});
var topResults = new j$.ResultsNode({}, '', null),
currentParent = topResults;
var stateBuilder = new ResultsStateBuilder();
this.suiteStarted = function(result) {
currentParent.addChild(result, 'suite');
currentParent = currentParent.last();
stateBuilder.suiteStarted(result);
};
this.suiteDone = function(result) {
@@ -61,27 +96,21 @@ jasmineRequire.HtmlReporter = function(j$) {
failedSuites.push(result);
}
if (currentParent == topResults) {
return;
}
currentParent = currentParent.parent;
stateBuilder.suiteDone(result);
};
this.specStarted = function(result) {
currentParent.addChild(result, 'spec');
stateBuilder.specStarted(result);
};
var failures = [];
this.specDone = function(result) {
stateBuilder.specDone(result);
if(noExpectations(result) && typeof console !== 'undefined' && typeof console.error !== 'undefined') {
console.error('Spec \'' + result.fullName + '\' has no expectations.');
}
if (result.status != 'disabled') {
specsExecuted++;
}
if (!symbols){
symbols = find('.jasmine-symbol-summary');
}
@@ -94,8 +123,6 @@ jasmineRequire.HtmlReporter = function(j$) {
));
if (result.status == 'failed') {
failureCount++;
var failure =
createDom('div', {className: 'jasmine-spec-detail jasmine-failed'},
createDom('div', {className: 'jasmine-description'},
@@ -113,10 +140,6 @@ jasmineRequire.HtmlReporter = function(j$) {
failures.push(failure);
}
if (result.status == 'pending') {
pendingSpecCount++;
}
};
this.jasmineDone = function(doneResult) {
@@ -179,9 +202,9 @@ jasmineRequire.HtmlReporter = function(j$) {
}
};
if (specsExecuted < totalSpecsDefined) {
var skippedMessage = 'Ran ' + specsExecuted + ' of ' + totalSpecsDefined + ' specs - run all';
var skippedLink = order && order.random ? '?random=true' : '?';
if (stateBuilder.specsExecuted < totalSpecsDefined) {
var skippedMessage = 'Ran ' + stateBuilder.specsExecuted + ' of ' + totalSpecsDefined + ' specs - run all';
var skippedLink = addToExistingQueryString('spec', '');
alert.appendChild(
createDom('span', {className: 'jasmine-bar jasmine-skipped'},
createDom('a', {href: skippedLink, title: 'Run all specs'}, skippedMessage)
@@ -192,9 +215,9 @@ jasmineRequire.HtmlReporter = function(j$) {
var statusBarClassName = 'jasmine-bar ';
if (totalSpecsDefined > 0) {
statusBarMessage += pluralize('spec', specsExecuted) + ', ' + pluralize('failure', failureCount);
if (pendingSpecCount) { statusBarMessage += ', ' + pluralize('pending spec', pendingSpecCount); }
statusBarClassName += (failureCount > 0) ? 'jasmine-failed' : 'jasmine-passed';
statusBarMessage += pluralize('spec', stateBuilder.specsExecuted) + ', ' + pluralize('failure', stateBuilder.failureCount);
if (stateBuilder.pendingSpecCount) { statusBarMessage += ', ' + pluralize('pending spec', stateBuilder.pendingSpecCount); }
statusBarClassName += (stateBuilder.failureCount > 0) ? 'jasmine-failed' : 'jasmine-passed';
} else {
statusBarClassName += 'jasmine-skipped';
statusBarMessage += 'No specs found';
@@ -229,7 +252,7 @@ jasmineRequire.HtmlReporter = function(j$) {
var results = find('.jasmine-results');
results.appendChild(summary);
summaryList(topResults, summary);
summaryList(stateBuilder.topResults, summary);
function summaryList(resultsTree, domParent) {
var specListNode;

View File

@@ -1,4 +1,10 @@
#!/bin/bash -e
rm -rf ~/.nvm
git clone https://github.com/creationix/nvm.git ~/.nvm
(cd ~/.nvm && git checkout `git describe --abbrev=0 --tags`)
source ~/.nvm/nvm.sh
nvm install ${1:-"v0.12.18"}
npm install
npm test