Compare commits

...

41 Commits

Author SHA1 Message Date
Peter Bacon Darwin 514639b585 docs(CHANGELOG): add 1.5.3 release notes 2016-03-25 20:01:45 +00:00
Peter Bacon Darwin 9cd9956dcb feat($compile): add more lifecycle hooks to directive controllers
This change adds in the following new lifecycle hooks, which map in some
way to those in Angular 2:

 * `$onChanges(changesObj)` - Called whenever one-way bindings are updated. The `changesObj` is a hash whose keys
   are the names of the bound properties that have changed, and the values are an object of the form
   `{ currentValue: ..., previousValue: ... }`. Use this hook to trigger updates within a component such as
   cloning the bound value to prevent accidental mutation of the outer value.
 * `$onDestroy` - Called on a controller when its containing scope is destroyed. Use this hook for releasing
   external resources, watches and event handlers.
 * `$postLink` - Called after this controller's element and its children been linked. Similar to the post-link
   function this hook can be used to set up DOM event handlers and do direct DOM manipulation.
   Note that child elements that contain `templateUrl` directives will not have been compiled and linked since
   they are waiting for their template to load asynchronously and their own compilation and linking has been
   suspended until that occurs.

Closes #14127
Closes #14030
Closes #14020
Closes #13991
Closes #14302
2016-03-25 12:56:08 +00:00
Martin Staffa c7813e9ebf fix(ngAnimate): run structural animations with cancelled out class changes
When multiple animations on the same element are queued before a $digest passes,
the animator tries to create as few actual animations as possible by joining / canceling
redundant animations. Class-based animations for example are cancelled when the classes that
are added and removed are the same, and the result is no class-change. This however must only
happen if there's no structural animation currently queued.

Fixes #14249
2016-03-24 00:13:16 +01:00
Martin Staffa ef91b04cdd fix(ngMessages): don't crash when nested messages are removed
Under specific circumstances, ngMessages would go into an infinite loop and crash the
browser / page:
- At least two ngMessage elements are wrapped inside another element (e.g. ngTransclude)
- The first message is currently visible
- The first message is removed (e.g. when the whole ngMessages element is removed by an ngIf)

When a message is removed, it looks for a previous message - in this specific case it would misidentify
the second message for a previous message, which would then cause the first message to be marked as the
second message's next message, resulting in an infinite loop, and crash.

This fix ensures that when searching for previous messages, ngMessage walks the DOM in a way so
that messages that come after the current message are never identified as previous messages.

This commit also detaches and destroys all child ngMessage elements when the ngMessages element is
destroyed, which should improve performance slightly.

Fixes #14183
Closes #14242
2016-03-24 00:13:16 +01:00
Alex Chuev 1acd97e18f docs(guide/component): add missing closing bracket
Closes #14299
2016-03-23 23:58:35 +02:00
Daniel Herman 513199ee9f fix($compile): workaround a GC bug in Chrome < 50
In the version of V8 used in Chrome < 50, the parent of template nodes for
`transclude: "element"` directives would be improperly garbage collected
despite still having been referenced via `parentNode`.

This bug surfaced due to the introduction of lazy transclusion (652b83e),
and appears under certain circumstances when using directive start and end elements.

It should be removed some time after Chrome 50 has been released.

Fixes #14041
Closes #14286
2016-03-23 22:06:17 +01:00
pmadruga 33f3c40e93 docs(error/$compile.baddir): mention "components" in directive name error
Closes #14212
2016-03-23 22:18:54 +02:00
Steve Mao 696cb95d5e docs($q): mention ES2015 (as a "synonym" for ES6) and remove "harmony"
Closes #14294
2016-03-22 12:09:02 +02:00
Georgios Kalpakas 457fd21a1a fix($sniffer): fix history sniffing in Chrome Packaged Apps
Although `window.history` is present in the context of Chrome Packaged Apps, it is not allowed to
access `window.history.pushState` or `window.history.state`, resulting in errors when trying to
"sniff" history support.
This commit fixes it by detecting a Chrome Packaged App (through the presence of
`window.chrome.app.runtime`). Note that `window.chrome.app` is present in the context of "normal"
webpages as well, but it doesn't have the `runtime` property, which is only available to packaged
apps (e.g. see https://developer.chrome.com/apps/api_index).

(It also also contains some style changes for making the structure and layout of `$sniffer` tests
 more consistent.)

Fixes #11932

Closes #13945
2016-03-22 11:57:18 +02:00
Wassim Chegham 3c6dfbf67d docs(guide/component-router): fix typos
Closes #14278
2016-03-22 02:18:44 +02:00
Owen Craig 3277b885c4 fix(formatNumber): handle small numbers correctly when gSize !== lgSize
By using `>=` when comparing the number length to `lgSize`, we'll provide the correct value, when
formatting numbers with different `lgSize` than `gSize`.

Fixes #14289

Closes #14290
2016-03-22 00:11:26 +02:00
Georgios Kalpakas 48a256d04b test(TzDate): fix test in Australia
Probably due to implementation differences in browsers for pre-DST period (see
https://github.com/angular/angular.js/issues/5017 and especially
https://github.com/angular/angular.js/issues/5017#issuecomment-90775226 for context), some
`TzDate` tests had different behavior on different Timezones/Regions (e.g. failed in Australia,
which started to observe DST in 1971).
Since the used year (`1970`) didn't have any particular significance, this commit fixes the issue
by using a year that is more consistently handled by browsers (`2000`).

Fixes #14272

Closes #14285
2016-03-21 20:45:35 +02:00
surya prakash singh 0579430799 docs(input[time]): fix a typo in the example
Closes #14220
2016-03-21 01:26:17 +02:00
Rongduan Zhu 39ac68dac1 docs(guide/component-router): changed path to match diagram
Closes #14277
2016-03-21 00:00:12 +02:00
Georgios Kalpakas 87fb44a5d3 docs(CHANGELOG.md): rearrange v1.5.1 to be right below v1.5.2
Moved the `v1.5.1` section above the `v1.4.10` one, so that it is right below the `v1.5.2` section
for easier reference. Also removed an empty "Breaking Changes" sub-section.

Closes #14283
2016-03-20 22:54:53 +02:00
Georgios Kalpakas 5c76b406f7 chore(ci-checks): fix the ddescribe-iit task for Jasmine 2
Closes #14276
2016-03-20 22:26:06 +02:00
Matias Niemelä f665968daf chore(CHANGELOG): update version reference 2016-03-18 15:37:43 -07:00
Matias Niemelä 5d1291c29d docs(CHANGELOG): add notes for v1.5.2 2016-03-18 15:04:06 -07:00
Martin Staffa ce7f400011 fix(ngAnimate.$animate): remove animation callbacks when the element is removed
The test for this didn't actually test the listener removal. The addClass animation after
the element removal didn't start because the enter animation was still in progress.
2016-03-18 17:10:26 +01:00
Martin Staffa 0fc8516b0c Revert "tests(jQuery): test on both oldest & latest supported jQuery version"
This reverts commit 94572e89c2. The commit snuck
in while cherry-picking commits from master.
2016-03-18 17:08:46 +01:00
Jason Bedard 28b2a9b583 style($compile,$controller): adding function names for debug/tracing
Closes #13420
2016-03-18 15:53:09 +01:00
Jason Bedard e8549372fc style($templateRequest): rename minError var to avoid name conflict
Closes #13701
2016-03-18 15:53:08 +01:00
Jason Bedard 5a434eb74e style(ngModel,ngOptions): make use of declared but unused variables 2016-03-18 15:53:06 +01:00
Jason Bedard 318de4db66 style(*): remove unused variables 2016-03-18 15:53:05 +01:00
Martin Staffa a6c79bf15d docs(guide/Services): add whitespace in code example
Closes #14156
2016-03-18 15:53:04 +01:00
Huc Arnaud c6a10a755e docs(error/tplrt): add missing ' in example code
Missing a ' @ line 46 class='wrapper'

Closes #14258
2016-03-18 15:53:03 +01:00
Maxim Salnikov 1d7bd5bf4f docs(guide/Component Router): fix typo in example code
Closes #14262
2016-03-18 15:53:03 +01:00
Michał Gołębiowski 94572e89c2 tests(jQuery): test on both oldest & latest supported jQuery version 2016-03-18 15:51:30 +01:00
Peter Bacon Darwin fee7bac392 revert: fix($compile): do not add <span> elements to root text nodes
This commit reverts 7617c08da6 which was accidentally
merged into 1.5.x (by @petebacondarwin in a moment of rebase madness) despite
it containing a breaking change.
2016-03-17 09:56:30 +00:00
Georgios Kalpakas dbb3b0f561 docs(CHANGELOG.md): add notes for v1.4.10 2016-03-16 21:17:02 +02:00
Michał Gołębiowski b8bfed6a52 tests(jQuery): make the tests pass on jQuery 3.0.0-beta1
Closes #14229
2016-03-16 18:29:13 +00:00
Georgios Kalpakas 7215a89e7d docs(CHANGELOG.md): fix typo in anchor name 2016-03-16 19:56:23 +02:00
Georgios Kalpakas b7cca56091 docs(CHANGELOG.md): add notes for v1.5.1 2016-03-16 19:53:43 +02:00
Josh Schneider 7338e433f8 docs(guide/component-router): fix incorrect hook name for $canActivate
The hook will most likely be named back to `$routerCanActivate` in the future,
but for now this change is accurate.

Closes #14237
2016-03-16 16:46:03 +00:00
Peter Bacon Darwin 119ed07d5a chore(travis): update node and browser versions 2016-03-16 14:08:10 +00:00
Peter Bacon Darwin 5f98ae8323 chore(package.json): fix up dist-tag
Now that 1.5.x is out of beta we need to change the dist-tag to `latest`
2016-03-16 14:08:10 +00:00
Peter Bacon Darwin 8a598b43bb chore(jenkins): update node version to 4.4 2016-03-16 14:08:10 +00:00
Peter Bacon Darwin e5dff4cfbe chore(jenkins): fix node version chooser in build scripts
The `set-node-version.sh` script was being run in its own shell and so
was not actually changing the current version of node.
2016-03-16 14:08:10 +00:00
Lucas Mirelmann 6c7a9cdd5f chore(*): Upgrade to Jasmine 2.4
Highlights:
New mechanism to run async tests as Jasmine 2 removed `runs`, `waits` and `waitsFor`
The functions `iit`, `ddescribe` and `tthey` were renamed `fit`, `fdescribe` and
`fthey` as the originals came from Karma, Karma no longer bundles Jasmine and the
new function name comes from Jasmine.

Closes #14226
2016-03-16 14:08:09 +00:00
Matias Niemelä 3b34f762fe chore(build): 1.5 versions should stick to 1.5.x 2016-03-16 14:06:49 +00:00
Martin Staffa fa167ba747 docs(guide/animations): fix code block styling 2016-03-16 14:06:49 +00:00
102 changed files with 8616 additions and 11338 deletions
+1 -1
View File
@@ -1,7 +1,7 @@
language: node_js
sudo: false
node_js:
- '4.2'
- '4.4'
cache:
directories:
+211
View File
@@ -1,3 +1,211 @@
<a name="1.5.3"></a>
# 1.5.3 diplohaplontic-meiosis (2016-03-25)
## Bug Fixes
- **$compile:** workaround a GC bug in Chrome < 50
([513199ee](https://github.com/angular/angular.js/commit/513199ee9f1c8eef1240983d6e52c824404adb98),
[#14041](https://github.com/angular/angular.js/issues/14041), [#14286](https://github.com/angular/angular.js/issues/14286))
- **$sniffer:** fix history sniffing in Chrome Packaged Apps
([457fd21a](https://github.com/angular/angular.js/commit/457fd21a1a0c10c66245c32a73602f3a09038bda),
[#11932](https://github.com/angular/angular.js/issues/11932), [#13945](https://github.com/angular/angular.js/issues/13945))
- **formatNumber:** handle small numbers correctly when `gSize` !== `lgSize`
([3277b885](https://github.com/angular/angular.js/commit/3277b885c4dec3edd51b8e8c3d1776057d6d4d1d),
[#14289](https://github.com/angular/angular.js/issues/14289), [#14290](https://github.com/angular/angular.js/issues/14290))
- **ngAnimate:** run structural animations with cancelled out class changes
([c7813e9e](https://github.com/angular/angular.js/commit/c7813e9ebf793fe89380dcad54e8e002fafdd985),
[#14249](https://github.com/angular/angular.js/issues/14249))
- **ngMessages:** don't crash when nested messages are removed
([ef91b04c](https://github.com/angular/angular.js/commit/ef91b04cdd794f308617bca7ebd0b1b747e4f7de),
[#14183](https://github.com/angular/angular.js/issues/14183), [#14242](https://github.com/angular/angular.js/issues/14242))
## Features
- **$compile:** add more lifecycle hooks to directive controllers
([9cd9956d](https://github.com/angular/angular.js/commit/9cd9956dcbc8382e8e8757a805398bd251bbc67e),
[#14127](https://github.com/angular/angular.js/issues/14127), [#14030](https://github.com/angular/angular.js/issues/14030), [#14020](https://github.com/angular/angular.js/issues/14020), [#13991](https://github.com/angular/angular.js/issues/13991), [#14302](https://github.com/angular/angular.js/issues/14302))
<a name="1.5.2"></a>
# 1.5.2 differential-recovery (2016-03-18)
This release reverts a breaking change that accidentally made it into the 1.5.1 release. See
[fee7bac3](https://github.com/angular/angular.js/commit/fee7bac392db24b6006d6a57ba71526f3afa102c)
for more info.
## Bug Fixes
- **ngAnimate.$animate:** remove animation callbacks when the element is removed
([ce7f4000](https://github.com/angular/angular.js/commit/ce7f400011e1e2e1b9316f18ce87b87b79d878b4))
<a name="1.5.1"></a>
# 1.5.1 equivocal-sophistication (2016-03-16)
## Bug Fixes
- **core:** only call `console.log` when `window.console` exists
([ce138f3c](https://github.com/angular/angular.js/commit/ce138f3c552f8bf741721ab8d10994ed35a4b2f5),
[#14006](https://github.com/angular/angular.js/issues/14006), [#14007](https://github.com/angular/angular.js/issues/14007), [#14047](https://github.com/angular/angular.js/issues/14047))
- **$compile:** allow directives to have decorators
([0728cc2f](https://github.com/angular/angular.js/commit/0728cc2f2bb04d5dbdfca41f3afacea16c75ee07))
- **$resource:** fix parse errors on older Android WebViews
([df8db7b4](https://github.com/angular/angular.js/commit/df8db7b446b5bae83afef457d706d2805e597f29),
[#13989](https://github.com/angular/angular.js/issues/13989))
- **$routeProvider:** properly handle optional eager path named groups
([c0797c68](https://github.com/angular/angular.js/commit/c0797c68866c9ef8ff3c2f6985e6eb9374346151),
[#14011](https://github.com/angular/angular.js/issues/14011))
- **copy:** add support for copying `Blob` objects
([e9d579b6](https://github.com/angular/angular.js/commit/e9d579b608c2be8fdcf0326d0679a76bb9ae5b6e),
[#9669](https://github.com/angular/angular.js/issues/9669), [#14064](https://github.com/angular/angular.js/issues/14064))
- **dateFilter:** correctly format BC years
([e36205f5](https://github.com/angular/angular.js/commit/e36205f5af82b69362def7d2b6eeeb038f592311))
- **formatNumber:** allow negative fraction size
([e046c170](https://github.com/angular/angular.js/commit/e046c170bcf677f26e61af6470cb5fd2f751c969),
[#13913](https://github.com/angular/angular.js/issues/13913))
- **input:** re-validate when partially editing date-family inputs
([e383804c](https://github.com/angular/angular.js/commit/e383804c4ab62278fbaf4fdfaa03caeacff77fc4),
[#12207](https://github.com/angular/angular.js/issues/12207), [#13886](https://github.com/angular/angular.js/issues/13886))
- **input\[date\]:** support years with more than 4 digits
([d76951f1](https://github.com/angular/angular.js/commit/d76951f1747abd2da6e320d4ff9019f170d9793f),
[#13735](https://github.com/angular/angular.js/issues/13735), [#13905](https://github.com/angular/angular.js/issues/13905))
- **ngOptions:** always set the 'selected' attribute for selected options
([9f5a1722](https://github.com/angular/angular.js/commit/9f5a172291ff6926dcd246f0972288916a4c9bf6),
[#14115](https://github.com/angular/angular.js/issues/14115))
- **ngRoute:** allow `ngView` to be included in an asynchronously loaded template
([8237482d](https://github.com/angular/angular.js/commit/8237482d49e76e2c4994fe6207e3c9799ef04163),
[#1213](https://github.com/angular/angular.js/issues/1213), [#6812](https://github.com/angular/angular.js/issues/6812), [#14088](https://github.com/angular/angular.js/issues/14088))
- **ngMock:**
- attach `$injector` to `$rootElement` and prevent memory leak due to attached data
([75373dd4](https://github.com/angular/angular.js/commit/75373dd4bdae6c6035272942c69444c386f824cd),
[#14022](https://github.com/angular/angular.js/issues/14022), [#14094](https://github.com/angular/angular.js/issues/14094), [#14098](https://github.com/angular/angular.js/issues/14098))
- don't break if `$rootScope.$destroy()` is not a function
([50ed8712](https://github.com/angular/angular.js/commit/50ed8712566d601c9fb76b71f7b534b5bc803a36),
[#14106](https://github.com/angular/angular.js/issues/14106), [#14107](https://github.com/angular/angular.js/issues/14107))
- **ngMockE2E:** pass `responseType` to `$delegate` when using `passThrough`
([d16faf9f](https://github.com/angular/angular.js/commit/d16faf9f2b9bd2b85d95e71d902cec0269282f2c),
[#5415](https://github.com/angular/angular.js/issues/5415), [#5783](https://github.com/angular/angular.js/issues/5783))
## Features
- **$compile:** add custom annotations to the controller
([0c800930](https://github.com/angular/angular.js/commit/0c8009300b819c39c5e4892856724a731a8dcda6),
[#14114](https://github.com/angular/angular.js/issues/14114))
- **$controllerProvider:** add a `has()` method for checking the existence of a controller
([bb9575db](https://github.com/angular/angular.js/commit/bb9575dbd3428176216355df7b2933d2a72783cd),
[#13951](https://github.com/angular/angular.js/issues/13951), [#14109](https://github.com/angular/angular.js/issues/14109))
- **dateFilter:** add support for STANDALONEMONTH in format (`LLLL`)
([3e5b25b3](https://github.com/angular/angular.js/commit/3e5b25b33f278376def432698c704b1807fdb8c0),
[#13999](https://github.com/angular/angular.js/issues/13999), [#14013](https://github.com/angular/angular.js/issues/14013))
- **ngMock:** add `sharedInjector()` to `angular.mock.module`
([a46ab60f](https://github.com/angular/angular.js/commit/a46ab60fd5bf94896f0761e858ef38b998eb0f80),
[#14093](https://github.com/angular/angular.js/issues/14093), [#10238](https://github.com/angular/angular.js/issues/10238))
## Performance Improvements
- **ngRepeat:** avoid duplicate jqLite wrappers
([632e15a3](https://github.com/angular/angular.js/commit/632e15a3afdcd30168700cec1367bd81966400d4))
- **ngAnimate:**
- avoid jqLite/jQuery for upward DOM traversal
([35251bd4](https://github.com/angular/angular.js/commit/35251bd4ce23251b5e9a2860cf414726c194721e))
- avoid `$.fn.data` overhead with jQuery
([15915e60](https://github.com/angular/angular.js/commit/15915e606fdf5114592db1a0a5e3f12e639d7cdb))
<a name="1.4.10"></a>
# 1.4.10 benignant-oscillation (2016-03-16)
## Bug Fixes
- **core:** only call `console.log` when `window.console` exists
([beb00e44](https://github.com/angular/angular.js/commit/beb00e44de947981dbe35d5cf7a116e10ea8dc67),
[#14006](https://github.com/angular/angular.js/issues/14006), [#14007](https://github.com/angular/angular.js/issues/14007), [#14047](https://github.com/angular/angular.js/issues/14047))
- **$animateCss:** cancel fallback timeout when animation ends normally
([a60bbc12](https://github.com/angular/angular.js/commit/a60bbc12e8c5170e70d95f1b2c3e309b3b95cb84),
[#13787](https://github.com/angular/angular.js/issues/13787))
- **$compile:**
- allow directives to have decorators
([77cdc37c](https://github.com/angular/angular.js/commit/77cdc37c65491b551fcf01a18ab848a693c293d7))
- properly denormalize templates when only one of the start/end symbols is different
([2d44a681](https://github.com/angular/angular.js/commit/2d44a681eb912a81a8bc8e16a278c45dae91fa24),
[#13848](https://github.com/angular/angular.js/issues/13848))
- handle boolean attributes in `@` bindings
([2ffbfb0a](https://github.com/angular/angular.js/commit/2ffbfb0ad0647d103ff339ee4b772b62d4823bf3),
[#13767](https://github.com/angular/angular.js/issues/13767), [#13769](https://github.com/angular/angular.js/issues/13769))
- **$parse:**
- prevent assignment on constructor properties
([f47e2180](https://github.com/angular/angular.js/commit/f47e218006029f39b4785d820b430de3a0eebcb0),
[#13417](https://github.com/angular/angular.js/issues/13417))
- preserve expensive checks when runnning `$eval` inside an expression
([96d62cc0](https://github.com/angular/angular.js/commit/96d62cc0fc77248d7e3ec4aa458bac0d3e072629))
- copy `inputs` for expressions with expensive checks
([0b7fff30](https://github.com/angular/angular.js/commit/0b7fff303f46202bbae1ff3ca9d0e5fa76e0fc9a))
- **$rootScope:** set no context when calling helper functions for `$watch`
([ab5c7698](https://github.com/angular/angular.js/commit/ab5c7698bb106669ca31b5f79a95afa54d65c5f1))
- **$route:** allow preventing a route reload
([4bc30314](https://github.com/angular/angular.js/commit/4bc3031497447ad527356f12bd0ceee1d7d09db5),
[#9824](https://github.com/angular/angular.js/issues/9824), [#13894](https://github.com/angular/angular.js/issues/13894))
- **$routeProvider:** properly handle optional eager path named groups
([6a4403a1](https://github.com/angular/angular.js/commit/6a4403a11845173d6a96232f77d73aa544b182af),
[#14011](https://github.com/angular/angular.js/issues/14011))
- **copy:** add support for copying `Blob` objects
([863a4232](https://github.com/angular/angular.js/commit/863a4232a6faa92428df45cd54d5a519be2434de),
[#9669](https://github.com/angular/angular.js/issues/9669), [#14064](https://github.com/angular/angular.js/issues/14064))
- **dateFilter:** follow the CLDR on pattern escape sequences
([f476060d](https://github.com/angular/angular.js/commit/f476060de6cc016380c0343490a184543f853652),
[#12839](https://github.com/angular/angular.js/issues/12839))
- **dateFilter, input:** fix Date parsing in IE/Edge when timezone offset contains `:`
([571afd65](https://github.com/angular/angular.js/commit/571afd6558786d7b99e2aebd307b4a94c9f2bb87),
[#13880](https://github.com/angular/angular.js/issues/13880), [#13887](https://github.com/angular/angular.js/issues/13887))
- **input:** re-validate when partially editing date-family inputs
([02929f82](https://github.com/angular/angular.js/commit/02929f82f30449301ff18fea84a6396a017683b1),
[#12207](https://github.com/angular/angular.js/issues/12207), [#13886](https://github.com/angular/angular.js/issues/13886))
- **select:** handle corner case of adding options via a custom directive
([df6e7315](https://github.com/angular/angular.js/commit/df6e731506831a3dc7f44c9a90abe17515450b3e),
[#13874](https://github.com/angular/angular.js/issues/13874), [#13878](https://github.com/angular/angular.js/issues/13878))
- **ngOptions:** always set the 'selected' attribute for selected options
([f87e8288](https://github.com/angular/angular.js/commit/f87e8288fb69526fd240a66a046f5de52ed204de),
[#14115](https://github.com/angular/angular.js/issues/14115))
- **ngAnimate:** properly cancel previously running class-based animations
([3b27dd37](https://github.com/angular/angular.js/commit/3b27dd37a2cc8a52992784ece6b371023dadf792),
[#10156](https://github.com/angular/angular.js/issues/10156), [#13822](https://github.com/angular/angular.js/issues/13822))
- **ngAnimateChildren:** make it compatible with `ngIf`
([dc158e7e](https://github.com/angular/angular.js/commit/dc158e7e40624ef94c66560386522ef7e991a9ce),
[#13865](https://github.com/angular/angular.js/issues/13865), [#13876](https://github.com/angular/angular.js/issues/13876))
- **ngMockE2E:** pass `responseType` to `$delegate` when using `passThrough`
([947cb4d1](https://github.com/angular/angular.js/commit/947cb4d1451afa4f5090a693df5b1968dd0df70c),
[#5415](https://github.com/angular/angular.js/issues/5415), [#5783](https://github.com/angular/angular.js/issues/5783))
## Features
- **$locale:** Include original locale ID in $locale
([e69f3550](https://github.com/angular/angular.js/commit/e69f35507e10c994708ce4f1efba7573951d1acd),
[#13390](https://github.com/angular/angular.js/issues/13390))
- **ngAnimate:** provide ng-[event]-prepare class for structural animations
([796f7ab4](https://github.com/angular/angular.js/commit/796f7ab41487e124b5b0c02dbf0a03bd581bf073))
## Performance Improvements
- **$compile:** avoid needless overhead when wrapping text nodes
([946d9ae9](https://github.com/angular/angular.js/commit/946d9ae90bb31fe911ebbe1b80cd4c8af5a665c6))
- **ngRepeat:** avoid duplicate jqLite wrappers
([d04c38c4](https://github.com/angular/angular.js/commit/d04c38c48968db777c3ea6a177ce2ff0116df7b4))
- **ngAnimate:**
- avoid jqLite/jQuery for upward DOM traversal
([ab95ba65](https://github.com/angular/angular.js/commit/ab95ba65c08b38cace83de6717b7681079182b45))
- avoid `$.fn.data` overhead with jQuery
([86416bcb](https://github.com/angular/angular.js/commit/86416bcbee2192fa31c017163c5d856763182ade))
<a name="1.5.0"></a>
# 1.5.0 ennoblement-facilitation (2016-02-05)
@@ -33,6 +241,9 @@
## Breaking Changes
### Upgrade to 1.5.1
This version of AngularJS is problematic due to a issue during its release. Please upgrade to version [1.5.2](#1.5.2).
- **ngAria:** due to [d06431e5](https://github.com/angular/angular.js/commit/d06431e5309bb0125588877451dc79b935808134),
Where appropriate, ngAria now applies ARIA to custom controls only, not native inputs. Because of this, support for `aria-multiline` on textareas has been removed.
+6 -1
View File
@@ -264,12 +264,17 @@ module.exports = function(grunt) {
],
options: {
disallowed: [
'fit',
'iit',
'xit',
'fthey',
'tthey',
'xthey',
'fdescribe',
'ddescribe',
'xdescribe'
'xdescribe',
'it.only',
'describe.only'
]
}
},
+1
View File
@@ -205,6 +205,7 @@ var angularFiles = {
"karmaModules": [
'build/angular.js',
'@angularSrcModules',
'test/modules/no_bootstrap.js',
'src/ngScenario/browserTrigger.js',
'test/helpers/*.js',
'test/ngMessageFormat/*.js',
+1 -1
View File
@@ -25,7 +25,7 @@ describe("DocsController", function() {
it("should update the Google Analytics with $location.path if currentPage is missing", inject(function($window, $location) {
$window._gaq = [];
spyOn($location, 'path').andReturn('x/y/z');
spyOn($location, 'path').and.returnValue('x/y/z');
$scope.$broadcast('$includeContentLoaded');
expect($window._gaq.pop()).toEqual(['_trackPageview', 'x/y/z']);
}));
+7 -7
View File
@@ -4,7 +4,7 @@ describe('errors', function() {
// Mock `ngSanitize` module
angular.
module('ngSanitize', []).
value('$sanitize', jasmine.createSpy('$sanitize').andCallFake(angular.identity));
value('$sanitize', jasmine.createSpy('$sanitize').and.callFake(angular.identity));
beforeEach(module('errors'));
@@ -103,12 +103,12 @@ describe('errors', function() {
it('should pass the final string through `$sanitize`', function() {
$sanitize.reset();
$sanitize.calls.reset();
var input = 'start https://foo/bar?baz#qux end';
var output = errorLinkFilter(input);
expect($sanitize.callCount).toBe(1);
expect($sanitize).toHaveBeenCalledTimes(1);
expect($sanitize).toHaveBeenCalledWith(output);
});
});
@@ -123,7 +123,7 @@ describe('errors', function() {
beforeEach(module(function($provide) {
$provide.decorator('errorLinkFilter', function() {
errorLinkFilter = jasmine.createSpy('errorLinkFilter');
errorLinkFilter.andCallFake(angular.identity);
errorLinkFilter.and.callFake(angular.identity);
return errorLinkFilter;
});
@@ -142,7 +142,7 @@ describe('errors', function() {
it('should interpolate the contents against `$location.search()`', function() {
spyOn($location, 'search').andReturn({p0: 'foo', p1: 'bar'});
spyOn($location, 'search').and.returnValue({p0: 'foo', p1: 'bar'});
var elem = $compile('<span error-display="foo = {0}, bar = {1}"></span>')($rootScope);
expect(elem.html()).toBe('foo = foo, bar = bar');
@@ -150,10 +150,10 @@ describe('errors', function() {
it('should pass the interpolated text through `errorLinkFilter`', function() {
$location.search = jasmine.createSpy('search').andReturn({p0: 'foo'});
$location.search = jasmine.createSpy('search').and.returnValue({p0: 'foo'});
var elem = $compile('<span error-display="foo = {0}"></span>')($rootScope);
expect(errorLinkFilter.callCount).toBe(1);
expect(errorLinkFilter).toHaveBeenCalledTimes(1);
expect(errorLinkFilter).toHaveBeenCalledWith('foo = foo', '_blank');
});
+3 -3
View File
@@ -1,8 +1,8 @@
@ngdoc error
@name $compile:baddir
@fullName Invalid Directive Name
@fullName Invalid Directive/Component Name
@description
This error occurs when the name of a directive is not valid.
This error occurs when the name of a directive or component is not valid.
Directives must start with a lowercase character and must not contain leading or trailing whitespaces.
Directives and Components must start with a lowercase character and must not contain leading or trailing whitespaces.
+30
View File
@@ -0,0 +1,30 @@
@ngdoc error
@name $compile:infchng
@fullName Unstable `$onChanges` hooks
@description
This error occurs when the application's model becomes unstable because some `$onChanges` hooks are causing updates which then trigger
further calls to `$onChanges` that can never complete.
Angular detects this situation and prevents an infinite loop from causing the browser to become unresponsive.
For example, the situation can occur by setting up a `$onChanges()` hook which triggers an event on the component, which subsequently
triggers the component's bound inputs to be updated:
```html
<c1 prop="a" on-change="a = -a"></c1>
```
```js
function Controller1() {}
Controller1.$onChanges = function() {
this.onChange();
};
mod.component('c1', {
controller: Controller1,
bindings: {'prop': '<', onChange: '&'}
}
```
The maximum number of allowed iterations of the `$onChanges` hooks is controlled via TTL setting which can be configured via
{@link ng.$compileProvider#onChangesTtl `$compileProvider.onChangesTtl`}.
+1 -1
View File
@@ -43,7 +43,7 @@ well. Consider the following template:
```
<div class='container'>
<div class='wrapper>
<div class='wrapper'>
...
</div> <!-- wrapper -->
</div> <!-- container -->
+2
View File
@@ -430,10 +430,12 @@ You can prevent this unwanted behavior by adding CSS to the `.ng-animate` class
for the whole duration of an animation. Simply overwrite the transition / animation duration. In the
case of the spinner, this would be:
```css
.spinner.ng-animate {
transition: 0s none;
animation: 0s none;
}
```
If you do have CSS transitions / animations defined for the animation events, make sure they have higher priority
than any styles that are independent from ngAnimate.
+15 -15
View File
@@ -33,7 +33,7 @@ Here is a table of the main concepts used in the Component Router.
## Component-based Applications
It recommended to develop AngularJS applications as a hierarchy of Components. Each Component
It is recommended to develop AngularJS applications as a hierarchy of Components. Each Component
is an isolated part of the application, which is responsible for its own user interface and has
a well defined programmatic interface to the Component that contains it. Take a look at the
{@link guide/component component guide} for more information.
@@ -105,7 +105,7 @@ Here we have specified that the **Root Component** is the component directive wi
Remember to instantiate this **Root Component** in our `index.html` file.
```html
<my-app><my-app>
<my-app></my-app>
```
## Route Matching
@@ -124,9 +124,9 @@ This process continues until we run out of **Routing Components** or consume the
![Routed Components](img/guide/component-routes.svg)
In the previous diagram can see that the URL `/heros/2` has been matched against the `App`, `Heroes` and
In the previous diagram, we can see that the URL `/heros/4` has been matched against the `App`, `Heroes` and
`HeroDetail` **Routing Components**. The **Routers** for each of the **Routing Components** consumed a part
of the URL: "/", "/heroes" and "/2" respectively.
of the URL: "/", "/heroes" and "/4" respectively.
The result is that we end up with a hierarchy of **Routing Components** rendered in **Outlets**, via the
{@link ngOutlet} directive, in each **Routing Component's** template, as you can see in the following diagram.
@@ -462,7 +462,7 @@ to display list and detail views of Heroes and Crises.
## Install the libraries
It is simplest to use npm to install the **Component Router** module. For this guide we will also install
It is easier to use npm to install the **Component Router** module. For this guide we will also install
AngularJS itself via npm:
```bash
@@ -485,7 +485,7 @@ Just like any Angular application, we load the JavaScript files into our `index.
## Create the `app` module
In the app.js file, create the main application module `app` which depends upon the `ngComponentRouter`
In the app.js file, create the main application module `app` which depends on the `ngComponentRouter`
module, which is provided by the **Component Router** script.
```js
@@ -494,10 +494,10 @@ angular.module('app', ['ngComponentRouter'])
We must choose what **Location Mode** the **Router** should use. We are going to use HTML5 mode locations,
so that we will not have hash-based paths. We must rely on the browser to provide `pushState` support,
which is true of most modern browsers. See {@link $locationProvider#html5Mode} for more information.
which is true for most modern browsers. See {@link $locationProvider#html5Mode} for more information.
<div class="alert alert-info">
Using HTML5 mode means that we can have clean URLs for our application routes but it does require that our
Using HTML5 mode means that we can have clean URLs for our application routes. However, HTML5 mode does require that our
web server, which hosts the application, understands that it must respond with the index.html file for
requests to URLs that represent all our application routes. We are going to use the `lite-server` web server
to do this for us.
@@ -550,7 +550,7 @@ Bootstrap the Angular application and add the top level App Component.
# Implementing the AppComponent
In the previous section we created a single top level **App Component**. Let's now create some more
In the previous section we have created a single top level **App Component**. Let's now create some more
**Routing Components** and wire up **Route Config** for those. We start with a Heroes Feature, which
will display one of two views.
@@ -590,7 +590,7 @@ of this view will be rendered.
### ngLink
We have used the `ng-link` directive to create a link to navigate to the Heroes Component. By using this
directive we don't need to know what the actual URL will be. We can leave the Router to generate that for us.
directive we don't need to know what the actual URL will be. We can let the Router generate that for us.
We have included a link to the Crisis Center but have not included the `ng-link` directive as we have not yet
implemented the CrisisCenter component.
@@ -765,7 +765,7 @@ function HeroListComponent(heroService) {
Running the application should update the browser's location to `/heroes` and display the list of heroes
returned from the `heroService`.
By returning a promise for the list of heroes from `$routerOnActivate()` we can delay activation of the
By returning a promise for the list of heroes from `$routerOnActivate()` we can delay the activation of the
Route until the heroes have arrived successfully. This is similar to how a `resolve` works in {@link ngRoute}.
@@ -956,9 +956,9 @@ respectively.
**How do I prevent navigation from occurring?**
Each **Component** can provide the `$routerCanActivate` and `$routerCanDeactivate` **Lifecycle Hooks**. The
`$routerCanDeactivate` hook is an instance method on the **Component**. The `$routerCanActivate` hook is a
static method defined on either the **Component Definition Object** or the **Component's** constructor function.
Each **Component** can provide the `$canActivate` and `$routerCanDeactivate` **Lifecycle Hooks**. The
`$routerCanDeactivate` hook is an instance method on the **Component**. The `$canActivate` hook is used as a
static method defined on the **Component Definition Object**.
The **Router** will call these hooks to control navigation from one **Route** to another. Each of these hooks can
return a `boolean` or a Promise that will resolve to a `boolean`.
@@ -966,7 +966,7 @@ return a `boolean` or a Promise that will resolve to a `boolean`.
During a navigation, some **Components** will become inactive and some will become active. Before the navigation
can complete, all the **Components** must agree that they can be deactivated or activated, respectively.
The **Router** will call the `$routerCanDeactivate` and `$routerCanActivate` hooks, if they are provided. If any
The **Router** will call the `$routerCanDeactivate` and `$canActivate` hooks, if they are provided. If any
of the hooks resolve to `false` then the navigation is cancelled.
### Dialog Box Service
+25 -1
View File
@@ -29,7 +29,7 @@ and link functions are unavailable
Components can be registered using the `.component()` method of an Angular module (returned by {@link module `angular.module()`}). The method takes two arguments:
* The name of the Component (as string).
* The Component config object (note that, unlike the `.directive()` method, this method does **not** take a factory function.
* The Component config object. (Note that, unlike the `.directive()` method, this method does **not** take a factory function.)
<example name="heroComponentSimple" module="heroApp">
<file name="index.js">
@@ -147,6 +147,30 @@ components should follow a few simple conventions:
}
```
- **Components have a well-defined lifecycle**
Each component can implement "lifecycle hooks". These are methods that will be called at certain points in the life
of the component. The following hook methods can be implemented:
* `$onInit()` - Called on each controller after all the controllers on an element have been constructed and
had their bindings initialized (and before the pre &amp; post linking functions for the directives on
this element). This is a good place to put initialization code for your controller.
* `$onChanges(changesObj)` - Called whenever one-way bindings are updated. The `changesObj` is a hash whose keys
are the names of the bound properties that have changed, and the values are an object of the form
`{ currentValue: ..., previousValue: ... }`. Use this hook to trigger updates within a component such as
cloning the bound value to prevent accidental mutation of the outer value.
* `$onDestroy()` - Called on a controller when its containing scope is destroyed. Use this hook for releasing
external resources, watches and event handlers.
* `$postLink()` - Called after this controller's element and its children have been linked. Similar to the post-link
function this hook can be used to set up DOM event handlers and do direct DOM manipulation.
Note that child elements that contain `templateUrl` directives will not have been compiled and linked since
they are waiting for their template to load asynchronously and their own compilation and linking has been
suspended until that occurs.
This hook can be considered analogous to the `ngAfterViewInit` and `ngAfterContentInit` hooks in Angular 2.
Since the compilation process is rather different in Angular 1 there is no direct mapping and care should
be taken when upgrading.
By implementing these methods, you component can take part in its lifecycle.
- **An application is a tree of components:**
Ideally, the whole application should be a tree of components that implement clearly defined inputs
and outputs, and minimize two-way data binding. That way, it's easier to predict when data changes and what the state
+1 -1
View File
@@ -43,7 +43,7 @@ subsystem takes care of the rest.
<file name="script.js">
angular.
module('myServiceModule', []).
controller('MyController', ['$scope','notify', function ($scope, notify) {
controller('MyController', ['$scope', 'notify', function ($scope, notify) {
$scope.callNotify = function(msg) {
notify(msg);
};
+1842 -3358
View File
File diff suppressed because it is too large Load Diff
+4001 -6361
View File
File diff suppressed because it is too large Load Diff
+4 -3
View File
@@ -1,9 +1,9 @@
{
"name": "angularjs",
"license": "MIT",
"branchVersion": "^1.5.0-beta.2",
"branchVersion": "1.5.x",
"branchPattern": "1.5.*",
"distTag": "beta",
"distTag": "latest",
"repository": {
"type": "git",
"url": "https://github.com/angular/angular.js.git"
@@ -52,6 +52,7 @@
"gulp-sourcemaps": "^1.2.2",
"gulp-uglify": "^1.0.1",
"gulp-util": "^3.0.1",
"jasmine-core": "^2.4.0",
"jasmine-node": "^2.0.0",
"jasmine-reporters": "~1.0.1",
"jshint-stylish": "~1.0.0",
@@ -59,7 +60,7 @@
"karma-browserstack-launcher": "^0.1.8",
"karma-chrome-launcher": "^0.2.2",
"karma-firefox-launcher": "^0.1.7",
"karma-jasmine": "^0.1.6",
"karma-jasmine": "^0.3.7",
"karma-junit-reporter": "^0.3.8",
"karma-ng-scenario": "^0.1.0",
"karma-sauce-launcher": "^0.3.0",
+6 -6
View File
@@ -9,7 +9,7 @@ if (process.env.BROWSER_PROVIDER === 'browserstack') {
capabilitiesForBrowserStack({
browserName: 'chrome',
platform: 'MAC',
version: '34'
version: '49'
}),
capabilitiesForBrowserStack({
browserName: 'firefox',
@@ -18,7 +18,7 @@ if (process.env.BROWSER_PROVIDER === 'browserstack') {
capabilitiesForBrowserStack({
browserName: 'safari',
platform: 'MAC',
version: '7'
version: '9'
})
];
} else {
@@ -28,8 +28,8 @@ if (process.env.BROWSER_PROVIDER === 'browserstack') {
config.multiCapabilities = [
capabilitiesForSauceLabs({
browserName: 'chrome',
platform: 'OS X 10.9',
version: '34'
platform: 'OS X 10.11',
version: '48'
}),
capabilitiesForSauceLabs({
browserName: 'firefox',
@@ -37,8 +37,8 @@ if (process.env.BROWSER_PROVIDER === 'browserstack') {
}),
capabilitiesForSauceLabs({
browserName: 'safari',
platform: 'OS X 10.9',
version: '7'
platform: 'OS X 10.11',
version: '9'
})
];
}
+2 -2
View File
@@ -4,11 +4,11 @@ echo "#################################"
echo "#### Jenkins Build ############"
echo "#################################"
source scripts/jenkins/set-node-version.sh
# Enable tracing and exit on first failure
set -xe
scripts/jenkins/set-node-version.sh
# This is the default set of browsers to use on the CI server unless overridden via env variable
if [[ -z "$BROWSERS" ]]
then
+1 -1
View File
@@ -35,7 +35,7 @@ function init {
}
function build {
./set-node-version.sh
source ./set-node-version.sh
cd ../..
npm install -g grunt-cli
+1 -1
View File
@@ -4,4 +4,4 @@
source ~/.nvm/nvm.sh
# Use node.js at 4.2.x
nvm install 4.2
nvm install 4.4
+2 -2
View File
@@ -457,7 +457,7 @@ function identity($) {return $;}
identity.$inject = [];
function valueFn(value) {return function() {return value;};}
function valueFn(value) {return function valueRef() {return value;};}
function hasCustomToString(obj) {
return isFunction(obj.toString) && obj.toString !== toString;
@@ -819,7 +819,7 @@ function copy(source, destination) {
function copyRecurse(source, destination) {
var h = destination.$$hashKey;
var result, key;
var key;
if (isArray(source)) {
for (var i = 0, ii = source.length; i < ii; i++) {
destination.push(copyElement(source[i]));
+8 -10
View File
@@ -24,7 +24,6 @@
*/
function Browser(window, document, $log, $sniffer) {
var self = this,
rawDocument = document[0],
location = window.location,
history = window.history,
setTimeout = window.setTimeout,
@@ -87,7 +86,14 @@ function Browser(window, document, $log, $sniffer) {
var cachedState, lastHistoryState,
lastBrowserUrl = location.href,
baseElement = document.find('base'),
pendingLocation = null;
pendingLocation = null,
getCurrentState = !$sniffer.history ? noop : function getCurrentState() {
try {
return history.state;
} catch (e) {
// MSIE can reportedly throw when there is no state (UNCONFIRMED).
}
};
cacheState();
lastHistoryState = cachedState;
@@ -195,14 +201,6 @@ function Browser(window, document, $log, $sniffer) {
fireUrlChange();
}
function getCurrentState() {
try {
return history.state;
} catch (e) {
// MSIE can reportedly throw when there is no state (UNCONFIRMED).
}
}
// This variable should be used *only* inside the cacheState function.
var lastCachedState = null;
function cacheState() {
+158 -15
View File
@@ -293,9 +293,23 @@
* `true` if the specified slot contains content (i.e. one or more DOM nodes).
*
* The controller can provide the following methods that act as life-cycle hooks:
* * `$onInit` - Called on each controller after all the controllers on an element have been constructed and
* * `$onInit()` - Called on each controller after all the controllers on an element have been constructed and
* had their bindings initialized (and before the pre &amp; post linking functions for the directives on
* this element). This is a good place to put initialization code for your controller.
* * `$onChanges(changesObj)` - Called whenever one-way (`<`) or interpolation (`@`) bindings are updated. The
* `changesObj` is a hash whose keys are the names of the bound properties that have changed, and the values are an
* object of the form `{ currentValue: ..., previousValue: ... }`. Use this hook to trigger updates within a component
* such as cloning the bound value to prevent accidental mutation of the outer value.
* * `$onDestroy()` - Called on a controller when its containing scope is destroyed. Use this hook for releasing
* external resources, watches and event handlers. Note that components have their `$onDestroy()` hooks called in
* the same order as the `$scope.$broadcast` events are triggered, which is top down. This means that parent
* components will have their `$onDestroy()` hook called before child components.
* * `$postLink()` - Called after this controller's element and its children have been linked. Similar to the post-link
* function this hook can be used to set up DOM event handlers and do direct DOM manipulation.
* Note that child elements that contain `templateUrl` directives will not have been compiled and linked since
* they are waiting for their template to load asynchronously and their own compilation and linking has been
* suspended until that occurs.
*
*
* #### `require`
* Require another directive and inject its controller as the fourth argument to the linking function. The
@@ -928,11 +942,11 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
function assertValidDirectiveName(name) {
var letter = name.charAt(0);
if (!letter || letter !== lowercase(letter)) {
throw $compileMinErr('baddir', "Directive name '{0}' is invalid. The first character must be a lowercase letter", name);
throw $compileMinErr('baddir', "Directive/Component name '{0}' is invalid. The first character must be a lowercase letter", name);
}
if (name !== name.trim()) {
throw $compileMinErr('baddir',
"Directive name '{0}' is invalid. The name should not contain leading or trailing whitespaces",
"Directive/Component name '{0}' is invalid. The name should not contain leading or trailing whitespaces",
name);
}
}
@@ -1207,6 +1221,36 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
return debugInfoEnabled;
};
var TTL = 10;
/**
* @ngdoc method
* @name $compileProvider#onChangesTtl
* @description
*
* Sets the number of times `$onChanges` hooks can trigger new changes before giving up and
* assuming that the model is unstable.
*
* The current default is 10 iterations.
*
* In complex applications it's possible that dependencies between `$onChanges` hooks and bindings will result
* in several iterations of calls to these hooks. However if an application needs more than the default 10
* iterations to stabilize then you should investigate what is causing the model to continuously change during
* the `$onChanges` hook execution.
*
* Increasing the TTL could have performance implications, so you should not change it without proper justification.
*
* @param {number} limit The number of `$onChanges` hook iterations.
* @returns {number|object} the current limit (or `this` if called as a setter for chaining)
*/
this.onChangesTtl = function(value) {
if (arguments.length) {
TTL = value;
return this;
}
return TTL;
};
this.$get = [
'$injector', '$interpolate', '$exceptionHandler', '$templateRequest', '$parse',
'$controller', '$rootScope', '$sce', '$animate', '$$sanitizeUri',
@@ -1215,7 +1259,37 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
var SIMPLE_ATTR_NAME = /^\w/;
var specialAttrHolder = document.createElement('div');
var Attributes = function(element, attributesToCopy) {
var onChangesTtl = TTL;
// The onChanges hooks should all be run together in a single digest
// When changes occur, the call to trigger their hooks will be added to this queue
var onChangesQueue;
// This function is called in a $$postDigest to trigger all the onChanges hooks in a single digest
function flushOnChangesQueue() {
try {
if (!(--onChangesTtl)) {
// We have hit the TTL limit so reset everything
onChangesQueue = undefined;
throw $compileMinErr('infchng', '{0} $onChanges() iterations reached. Aborting!\n', TTL);
}
// We must run this hook in an apply since the $$postDigest runs outside apply
$rootScope.$apply(function() {
for (var i = 0, ii = onChangesQueue.length; i < ii; ++i) {
onChangesQueue[i]();
}
// Reset the queue to trigger a new schedule next time there is a change
onChangesQueue = undefined;
});
} finally {
onChangesTtl++;
}
}
function Attributes(element, attributesToCopy) {
if (attributesToCopy) {
var keys = Object.keys(attributesToCopy);
var i, l, key;
@@ -1229,7 +1303,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
}
this.$$element = element;
};
}
Attributes.prototype = {
/**
@@ -1529,6 +1603,19 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
// modify it.
$compileNodes = jqLite($compileNodes);
}
var NOT_EMPTY = /\S+/;
// We can not compile top level text elements since text nodes can be merged and we will
// not be able to attach scope data to them, so we will wrap them in <span>
for (var i = 0, len = $compileNodes.length; i < len; i++) {
var domNode = $compileNodes[i];
if (domNode.nodeType === NODE_TYPE_TEXT && domNode.nodeValue.match(NOT_EMPTY) /* non-empty */) {
jqLiteWrapNode(domNode, $compileNodes[i] = document.createElement('span'));
}
}
var compositeLinkFn =
compileNodes($compileNodes, transcludeFn, $compileNodes,
maxPriority, ignoreDirective, previousCompileContext);
@@ -1718,8 +1805,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
}
function createBoundTranscludeFn(scope, transcludeFn, previousBoundTranscludeFn) {
var boundTranscludeFn = function(transcludedScope, cloneFn, controllers, futureParentElement, containingScope) {
function boundTranscludeFn(transcludedScope, cloneFn, controllers, futureParentElement, containingScope) {
if (!transcludedScope) {
transcludedScope = scope.$new(false, containingScope);
@@ -1731,7 +1817,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
transcludeControllers: controllers,
futureParentElement: futureParentElement
});
};
}
// We need to attach the transclusion slots onto the `boundTranscludeFn`
// so that they are available inside the `controllersBoundTransclude` function
@@ -1896,7 +1982,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
* @returns {Function}
*/
function groupElementsLinkFnWrapper(linkFn, attrStart, attrEnd) {
return function(scope, element, attrs, controllers, transcludeFn) {
return function groupedElementsLink(scope, element, attrs, controllers, transcludeFn) {
element = groupScan(element[0], attrStart, attrEnd);
return linkFn(scope, element, attrs, controllers, transcludeFn);
};
@@ -1919,7 +2005,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
if (eager) {
return compile($compileNodes, transcludeFn, maxPriority, ignoreDirective, previousCompileContext);
}
return function() {
return function lazyCompilation() {
if (!compiled) {
compiled = compile($compileNodes, transcludeFn, maxPriority, ignoreDirective, previousCompileContext);
@@ -2068,6 +2154,17 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
compileNode = $compileNode[0];
replaceWith(jqCollection, sliceArgs($template), compileNode);
// Support: Chrome < 50
// https://github.com/angular/angular.js/issues/14041
// In the versions of V8 prior to Chrome 50, the document fragment that is created
// in the `replaceWith` function is improperly garbage collected despite still
// being referenced by the `parentNode` property of all of the child nodes. By adding
// a reference to the fragment via a different property, we can avoid that incorrect
// behavior.
// TODO: remove this line after Chrome 50 has been released
$template[0].$$parentNode = $template[0].parentNode;
childTranscludeFn = compilationGenerator(mightHaveMultipleTransclusionError, $template, transcludeFn, terminalPriority,
replaceDirective && replaceDirective.name, {
// Don't pass in:
@@ -2350,10 +2447,16 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
}
});
// Trigger the `$onInit` method on all controllers that have one
// Handle the init and destroy lifecycle hooks on all controllers that have them
forEach(elementControllers, function(controller) {
if (isFunction(controller.instance.$onInit)) {
controller.instance.$onInit();
var controllerInstance = controller.instance;
if (isFunction(controllerInstance.$onInit)) {
controllerInstance.$onInit();
}
if (isFunction(controllerInstance.$onDestroy)) {
controllerScope.$on('$destroy', function callOnDestroyHook() {
controllerInstance.$onDestroy();
});
}
});
@@ -2390,6 +2493,14 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
);
}
// Trigger $postLink lifecycle hooks
forEach(elementControllers, function(controller) {
var controllerInstance = controller.instance;
if (isFunction(controllerInstance.$postLink)) {
controllerInstance.$postLink();
}
});
// This is the function that is injected as `$transclude`.
// Note: all arguments are optional!
function controllersBoundTransclude(scope, cloneAttachFn, futureParentElement, slotName) {
@@ -2985,7 +3096,8 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
// only occurs for isolate scopes and new scopes with controllerAs.
function initializeDirectiveBindings(scope, attrs, destination, bindings, directive) {
var removeWatchCollection = [];
forEach(bindings, function(definition, scopeName) {
var changes;
forEach(bindings, function initializeBinding(definition, scopeName) {
var attrName = definition.attrName,
optional = definition.optional,
mode = definition.mode, // @, =, or &
@@ -3000,6 +3112,8 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
}
attrs.$observe(attrName, function(value) {
if (isString(value)) {
var oldValue = destination[scopeName];
recordChanges(scopeName, value, oldValue);
destination[scopeName] = value;
}
});
@@ -3027,7 +3141,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
if (parentGet.literal) {
compare = equals;
} else {
compare = function(a, b) { return a === b || (a !== a && b !== b); };
compare = function simpleCompare(a, b) { return a === b || (a !== a && b !== b); };
}
parentSet = parentGet.assign || function() {
// reset the change, or we will throw this exception on every $digest
@@ -3071,6 +3185,8 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
destination[scopeName] = parentGet(scope);
removeWatch = scope.$watch(parentGet, function parentValueWatchAction(newParentValue) {
var oldValue = destination[scopeName];
recordChanges(scopeName, newParentValue, oldValue);
destination[scopeName] = newParentValue;
}, parentGet.literal);
@@ -3091,6 +3207,33 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
}
});
function recordChanges(key, currentValue, previousValue) {
if (isFunction(destination.$onChanges) && currentValue !== previousValue) {
// If we have not already scheduled the top level onChangesQueue handler then do so now
if (!onChangesQueue) {
scope.$$postDigest(flushOnChangesQueue);
onChangesQueue = [];
}
// If we have not already queued a trigger of onChanges for this controller then do so now
if (!changes) {
changes = {};
onChangesQueue.push(triggerOnChangesHook);
}
// If the has been a change on this property already then we need to reuse the previous value
if (changes[key]) {
previousValue = changes[key].previousValue;
}
// Store this change
changes[key] = {previousValue: previousValue, currentValue: currentValue};
}
}
function triggerOnChangesHook() {
destination.$onChanges(changes);
// Now clear the changes so that we schedule onChanges when more changes arrive
changes = undefined;
}
return removeWatchCollection.length && function removeWatches() {
for (var i = 0, ii = removeWatchCollection.length; i < ii; ++i) {
removeWatchCollection[i]();
+2 -2
View File
@@ -92,7 +92,7 @@ function $ControllerProvider() {
* It's just a simple call to {@link auto.$injector $injector}, but extracted into
* a service, so that one can override this service with [BC version](https://gist.github.com/1649788).
*/
return function(expression, locals, later, ident) {
return function $controller(expression, locals, later, ident) {
// PRIVATE API:
// param `later` --- indicates that the controller's constructor is invoked at a later time.
// If true, $controller will allocate the object with the correct
@@ -143,7 +143,7 @@ function $ControllerProvider() {
}
var instantiate;
return instantiate = extend(function() {
return instantiate = extend(function $controllerInit() {
var result = $injector.invoke(expression, instance, locals, constructor);
if (result !== instance && (isObject(result) || isFunction(result))) {
instance = result;
+2 -2
View File
@@ -393,7 +393,7 @@ var inputType = {
}]);
</script>
<form name="myForm" ng-controller="DateController as dateCtrl">
<label for="exampleInput">Pick a between 8am and 5pm:</label>
<label for="exampleInput">Pick a time between 8am and 5pm:</label>
<input type="time" id="exampleInput" name="input" ng-model="example.value"
placeholder="HH:mm:ss" min="08:00:00" max="17:00:00" required />
<div role="alert">
@@ -1114,7 +1114,7 @@ function baseInputType(scope, element, attr, ctrl, $sniffer, $browser) {
if (!$sniffer.android) {
var composing = false;
element.on('compositionstart', function(data) {
element.on('compositionstart', function() {
composing = true;
});
+4 -4
View File
@@ -265,9 +265,9 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
};
ngModelSet = function($scope, newValue) {
if (isFunction(parsedNgModel($scope))) {
invokeModelSetter($scope, {$$$p: ctrl.$modelValue});
invokeModelSetter($scope, {$$$p: newValue});
} else {
parsedNgModelAssign($scope, ctrl.$modelValue);
parsedNgModelAssign($scope, newValue);
}
};
} else if (!parsedNgModel.assign) {
@@ -644,7 +644,7 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
setValidity(name, undefined);
validatorPromises.push(promise.then(function() {
setValidity(name, true);
}, function(error) {
}, function() {
allValid = false;
setValidity(name, false);
}));
@@ -1118,7 +1118,7 @@ var ngModelDirective = ['$rootScope', function($rootScope) {
});
}
element.on('blur', function(ev) {
element.on('blur', function() {
if (modelCtrl.$touched) return;
if ($rootScope.$$phase) {
+2 -2
View File
@@ -342,8 +342,8 @@ var ngOptionsDirective = ['$compile', '$parse', function($compile, $parse) {
var key = (optionValues === optionValuesKeys) ? index : optionValuesKeys[index];
var value = optionValues[key];
var locals = getLocals(optionValues[key], key);
var selectValue = getTrackByValueFn(optionValues[key], locals);
var locals = getLocals(value, key);
var selectValue = getTrackByValueFn(value, locals);
watchedArray.push(selectValue);
// Only need to watch the displayFn if there is a specific label expression
+1 -1
View File
@@ -47,7 +47,7 @@
* <div ng-controller="ExampleController">
* <input ng-model="title" aria-label="title"> <br/>
* <textarea ng-model="text" aria-label="text"></textarea> <br/>
* <pane title="{{title}}"><span>{{text}}</span></pane>
* <pane title="{{title}}">{{text}}</pane>
* </div>
* </file>
* <file name="protractor.js" type="protractor">
+1 -1
View File
@@ -20,7 +20,7 @@ function chromeHack(optionElement) {
* added `<option>` elements, perhaps by an `ngRepeat` directive.
*/
var SelectController =
['$element', '$scope', '$attrs', function($element, $scope, $attrs) {
['$element', '$scope', function($element, $scope) {
var self = this,
optionsMap = new HashMap();
+1 -1
View File
@@ -323,7 +323,7 @@ function formatNumber(number, pattern, groupSep, decimalSep, fractionSize) {
// format the integer digits with grouping separators
var groups = [];
if (digits.length > pattern.lgSize) {
if (digits.length >= pattern.lgSize) {
groups.unshift(digits.splice(-pattern.lgSize).join(''));
}
while (digits.length > pattern.gSize) {
+1 -1
View File
@@ -47,7 +47,7 @@ function $HttpParamSerializerProvider() {
forEachSorted(params, function(value, key) {
if (value === null || isUndefined(value)) return;
if (isArray(value)) {
forEach(value, function(v, k) {
forEach(value, function(v) {
parts.push(encodeUriQuery(key) + '=' + encodeUriQuery(serializeValue(v)));
});
} else {
+1 -1
View File
@@ -1449,7 +1449,7 @@ ASTInterpreter.prototype = {
return context ? {value: locals} : locals;
};
case AST.NGValueParameter:
return function(scope, locals, assign, inputs) {
return function(scope, locals, assign) {
return context ? {value: assign} : assign;
};
}
+3 -3
View File
@@ -13,15 +13,15 @@
* [Kris Kowal's Q](https://github.com/kriskowal/q).
*
* $q can be used in two fashions --- one which is more similar to Kris Kowal's Q or jQuery's Deferred
* implementations, and the other which resembles ES6 promises to some degree.
* implementations, and the other which resembles ES6 (ES2015) promises to some degree.
*
* # $q constructor
*
* The streamlined ES6 style promise is essentially just using $q as a constructor which takes a `resolver`
* function as the first argument. This is similar to the native Promise implementation from ES6 Harmony,
* function as the first argument. This is similar to the native Promise implementation from ES6,
* see [MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise).
*
* While the constructor-style use is supported, not all of the supporting methods from ES6 Harmony promises are
* While the constructor-style use is supported, not all of the supporting methods from ES6 promises are
* available yet.
*
* It can be used like so:
+1 -1
View File
@@ -750,7 +750,7 @@ function $RootScopeProvider() {
dirty, ttl = TTL,
next, current, target = this,
watchLog = [],
logIdx, logMsg, asyncTask;
logIdx, asyncTask;
beginPhase('$digest');
// Check for changes to browser url that happened in sync before the call to $digest
+5 -1
View File
@@ -17,6 +17,10 @@
function $SnifferProvider() {
this.$get = ['$window', '$document', function($window, $document) {
var eventSupport = {},
// Chrome Packaged Apps are not allowed to access `history.pushState`. They can be detected by
// the presence of `chrome.app.runtime` (see https://developer.chrome.com/apps/api_index)
isChromePackagedApp = $window.chrome && $window.chrome.app && $window.chrome.app.runtime,
hasHistoryPushState = !isChromePackagedApp && $window.history && $window.history.pushState,
android =
toInt((/android (\d+)/.exec(lowercase(($window.navigator || {}).userAgent)) || [])[1]),
boxee = /Boxee/i.test(($window.navigator || {}).userAgent),
@@ -61,7 +65,7 @@ function $SnifferProvider() {
// so let's not use the history API also
// We are purposefully using `!(android < 4)` to cover the case when `android` is undefined
// jshint -W018
history: !!($window.history && $window.history.pushState && !(android < 4) && !boxee),
history: !!(hasHistoryPushState && !(android < 4) && !boxee),
// jshint +W018
hasEvent: function(event) {
// IE9 implements 'input' event it's so fubared that we rather pretend that it doesn't have
+2 -2
View File
@@ -1,6 +1,6 @@
'use strict';
var $compileMinErr = minErr('$compile');
var $templateRequestMinErr = minErr('$compile');
/**
* @ngdoc provider
@@ -96,7 +96,7 @@ function $TemplateRequestProvider() {
function handleError(resp) {
if (!ignoreRequestError) {
throw $compileMinErr('tpload', 'Failed to load template: {0} (HTTP status: {1} {2})',
throw $templateRequestMinErr('tpload', 'Failed to load template: {0} (HTTP status: {1} {2})',
tpl, resp.status, resp.statusText);
}
return $q.reject(resp);
+13 -1
View File
@@ -82,6 +82,11 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) {
});
rules.cancel.push(function(element, newAnimation, currentAnimation) {
// cancel the animation if classes added / removed in both animation cancel each other out,
// but only if the current animation isn't structural
if (currentAnimation.structural) return false;
var nA = newAnimation.addClass;
var nR = newAnimation.removeClass;
var cA = currentAnimation.addClass;
@@ -194,7 +199,7 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) {
return matches;
}
return {
var $animate = {
on: function(event, container, callback) {
var node = extractElementNode(container);
callbackRegistry[event] = callbackRegistry[event] || [];
@@ -202,6 +207,11 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) {
node: node,
callback: callback
});
// Remove the callback when the element is removed from the DOM
jqLite(container).on('$destroy', function() {
$animate.off(event, container, callback);
});
},
off: function(event, container, callback) {
@@ -269,6 +279,8 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) {
}
};
return $animate;
function queueAnimation(element, event, initialOptions) {
// we always make a copy of the options since
// there should never be any side effects on
+14 -3
View File
@@ -410,6 +410,13 @@ angular.module('ngMessages', [])
$scope.$watchCollection($attrs.ngMessages || $attrs['for'], ctrl.render);
// If the element is destroyed, proactively destroy all the currently visible messages
$element.on('$destroy', function() {
forEach(messages, function(item) {
item.message.detach();
});
});
this.reRender = function() {
if (!renderLater) {
renderLater = true;
@@ -444,6 +451,7 @@ angular.module('ngMessages', [])
function findPreviousMessage(parent, comment) {
var prevNode = comment;
var parentLookup = [];
while (prevNode && prevNode !== parent) {
var prevKey = prevNode.$$ngMessageNode;
if (prevKey && prevKey.length) {
@@ -455,8 +463,11 @@ angular.module('ngMessages', [])
if (prevNode.childNodes.length && parentLookup.indexOf(prevNode) == -1) {
parentLookup.push(prevNode);
prevNode = prevNode.childNodes[prevNode.childNodes.length - 1];
} else if (prevNode.previousSibling) {
prevNode = prevNode.previousSibling;
} else {
prevNode = prevNode.previousSibling || prevNode.parentNode;
prevNode = prevNode.parentNode;
parentLookup.push(prevNode);
}
}
}
@@ -669,8 +680,8 @@ function ngMessageDirectiveFactory() {
// when we are destroying the node later.
var $$attachId = currentElement.$$attachId = ngMessagesCtrl.getAttachId();
// in the event that the parent element is destroyed
// by any other structural directive then it's time
// in the event that the element or a parent element is destroyed
// by another structural directive then it's time
// to deregister the message from the controller
currentElement.on('$destroy', function() {
if (currentElement && currentElement.$$attachId === $$attachId) {
+1 -1
View File
@@ -64,7 +64,7 @@ angular.scenario.Application.prototype.navigateTo = function(url, loadFn, errorF
self.context.find('#test-frames').append('<iframe>');
frame = self.getFrame_();
frame.load(function() {
frame.on('load', function() {
frame.off();
try {
var $window = self.getWindow_();
+5 -4
View File
@@ -131,9 +131,9 @@
/* jasmine / karma */
"it": false,
"iit": false,
"fit": false,
"describe": false,
"ddescribe": false,
"fdescribe": false,
"beforeEach": false,
"afterEach": false,
"expect": false,
@@ -144,7 +144,7 @@
"runs": false,
"dump": false,
"they": false,
"tthey": false,
"fthey": false,
"xthey": false,
"assertCompareNodes": false,
@@ -168,6 +168,7 @@
"createMockStyleSheet": false,
"browserSupportsCssAnimations": false,
"browserTrigger": false,
"jqLiteCacheSize": false
"jqLiteCacheSize": false,
"createAsync": false
}
}
+18 -18
View File
@@ -983,7 +983,7 @@ describe('angular', function() {
describe('csp', function() {
function mockCspElement(cspAttrName, cspAttrValue) {
return spyOn(document, 'querySelector').andCallFake(function(selector) {
return spyOn(document, 'querySelector').and.callFake(function(selector) {
if (selector == '[' + cspAttrName + ']') {
var html = '<div ' + cspAttrName + (cspAttrValue ? ('="' + cspAttrValue + '" ') : '') + '></div>';
return jqLite(html)[0];
@@ -1009,7 +1009,7 @@ describe('angular', function() {
it('should return true for noUnsafeEval if eval causes a CSP security policy error', function() {
window.Function.andCallFake(function() { throw new Error('CSP test'); });
window.Function.and.callFake(function() { throw new Error('CSP test'); });
expect(csp()).toEqual({ noUnsafeEval: true, noInlineStyle: false });
expect(window.Function).toHaveBeenCalledWith('');
});
@@ -1073,7 +1073,7 @@ describe('angular', function() {
it('should return empty string when jq is enabled manually via [ng-jq] with empty string', function() {
element.setAttribute('ng-jq', '');
spyOn(document, 'querySelector').andCallFake(function(selector) {
spyOn(document, 'querySelector').and.callFake(function(selector) {
if (selector === '[ng-jq]') return element;
});
expect(jq()).toBe('');
@@ -1081,7 +1081,7 @@ describe('angular', function() {
it('should return empty string when jq is enabled manually via [data-ng-jq] with empty string', function() {
element.setAttribute('data-ng-jq', '');
spyOn(document, 'querySelector').andCallFake(function(selector) {
spyOn(document, 'querySelector').and.callFake(function(selector) {
if (selector === '[data-ng-jq]') return element;
});
expect(jq()).toBe('');
@@ -1090,7 +1090,7 @@ describe('angular', function() {
it('should return empty string when jq is enabled manually via [x-ng-jq] with empty string', function() {
element.setAttribute('x-ng-jq', '');
spyOn(document, 'querySelector').andCallFake(function(selector) {
spyOn(document, 'querySelector').and.callFake(function(selector) {
if (selector === '[x-ng-jq]') return element;
});
expect(jq()).toBe('');
@@ -1099,7 +1099,7 @@ describe('angular', function() {
it('should return empty string when jq is enabled manually via [ng:jq] with empty string', function() {
element.setAttribute('ng:jq', '');
spyOn(document, 'querySelector').andCallFake(function(selector) {
spyOn(document, 'querySelector').and.callFake(function(selector) {
if (selector === '[ng\\:jq]') return element;
});
expect(jq()).toBe('');
@@ -1108,7 +1108,7 @@ describe('angular', function() {
it('should return "jQuery" when jq is enabled manually via [ng-jq] with value "jQuery"', function() {
element.setAttribute('ng-jq', 'jQuery');
spyOn(document, 'querySelector').andCallFake(function(selector) {
spyOn(document, 'querySelector').and.callFake(function(selector) {
if (selector === '[ng-jq]') return element;
});
expect(jq()).toBe('jQuery');
@@ -1117,7 +1117,7 @@ describe('angular', function() {
it('should return "jQuery" when jq is enabled manually via [data-ng-jq] with value "jQuery"', function() {
element.setAttribute('data-ng-jq', 'jQuery');
spyOn(document, 'querySelector').andCallFake(function(selector) {
spyOn(document, 'querySelector').and.callFake(function(selector) {
if (selector === '[data-ng-jq]') return element;
});
expect(jq()).toBe('jQuery');
@@ -1126,7 +1126,7 @@ describe('angular', function() {
it('should return "jQuery" when jq is enabled manually via [x-ng-jq] with value "jQuery"', function() {
element.setAttribute('x-ng-jq', 'jQuery');
spyOn(document, 'querySelector').andCallFake(function(selector) {
spyOn(document, 'querySelector').and.callFake(function(selector) {
if (selector === '[x-ng-jq]') return element;
});
expect(jq()).toBe('jQuery');
@@ -1135,7 +1135,7 @@ describe('angular', function() {
it('should return "jQuery" when jq is enabled manually via [ng:jq] with value "jQuery"', function() {
element.setAttribute('ng:jq', 'jQuery');
spyOn(document, 'querySelector').andCallFake(function(selector) {
spyOn(document, 'querySelector').and.callFake(function(selector) {
if (selector === '[ng\\:jq]') return element;
});
expect(jq()).toBe('jQuery');
@@ -1636,7 +1636,7 @@ describe('angular', function() {
expect(function() {
angularInit(appElement, angular.bootstrap);
}).toThrowMatching(
}).toThrowError(
new RegExp('\\[\\$injector:modulerr] Failed to instantiate module doesntexist due to:\\n' +
'.*\\[\\$injector:nomod] Module \'doesntexist\' is not available! You either ' +
'misspelled the module name or forgot to load it\\.')
@@ -1650,7 +1650,7 @@ describe('angular', function() {
expect(function() {
angular.bootstrap(element);
}).toThrowMatching(
}).toThrowError(
/\[ng:btstrpd\] App Already Bootstrapped with this Element '&lt;div class="?ng\-scope"?( ng[0-9]+="?[0-9]+"?)?&gt;'/i
);
@@ -1662,7 +1662,7 @@ describe('angular', function() {
angular.bootstrap(document.getElementsByTagName('html')[0]);
expect(function() {
angular.bootstrap(document);
}).toThrowMatching(
}).toThrowError(
/\[ng:btstrpd\] App Already Bootstrapped with this Element 'document'/i
);
@@ -1671,11 +1671,11 @@ describe('angular', function() {
it('should bootstrap in strict mode when ng-strict-di attribute is specified', function() {
bootstrapSpy = spyOn(angular, 'bootstrap').andCallThrough();
bootstrapSpy = spyOn(angular, 'bootstrap').and.callThrough();
var appElement = jqLite('<div ng-app="" ng-strict-di></div>');
angularInit(jqLite('<div></div>').append(appElement[0])[0], bootstrapSpy);
expect(bootstrapSpy).toHaveBeenCalledOnce();
expect(bootstrapSpy.mostRecentCall.args[2].strictDi).toBe(true);
expect(bootstrapSpy.calls.mostRecent().args[2].strictDi).toBe(true);
var injector = appElement.injector();
function testFactory($rootScope) {}
@@ -1863,7 +1863,7 @@ describe('angular', function() {
expect(function() {
angular.bootstrap(element, ['doesntexist']);
}).toThrowMatching(
}).toThrowError(
new RegExp('\\[\\$injector:modulerr\\] Failed to instantiate module doesntexist due to:\\n' +
'.*\\[\\$injector:nomod\\] Module \'doesntexist\' is not available! You either ' +
'misspelled the module name or forgot to load it\\.'));
@@ -1997,7 +1997,7 @@ describe('angular', function() {
describe('fromJson', function() {
it('should delegate to JSON.parse', function() {
var spy = spyOn(JSON, 'parse').andCallThrough();
var spy = spyOn(JSON, 'parse').and.callThrough();
expect(fromJson('{}')).toEqual({});
expect(spy).toHaveBeenCalled();
@@ -2008,7 +2008,7 @@ describe('angular', function() {
describe('toJson', function() {
it('should delegate to JSON.stringify', function() {
var spy = spyOn(JSON, 'stringify').andCallThrough();
var spy = spyOn(JSON, 'stringify').and.callThrough();
expect(toJson({})).toEqual('{}');
expect(spy).toHaveBeenCalled();
+1 -1
View File
@@ -186,7 +186,7 @@ describe('Binder', function() {
$rootScope.error['throw'] = function() { return 'X';};
$rootScope.$apply();
expect(errorLogs.length).toMatch(0);
expect(errorLogs.length).toMatch('0');
});
});
+7 -7
View File
@@ -84,14 +84,14 @@ describe('injector', function() {
});
it('should provide the caller name if given', function(done) {
it('should provide the caller name if given', function() {
expect(function() {
injector.get('idontexist', 'callerName');
}).toThrowMinErr("$injector", "unpr", "Unknown provider: idontexistProvider <- idontexist <- callerName");
});
it('should provide the caller name for controllers', function(done) {
it('should provide the caller name for controllers', function() {
controllerProvider.register('myCtrl', function(idontexist) {});
var $controller = injector.get('$controller');
expect(function() {
@@ -282,9 +282,9 @@ describe('injector', function() {
if (support.classes) {
it('should be possible to instantiate ES6 classes', function() {
providers('a', function() { return 'a-value'; });
var clazz = eval('(class { constructor(a) { this.a = a; } aVal() { return this.a; } })');
var instance = injector.instantiate(clazz);
expect(instance).toEqual({a: 'a-value'});
var Clazz = eval('(class { constructor(a) { this.a = a; } aVal() { return this.a; } })');
var instance = injector.instantiate(Clazz);
expect(instance).toEqual(new Clazz('a-value'));
expect(instance.aVal()).toEqual('a-value');
});
}
@@ -294,7 +294,7 @@ describe('injector', function() {
it('should publish annotate API', function() {
expect(angular.mock.$$annotate).toBe(annotate);
spyOn(angular.mock, '$$annotate').andCallThrough();
spyOn(angular.mock, '$$annotate').and.callThrough();
function fn() {}
injector.annotate(fn);
expect(angular.mock.$$annotate).toHaveBeenCalledWith(fn);
@@ -1012,7 +1012,7 @@ describe('injector', function() {
createInjector([function($provide) {
$provide.value('name', 'angular');
}, instanceLookupInModule]);
}).toThrowMatching(/\[\$injector:unpr] Unknown provider: name/);
}).toThrowError(/\[\$injector:unpr] Unknown provider: name/);
});
});
});
+1 -1
View File
@@ -1,3 +1,3 @@
{
"node": true
}
}
+281 -203
View File
@@ -4,24 +4,31 @@ beforeEach(function() {
function cssMatcher(presentClasses, absentClasses) {
return function() {
var element = angular.element(this.actual);
var present = true;
var absent = false;
return {
compare: function(actual) {
var element = angular.element(actual);
var present = true;
var absent = false;
angular.forEach(presentClasses.split(' '), function(className) {
present = present && element.hasClass(className);
});
angular.forEach(presentClasses.split(' '), function(className) {
present = present && element.hasClass(className);
});
angular.forEach(absentClasses.split(' '), function(className) {
absent = absent || element.hasClass(className);
});
angular.forEach(absentClasses.split(' '), function(className) {
absent = absent || element.hasClass(className);
});
this.message = function() {
return "Expected to have " + presentClasses +
(absentClasses ? (" and not have " + absentClasses + "") : "") +
" but had " + element[0].className + ".";
var message = function() {
return "Expected to have " + presentClasses +
(absentClasses ? (" and not have " + absentClasses + "") : "") +
" but had " + element[0].className + ".";
};
return {
pass: present && !absent,
message: message
};
}
};
return present && !absent;
};
}
@@ -36,7 +43,7 @@ beforeEach(function() {
return hidden;
}
this.addMatchers({
jasmine.addMatchers({
toBeEmpty: cssMatcher('ng-empty', 'ng-not-empty'),
toBeNotEmpty: cssMatcher('ng-not-empty', 'ng-empty'),
toBeInvalid: cssMatcher('ng-invalid', 'ng-valid'),
@@ -46,236 +53,307 @@ beforeEach(function() {
toBeUntouched: cssMatcher('ng-untouched', 'ng-touched'),
toBeTouched: cssMatcher('ng-touched', 'ng-untouched'),
toBeAPromise: function() {
this.message = valueFn(
"Expected object " + (this.isNot ? "not " : "") + "to be a promise");
return isPromiseLike(this.actual);
return {
compare: generateCompare(false),
negativeCompare: generateCompare(true)
};
function generateCompare(isNot) {
return function(actual) {
var message = valueFn(
"Expected object " + (isNot ? "not " : "") + "to be a promise");
return { pass: isPromiseLike(actual), message: message };
};
}
},
toBeShown: function() {
this.message = valueFn(
"Expected element " + (this.isNot ? "" : "not ") + "to have 'ng-hide' class");
return !isNgElementHidden(this.actual);
return {
compare: generateCompare(false),
negativeCompare: generateCompare(true)
};
function generateCompare(isNot) {
return function(actual) {
var message = valueFn("Expected element " + (isNot ? "" : "not ") + "to have 'ng-hide' class");
var pass = !isNgElementHidden(actual);
if (isNot) {
pass = !pass;
}
return { pass: pass, message: message };
};
}
},
toBeHidden: function() {
this.message = valueFn(
"Expected element " + (this.isNot ? "not " : "") + "to have 'ng-hide' class");
return isNgElementHidden(this.actual);
},
toEqual: function(expected) {
if (this.actual && this.actual.$$log) {
this.actual = (typeof expected === 'string')
? this.actual.toString()
: this.actual.toArray();
return {
compare: generateCompare(false),
negativeCompare: generateCompare(true)
};
function generateCompare(isNot) {
return function(actual) {
var message = valueFn("Expected element " + (isNot ? "not " : "") + "to have 'ng-hide' class");
var pass = isNgElementHidden(actual);
if (isNot) {
pass = !pass;
}
return { pass: pass, message: message };
};
}
return jasmine.Matchers.prototype.toEqual.call(this, expected);
},
toEqualData: function(expected) {
return angular.equals(this.actual, expected);
},
toEqualError: function(message) {
this.message = function() {
var expected;
if (this.actual.message && this.actual.name == 'Error') {
expected = angular.toJson(this.actual.message);
} else {
expected = angular.toJson(this.actual);
toEqual: function(util) {
return {
compare: function(actual, expected) {
if (actual && actual.$$log) {
actual = (typeof expected === 'string')
? actual.toString()
: actual.toArray();
}
return {
pass: util.equals(actual, expected, [DOMTester])
};
}
return "Expected " + expected + " to be an Error with message " + angular.toJson(message);
};
return this.actual.name == 'Error' && this.actual.message == message;
function DOMTester(a, b) {
if (a && b && a.nodeType > 0 && b.nodeType > 0) {
return a === b;
}
}
},
toMatchError: function(messageRegexp) {
this.message = function() {
var expected;
if (this.actual.message && this.actual.name == 'Error') {
expected = angular.toJson(this.actual.message);
} else {
expected = angular.toJson(this.actual);
toEqualData: function() {
return {
compare: function(actual, expected) {
return { pass: angular.equals(actual, expected) };
}
return "Expected " + expected + " to match an Error with message " + angular.toJson(messageRegexp);
};
return this.actual.name == 'Error' && messageRegexp.test(this.actual.message);
},
toHaveBeenCalledOnce: function() {
if (arguments.length > 0) {
throw new Error('toHaveBeenCalledOnce does not take arguments, use toHaveBeenCalledWith');
}
if (!jasmine.isSpy(this.actual)) {
throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.');
}
this.message = function() {
var msg = 'Expected spy ' + this.actual.identity + ' to have been called once, but was ',
count = this.actual.callCount;
return [
count === 0 ? msg + 'never called.' :
msg + 'called ' + count + ' times.',
msg.replace('to have', 'not to have') + 'called once.'
];
};
return this.actual.callCount == 1;
},
toHaveBeenCalledOnceWith: function() {
var expectedArgs = jasmine.util.argsToArray(arguments);
if (!jasmine.isSpy(this.actual)) {
throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.');
}
this.message = function() {
if (this.actual.callCount != 1) {
if (this.actual.callCount === 0) {
return [
'Expected spy ' + this.actual.identity + ' to have been called once with ' +
jasmine.pp(expectedArgs) + ' but it was never called.',
'Expected spy ' + this.actual.identity + ' not to have been called with ' +
jasmine.pp(expectedArgs) + ' but it was.'
];
return {
compare: function(actual) {
if (arguments.length > 1) {
throw new Error('toHaveBeenCalledOnce does not take arguments, use toHaveBeenCalledWith');
}
return [
'Expected spy ' + this.actual.identity + ' to have been called once with ' +
jasmine.pp(expectedArgs) + ' but it was called ' + this.actual.callCount + ' times.',
'Expected spy ' + this.actual.identity + ' not to have been called once with ' +
jasmine.pp(expectedArgs) + ' but it was.'
];
} else {
return [
'Expected spy ' + this.actual.identity + ' to have been called once with ' +
jasmine.pp(expectedArgs) + ' but was called with ' + jasmine.pp(this.actual.argsForCall),
'Expected spy ' + this.actual.identity + ' not to have been called once with ' +
jasmine.pp(expectedArgs) + ' but was called with ' + jasmine.pp(this.actual.argsForCall)
];
if (!jasmine.isSpy(actual)) {
throw new Error('Expected a spy, but got ' + jasmine.pp(actual) + '.');
}
var message = function() {
var msg = 'Expected spy ' + actual.identity() + ' to have been called once, but was ',
count = this.actual.calls.count();
return [
count === 0 ? msg + 'never called.' :
msg + 'called ' + count + ' times.',
msg.replace('to have', 'not to have') + 'called once.'
];
};
return {
pass: actual.calls.count() == 1,
message: message
};
}
};
return this.actual.callCount === 1 && this.env.contains_(this.actual.argsForCall, expectedArgs);
},
toHaveBeenCalledOnceWith: function(util, customEqualityTesters) {
return {
compare: function(actual) {
var expectedArgs = Array.prototype.slice.call(arguments, 1);
if (!jasmine.isSpy(actual)) {
throw new Error('Expected a spy, but got ' + jasmine.pp(actual) + '.');
}
var message = function() {
if (actual.calls.count() != 1) {
if (actual.calls.count() === 0) {
return [
'Expected spy ' + actual.identity() + ' to have been called once with ' +
jasmine.pp(expectedArgs) + ' but it was never called.',
'Expected spy ' + actual.identity() + ' not to have been called with ' +
jasmine.pp(expectedArgs) + ' but it was.'
];
}
return [
'Expected spy ' + actual.identity() + ' to have been called once with ' +
jasmine.pp(expectedArgs) + ' but it was called ' + actual.calls.count() + ' times.',
'Expected spy ' + actual.identity() + ' not to have been called once with ' +
jasmine.pp(expectedArgs) + ' but it was.'
];
} else {
return [
'Expected spy ' + actual.identity() + ' to have been called once with ' +
jasmine.pp(expectedArgs) + ' but was called with ' + jasmine.pp(actual.calls.argsFor(0)),
'Expected spy ' + actual.identity() + ' not to have been called once with ' +
jasmine.pp(expectedArgs) + ' but was called with ' + jasmine.pp(actual.calls.argsFor(0))
];
}
};
return {
pass: actual.calls.count() === 1 && util.equals(actual.calls.argsFor(0), expectedArgs),
message: message
};
}
};
},
toBeOneOf: function() {
return Array.prototype.indexOf.call(arguments, this.actual) !== -1;
},
toHaveClass: function(clazz) {
this.message = function() {
return "Expected '" + angular.mock.dump(this.actual) + "'" + (this.isNot ? " not " : "") + " to have class '" + clazz + "'.";
};
var classes = clazz.trim().split(/\s+/);
for (var i = 0; i < classes.length; ++i) {
if (!jqLiteHasClass(this.actual[0], classes[i])) {
return false;
return {
compare: function(actual) {
var expectedArgs = Array.prototype.slice.call(arguments, 1);
return { pass: expectedArgs.indexOf(actual) !== -1 };
}
}
return true;
},
toThrowMatching: function(expected) {
return jasmine.Matchers.prototype.toThrow.call(this, expected);
},
toThrowMinErr: function(namespace, code, content) {
var result,
exception,
exceptionMessage = '',
escapeRegexp = function(str) {
// This function escapes all special regex characters.
// We use it to create matching regex from arbitrary strings.
// http://stackoverflow.com/questions/3446170/escape-string-for-use-in-javascript-regex
return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
},
codeRegex = new RegExp('^\\[' + escapeRegexp(namespace) + ':' + escapeRegexp(code) + '\\]'),
not = this.isNot ? "not " : "",
regex = jasmine.isA_("RegExp", content) ? content :
angular.isDefined(content) ? new RegExp(escapeRegexp(content)) : undefined;
if (!angular.isFunction(this.actual)) {
throw new Error('Actual is not a function');
}
try {
this.actual();
} catch (e) {
exception = e;
}
if (exception) {
exceptionMessage = exception.message || exception;
}
this.message = function() {
return "Expected function " + not + "to throw " +
namespace + "MinErr('" + code + "')" +
(regex ? " matching " + regex.toString() : "") +
(exception ? ", but it threw " + exceptionMessage : ".");
};
},
result = codeRegex.test(exceptionMessage);
if (!result) {
return result;
toHaveClass: function() {
return {
compare: generateCompare(false),
negativeCompare: generateCompare(true)
};
function generateCompare(isNot) {
return function(actual, clazz) {
var message = function() {
return "Expected '" + angular.mock.dump(actual) + "'" + (isNot ? " not " : "") + " to have class '" + clazz + "'.";
};
var classes = clazz.trim().split(/\s+/);
for (var i = 0; i < classes.length; ++i) {
if (!jqLiteHasClass(actual[0], classes[i])) {
return { pass: isNot };
}
}
return { pass: !isNot };
};
}
},
if (angular.isDefined(regex)) {
return regex.test(exceptionMessage);
toThrowMinErr: function() {
return {
compare: generateCompare(false),
negativeCompare: generateCompare(true)
};
function generateCompare(isNot) {
return function(actual, namespace, code, content) {
var result,
exception,
exceptionMessage = '',
escapeRegexp = function(str) {
// This function escapes all special regex characters.
// We use it to create matching regex from arbitrary strings.
// http://stackoverflow.com/questions/3446170/escape-string-for-use-in-javascript-regex
return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
},
codeRegex = new RegExp('^\\[' + escapeRegexp(namespace) + ':' + escapeRegexp(code) + '\\]'),
not = isNot ? "not " : "",
regex = jasmine.isA_("RegExp", content) ? content :
angular.isDefined(content) ? new RegExp(escapeRegexp(content)) : undefined;
if (!angular.isFunction(actual)) {
throw new Error('Actual is not a function');
}
try {
actual();
} catch (e) {
exception = e;
}
if (exception) {
exceptionMessage = exception.message || exception;
}
var message = function() {
return "Expected function " + not + "to throw " +
namespace + "MinErr('" + code + "')" +
(regex ? " matching " + regex.toString() : "") +
(exception ? ", but it threw " + exceptionMessage : ".");
};
result = codeRegex.test(exceptionMessage);
if (!result) {
if (isNot) {
return { pass: !result, message: message };
} else {
return { pass: result, message: message };
}
}
if (angular.isDefined(regex)) {
if (isNot) {
return { pass: !regex.test(exceptionMessage), message: message };
} else {
return { pass: regex.test(exceptionMessage), message: message };
}
}
if (isNot) {
return { pass: !result, message: message };
} else {
return { pass: result, message: message };
}
};
}
return result;
}
});
});
// TODO(vojta): remove this once Jasmine in Karma gets updated
// https://github.com/pivotal/jasmine/blob/c40b64a24c607596fa7488f2a0ddb98d063c872a/src/core/Matchers.js#L217-L246
// This toThrow supports RegExps.
jasmine.Matchers.prototype.toThrow = function(expected) {
var result = false;
var exception, exceptionMessage;
if (typeof this.actual != 'function') {
throw new Error('Actual is not a function');
}
try {
this.actual();
} catch (e) {
exception = e;
}
if (exception) {
exceptionMessage = exception.message || exception;
result = (isUndefined(expected) || this.env.equals_(exceptionMessage, expected.message || expected) || (jasmine.isA_("RegExp", expected) && expected.test(exceptionMessage)));
}
var not = this.isNot ? "not " : "";
var regexMatch = jasmine.isA_("RegExp", expected) ? " an exception matching" : "";
this.message = function() {
if (exception) {
return ["Expected function " + not + "to throw" + regexMatch, expected ? expected.message || expected : "an exception", ", but it threw", exceptionMessage].join(' ');
} else {
return "Expected function to throw an exception.";
}
};
return result;
};
/**
* Create jasmine.Spy on given method, but ignore calls without arguments
* This is helpful when need to spy only setter methods and ignore getters
*/
function spyOnlyCallsWithArgs(obj, method) {
var originalFn = obj[method];
var spy = spyOn(obj, method);
obj[method] = function() {
if (arguments.length) return spy.apply(this, arguments);
return spy.originalValue.apply(this);
return originalFn.apply(this);
};
return spy;
}
// Minimal implementation to mock what was removed from Jasmine 1.x
function createAsync(doneFn) {
function Job() {
this.next = [];
}
Job.prototype.done = function() {
return this.runs(doneFn);
};
Job.prototype.runs = function(fn) {
var newJob = new Job();
this.next.push(function() {
fn();
newJob.start();
});
return newJob;
};
Job.prototype.waitsFor = function(fn, error, timeout) {
var newJob = new Job();
timeout = timeout || 5000;
this.next.push(function() {
var counter = 0,
intervalId = window.setInterval(function() {
if (fn()) {
window.clearInterval(intervalId);
newJob.start();
}
counter += 5;
if (counter > timeout) {
window.clearInterval(intervalId);
throw new Error(error);
}
}, 5);
});
return newJob;
};
Job.prototype.waits = function(timeout) {
return this.waitsFor(function() { return true; }, undefined, timeout);
};
Job.prototype.start = function() {
var i;
for (i = 0; i < this.next.length; i += 1) {
this.next[i]();
}
};
return new Job();
}
+2 -2
View File
@@ -23,8 +23,8 @@ function they(msg, vals, spec) {
baseThey(msg, vals, spec, it);
}
function tthey(msg, vals, spec) {
baseThey(msg, vals, spec, iit);
function fthey(msg, vals, spec) {
baseThey(msg, vals, spec, fit);
}
function xthey(msg, vals, spec) {
+67 -64
View File
@@ -9,7 +9,7 @@ describe('private mocks', function() {
spyOn(window, 'it');
they('should do stuff with $prop', ['a', 'b', 'c']);
expect(window.it.calls.length).toEqual(3);
expect(window.it).toHaveBeenCalledTimes(3);
expect(window.it).toHaveBeenCalledWith('should do stuff with "a"', jasmine.any(Function));
expect(window.it).toHaveBeenCalledWith('should do stuff with "b"', jasmine.any(Function));
expect(window.it).toHaveBeenCalledWith('should do stuff with "c"', jasmine.any(Function));
@@ -31,7 +31,7 @@ describe('private mocks', function() {
it('should pass each item in an array to the handler', function() {
var handlerSpy = jasmine.createSpy('handler');
spyOn(window, 'it').andCallFake(function(msg, handler) {
spyOn(window, 'it').and.callFake(function(msg, handler) {
handler();
});
they('should do stuff with $prop', ['a', 'b', 'c'], handlerSpy);
@@ -45,7 +45,7 @@ describe('private mocks', function() {
spyOn(window, 'it');
they('should do stuff with $prop', {a: 1, b:2, c:3});
expect(window.it.calls.length).toEqual(3);
expect(window.it).toHaveBeenCalledTimes(3);
expect(window.it).toHaveBeenCalledWith('should do stuff with "a"', jasmine.any(Function));
expect(window.it).toHaveBeenCalledWith('should do stuff with "b"', jasmine.any(Function));
expect(window.it).toHaveBeenCalledWith('should do stuff with "c"', jasmine.any(Function));
@@ -54,7 +54,7 @@ describe('private mocks', function() {
it('should pass each key-value pair in an object to the handler', function() {
var handlerSpy = jasmine.createSpy('handler');
spyOn(window, 'it').andCallFake(function(msg, handler) {
spyOn(window, 'it').and.callFake(function(msg, handler) {
handler();
});
they('should do stuff with $prop', {a: 1, b:2, c:3}, handlerSpy);
@@ -68,35 +68,35 @@ describe('private mocks', function() {
var handlerSpy = jasmine.createSpy('handler');
var dummyThis = { name: 'dummyThis' };
spyOn(window, 'it').andCallFake(function(msg, handler) {
spyOn(window, 'it').and.callFake(function(msg, handler) {
handler.call(dummyThis);
});
they('should do stuff with $prop', ['a'], handlerSpy);
expect(window.it).toHaveBeenCalledWith('should do stuff with "a"', jasmine.any(Function));
expect(handlerSpy.mostRecentCall.object).toBe(dummyThis);
expect(handlerSpy.calls.mostRecent().object).toBe(dummyThis);
});
});
describe('tthey', function() {
it('should call `iit` for each item in an array', function() {
spyOn(window, 'iit');
describe('fthey', function() {
it('should call `fit` for each item in an array', function() {
spyOn(window, 'fit');
tthey('should do stuff with $prop', ['a', 'b', 'c']);
expect(window.iit.calls.length).toEqual(3);
expect(window.iit).toHaveBeenCalledWith('should do stuff with "a"', jasmine.any(Function));
expect(window.iit).toHaveBeenCalledWith('should do stuff with "b"', jasmine.any(Function));
expect(window.iit).toHaveBeenCalledWith('should do stuff with "c"', jasmine.any(Function));
fthey('should do stuff with $prop', ['a', 'b', 'c']);
expect(window.fit).toHaveBeenCalledTimes(3);
expect(window.fit).toHaveBeenCalledWith('should do stuff with "a"', jasmine.any(Function));
expect(window.fit).toHaveBeenCalledWith('should do stuff with "b"', jasmine.any(Function));
expect(window.fit).toHaveBeenCalledWith('should do stuff with "c"', jasmine.any(Function));
});
it('should pass each item in an array to the handler', function() {
var handlerSpy = jasmine.createSpy('handler');
spyOn(window, 'iit').andCallFake(function(msg, handler) {
spyOn(window, 'fit').and.callFake(function(msg, handler) {
handler();
});
tthey('should do stuff with $prop', ['a', 'b', 'c'], handlerSpy);
fthey('should do stuff with $prop', ['a', 'b', 'c'], handlerSpy);
expect(handlerSpy).toHaveBeenCalledWith('a');
expect(handlerSpy).toHaveBeenCalledWith('b');
expect(handlerSpy).toHaveBeenCalledWith('c');
@@ -104,22 +104,22 @@ describe('private mocks', function() {
it('should call `it` for each key-value pair an object', function() {
spyOn(window, 'iit');
spyOn(window, 'fit');
tthey('should do stuff with $prop', {a: 1, b:2, c:3});
expect(window.iit.calls.length).toEqual(3);
expect(window.iit).toHaveBeenCalledWith('should do stuff with "a"', jasmine.any(Function));
expect(window.iit).toHaveBeenCalledWith('should do stuff with "b"', jasmine.any(Function));
expect(window.iit).toHaveBeenCalledWith('should do stuff with "c"', jasmine.any(Function));
fthey('should do stuff with $prop', {a: 1, b:2, c:3});
expect(window.fit).toHaveBeenCalledTimes(3);
expect(window.fit).toHaveBeenCalledWith('should do stuff with "a"', jasmine.any(Function));
expect(window.fit).toHaveBeenCalledWith('should do stuff with "b"', jasmine.any(Function));
expect(window.fit).toHaveBeenCalledWith('should do stuff with "c"', jasmine.any(Function));
});
it('should pass each key-value pair in an object to the handler', function() {
var handlerSpy = jasmine.createSpy('handler');
spyOn(window, 'iit').andCallFake(function(msg, handler) {
spyOn(window, 'fit').and.callFake(function(msg, handler) {
handler();
});
tthey('should do stuff with $prop', {a: 1, b:2, c:3}, handlerSpy);
fthey('should do stuff with $prop', {a: 1, b:2, c:3}, handlerSpy);
expect(handlerSpy).toHaveBeenCalledWith(1);
expect(handlerSpy).toHaveBeenCalledWith(2);
expect(handlerSpy).toHaveBeenCalledWith(3);
@@ -130,13 +130,13 @@ describe('private mocks', function() {
var handlerSpy = jasmine.createSpy('handler');
var dummyThis = { name: 'dummyThis' };
spyOn(window, 'iit').andCallFake(function(msg, handler) {
spyOn(window, 'fit').and.callFake(function(msg, handler) {
handler.call(dummyThis);
});
tthey('should do stuff with $prop', ['a'], handlerSpy);
expect(window.iit).toHaveBeenCalledWith('should do stuff with "a"', jasmine.any(Function));
expect(handlerSpy.mostRecentCall.object).toBe(dummyThis);
fthey('should do stuff with $prop', ['a'], handlerSpy);
expect(window.fit).toHaveBeenCalledWith('should do stuff with "a"', jasmine.any(Function));
expect(handlerSpy.calls.mostRecent().object).toBe(dummyThis);
});
});
@@ -146,7 +146,7 @@ describe('private mocks', function() {
spyOn(window, 'xit');
xthey('should do stuff with $prop', ['a', 'b', 'c']);
expect(window.xit.calls.length).toEqual(3);
expect(window.xit).toHaveBeenCalledTimes(3);
expect(window.xit).toHaveBeenCalledWith('should do stuff with "a"', jasmine.any(Function));
expect(window.xit).toHaveBeenCalledWith('should do stuff with "b"', jasmine.any(Function));
expect(window.xit).toHaveBeenCalledWith('should do stuff with "c"', jasmine.any(Function));
@@ -155,7 +155,7 @@ describe('private mocks', function() {
it('should pass each item in an array to the handler', function() {
var handlerSpy = jasmine.createSpy('handler');
spyOn(window, 'xit').andCallFake(function(msg, handler) {
spyOn(window, 'xit').and.callFake(function(msg, handler) {
handler();
});
xthey('should do stuff with $prop', ['a', 'b', 'c'], handlerSpy);
@@ -169,7 +169,7 @@ describe('private mocks', function() {
spyOn(window, 'xit');
xthey('should do stuff with $prop', {a: 1, b:2, c:3});
expect(window.xit.calls.length).toEqual(3);
expect(window.xit).toHaveBeenCalledTimes(3);
expect(window.xit).toHaveBeenCalledWith('should do stuff with "a"', jasmine.any(Function));
expect(window.xit).toHaveBeenCalledWith('should do stuff with "b"', jasmine.any(Function));
expect(window.xit).toHaveBeenCalledWith('should do stuff with "c"', jasmine.any(Function));
@@ -178,7 +178,7 @@ describe('private mocks', function() {
it('should pass each key-value pair in an object to the handler', function() {
var handlerSpy = jasmine.createSpy('handler');
spyOn(window, 'xit').andCallFake(function(msg, handler) {
spyOn(window, 'xit').and.callFake(function(msg, handler) {
handler();
});
xthey('should do stuff with $prop', {a: 1, b:2, c:3}, handlerSpy);
@@ -192,13 +192,13 @@ describe('private mocks', function() {
var handlerSpy = jasmine.createSpy('handler');
var dummyThis = { name: 'dummyThis' };
spyOn(window, 'xit').andCallFake(function(msg, handler) {
spyOn(window, 'xit').and.callFake(function(msg, handler) {
handler.call(dummyThis);
});
xthey('should do stuff with $prop', ['a'], handlerSpy);
expect(window.xit).toHaveBeenCalledWith('should do stuff with "a"', jasmine.any(Function));
expect(handlerSpy.mostRecentCall.object).toBe(dummyThis);
expect(handlerSpy.calls.mostRecent().object).toBe(dummyThis);
});
});
});
@@ -206,43 +206,46 @@ describe('private mocks', function() {
describe('createMockStyleSheet', function() {
it('should allow custom styles to be created and removed when the stylesheet is destroyed',
it('should allow custom styles to be created and removed when the stylesheet is destroyed', function(done) {
inject(function($compile, $document, $window, $rootElement, $rootScope) {
var doc = $document[0];
var count = doc.styleSheets.length;
var stylesheet = createMockStyleSheet($document);
var elm;
runs(function() {
expect(doc.styleSheets.length).toBe(count + 1);
var doc = $document[0];
var count = doc.styleSheets.length;
var stylesheet = createMockStyleSheet($document);
var elm;
var job = createAsync(done);
job
.runs(function() {
expect(doc.styleSheets.length).toBe(count + 1);
angular.element(doc.body).append($rootElement);
angular.element(doc.body).append($rootElement);
elm = $compile('<div class="padded">...</div>')($rootScope);
$rootElement.append(elm);
elm = $compile('<div class="padded">...</div>')($rootScope);
$rootElement.append(elm);
expect(getStyle(elm, 'paddingTop')).toBe('0px');
expect(getStyle(elm, 'paddingTop')).toBe('0px');
stylesheet.addRule('.padded', 'padding-top:2px');
stylesheet.addRule('.padded', 'padding-top:2px');
})
.waitsFor(function() {
return getStyle(elm, 'paddingTop') === '2px';
})
.runs(function() {
stylesheet.destroy();
expect(getStyle(elm, 'paddingTop')).toBe('0px');
})
.done();
job.start();
function getStyle(element, key) {
var node = element[0];
return node.currentStyle ?
node.currentStyle[key] :
$window.getComputedStyle(node)[key];
}
});
waitsFor(function() {
return getStyle(elm, 'paddingTop') === '2px';
});
runs(function() {
stylesheet.destroy();
expect(getStyle(elm, 'paddingTop')).toBe('0px');
});
function getStyle(element, key) {
var node = element[0];
return node.currentStyle ?
node.currentStyle[key] :
$window.getComputedStyle(node)[key];
}
}));
});
});
});
+56 -54
View File
@@ -358,66 +358,68 @@ window.dump = function() {
}));
};
function getInputCompileHelper(currentSpec) {
function generateInputCompilerHelper(helper) {
beforeEach(function() {
module(function($compileProvider) {
$compileProvider.directive('attrCapture', function() {
return function(scope, element, $attrs) {
helper.attrs = $attrs;
};
});
});
inject(function($compile, $rootScope, $sniffer) {
var helper = {};
helper.compileInput = function(inputHtml, mockValidity, scope) {
module(function($compileProvider) {
$compileProvider.directive('attrCapture', function() {
return function(scope, element, $attrs) {
helper.attrs = $attrs;
scope = helper.scope = scope || $rootScope;
// Create the input element and dealoc when done
helper.inputElm = jqLite(inputHtml);
// Set up mock validation if necessary
if (isObject(mockValidity)) {
VALIDITY_STATE_PROPERTY = 'ngMockValidity';
helper.inputElm.prop(VALIDITY_STATE_PROPERTY, mockValidity);
}
// Create the form element and dealoc when done
helper.formElm = jqLite('<form name="form"></form>');
helper.formElm.append(helper.inputElm);
// Compile the lot and return the input element
$compile(helper.formElm)(scope);
spyOn(scope.form, '$addControl').and.callThrough();
spyOn(scope.form, '$$renameControl').and.callThrough();
scope.$digest();
return helper.inputElm;
};
helper.changeInputValueTo = function(value) {
helper.inputElm.val(value);
browserTrigger(helper.inputElm, $sniffer.hasEvent('input') ? 'input' : 'change');
};
helper.changeGivenInputTo = function(inputElm, value) {
inputElm.val(value);
browserTrigger(inputElm, $sniffer.hasEvent('input') ? 'input' : 'change');
};
helper.dealoc = function() {
dealoc(helper.inputElm);
dealoc(helper.formElm);
};
});
});
inject(function($compile, $rootScope, $sniffer) {
helper.compileInput = function(inputHtml, mockValidity, scope) {
scope = helper.scope = scope || $rootScope;
// Create the input element and dealoc when done
helper.inputElm = jqLite(inputHtml);
// Set up mock validation if necessary
if (isObject(mockValidity)) {
VALIDITY_STATE_PROPERTY = 'ngMockValidity';
helper.inputElm.prop(VALIDITY_STATE_PROPERTY, mockValidity);
currentSpec.after(function() {
VALIDITY_STATE_PROPERTY = 'validity';
});
}
// Create the form element and dealoc when done
helper.formElm = jqLite('<form name="form"></form>');
helper.formElm.append(helper.inputElm);
// Compile the lot and return the input element
$compile(helper.formElm)(scope);
spyOn(scope.form, '$addControl').andCallThrough();
spyOn(scope.form, '$$renameControl').andCallThrough();
scope.$digest();
return helper.inputElm;
};
helper.changeInputValueTo = function(value) {
helper.inputElm.val(value);
browserTrigger(helper.inputElm, $sniffer.hasEvent('input') ? 'input' : 'change');
};
helper.changeGivenInputTo = function(inputElm, value) {
inputElm.val(value);
browserTrigger(inputElm, $sniffer.hasEvent('input') ? 'input' : 'change');
};
helper.dealoc = function() {
dealoc(helper.inputElm);
dealoc(helper.formElm);
};
afterEach(function() {
helper.dealoc();
});
return helper;
afterEach(function() {
VALIDITY_STATE_PROPERTY = 'validity';
});
}
+3 -3
View File
@@ -23,9 +23,9 @@ if (window.jQuery) {
expect(divSpy).not.toHaveBeenCalled();
expect(spy1).toHaveBeenCalled();
expect(spy1.callCount).toEqual(1);
expect(spy1).toHaveBeenCalledTimes(1);
expect(spy2).toHaveBeenCalled();
expect(spy2.callCount).toEqual(1);
expect(spy2).toHaveBeenCalledTimes(1);
});
describe('$detach event', function() {
@@ -82,7 +82,7 @@ if (window.jQuery) {
it('should fire only on matched elements on remove(selector)', function() {
doc.find('span').remove('.second');
expect(spy2).toHaveBeenCalled();
expect(spy2.callCount).toEqual(1);
expect(spy2).toHaveBeenCalledTimes(1);
});
it('should not fire on html()', function() {
+85 -66
View File
@@ -15,21 +15,25 @@ describe('jqLite', function() {
beforeEach(inject(function($rootScope) {
scope = $rootScope;
this.addMatchers({
toJqEqual: function(expected) {
var msg = "Unequal length";
this.message = function() {return msg;};
jasmine.addMatchers({
toJqEqual: function() {
return {
compare: function(_actual_, expected) {
var msg = "Unequal length";
var message = function() {return msg;};
var value = this.actual && expected && this.actual.length == expected.length;
for (var i = 0; value && i < expected.length; i++) {
var actual = jqLite(this.actual[i])[0];
var expect = jqLite(expected[i])[0];
value = value && equals(expect, actual);
msg = "Not equal at index: " + i
+ " - Expected: " + expect
+ " - Actual: " + actual;
}
return value;
var value = _actual_ && expected && _actual_.length == expected.length;
for (var i = 0; value && i < expected.length; i++) {
var actual = jqLite(_actual_[i])[0];
var expect = jqLite(expected[i])[0];
value = value && equals(expect, actual);
msg = "Not equal at index: " + i
+ " - Expected: " + expect
+ " - Actual: " + actual;
}
return { pass: value, message: message };
}
};
}
});
}));
@@ -1009,11 +1013,24 @@ describe('jqLite', function() {
'<option>test 2</option>' +
'</select>').val()).toEqual(['test 1']);
// In jQuery >= 3.0 .val() on select[multiple] with no selected options returns an
// empty array, not null.
// See https://github.com/jquery/jquery/issues/2562 for more details.
// jqLite will align with jQuery 3.0 behavior in Angular 1.6.
var val;
var jQueryVersion = window.jQuery && window.jQuery.fn.jquery.split('.')[0];
var jQuery3xOrNewer = jQueryVersion && (Number(jQueryVersion) >= 3);
if (!_jqLiteMode && jQuery3xOrNewer) {
val = [];
} else {
val = null;
}
expect(jqLite(
'<select multiple>' +
'<option>test 1</option>' +
'<option>test 2</option>' +
'</select>').val()).toEqual(null);
'</select>').val()).toEqual(val);
});
});
@@ -1021,7 +1038,7 @@ describe('jqLite', function() {
describe('html', function() {
it('should return null on empty', function() {
expect(jqLite().length).toEqual(0);
expect(jqLite().html()).toEqual(null);
expect(jqLite().html()).toEqual(undefined);
});
@@ -1105,12 +1122,12 @@ describe('jqLite', function() {
browserTrigger(a, 'click');
expect(callback).toHaveBeenCalled();
expect(callback.callCount).toBe(2);
expect(callback).toHaveBeenCalledTimes(2);
callback.reset();
callback.calls.reset();
browserTrigger(a, 'keypress');
expect(callback).toHaveBeenCalled();
expect(callback.callCount).toBe(1);
expect(callback).toHaveBeenCalledTimes(1);
});
it('should set event.target on IE', function() {
@@ -1126,7 +1143,7 @@ describe('jqLite', function() {
var element = jqLite(a),
clickSpy = jasmine.createSpy('clickSpy');
clickSpy.andCallFake(function(e) {
clickSpy.and.callFake(function(e) {
expect(function() {
expect(e.isDefaultPrevented()).toBe(false);
e.preventDefault();
@@ -1143,7 +1160,7 @@ describe('jqLite', function() {
it('should stop triggering handlers when stopImmediatePropagation is called', function() {
var element = jqLite(a),
clickSpy1 = jasmine.createSpy('clickSpy1'),
clickSpy2 = jasmine.createSpy('clickSpy2').andCallFake(function(event) { event.stopImmediatePropagation(); }),
clickSpy2 = jasmine.createSpy('clickSpy2').and.callFake(function(event) { event.stopImmediatePropagation(); }),
clickSpy3 = jasmine.createSpy('clickSpy3'),
clickSpy4 = jasmine.createSpy('clickSpy4');
@@ -1164,7 +1181,7 @@ describe('jqLite', function() {
var element = jqLite(a),
clickSpy = jasmine.createSpy('clickSpy');
clickSpy.andCallFake(function(event) {
clickSpy.and.callFake(function(event) {
spyOn(event, 'stopPropagation');
event.stopImmediatePropagation();
expect(event.stopPropagation).toHaveBeenCalled();
@@ -1180,7 +1197,7 @@ describe('jqLite', function() {
var element = jqLite(a),
clickSpy = jasmine.createSpy('clickSpy');
clickSpy.andCallFake(function(event) {
clickSpy.and.callFake(function(event) {
expect(event.isImmediatePropagationStopped()).toBe(false);
event.stopImmediatePropagation();
expect(event.isImmediatePropagationStopped()).toBe(true);
@@ -1334,8 +1351,8 @@ describe('jqLite', function() {
browserTrigger(a, 'mouseover');
expect(mouseoverSpy).toHaveBeenCalledOnce();
clickSpy.reset();
mouseoverSpy.reset();
clickSpy.calls.reset();
mouseoverSpy.calls.reset();
aElem.off();
@@ -1359,8 +1376,8 @@ describe('jqLite', function() {
browserTrigger(a, 'mouseover');
expect(mouseoverSpy).toHaveBeenCalledOnce();
clickSpy.reset();
mouseoverSpy.reset();
clickSpy.calls.reset();
mouseoverSpy.calls.reset();
aElem.off('click');
@@ -1369,7 +1386,7 @@ describe('jqLite', function() {
browserTrigger(a, 'mouseover');
expect(mouseoverSpy).toHaveBeenCalledOnce();
mouseoverSpy.reset();
mouseoverSpy.calls.reset();
aElem.off('mouseover');
browserTrigger(a, 'mouseover');
@@ -1390,8 +1407,8 @@ describe('jqLite', function() {
browserTrigger(a, 'mouseover');
expect(mouseoverSpy).toHaveBeenCalledOnce();
clickSpy.reset();
mouseoverSpy.reset();
clickSpy.calls.reset();
mouseoverSpy.calls.reset();
aElem.off('click mouseover');
@@ -1414,8 +1431,8 @@ describe('jqLite', function() {
expect(clickSpy1).toHaveBeenCalledOnce();
expect(clickSpy2).toHaveBeenCalledOnce();
clickSpy1.reset();
clickSpy2.reset();
clickSpy1.calls.reset();
clickSpy2.calls.reset();
aElem.off('click', clickSpy1);
@@ -1423,7 +1440,7 @@ describe('jqLite', function() {
expect(clickSpy1).not.toHaveBeenCalled();
expect(clickSpy2).toHaveBeenCalledOnce();
clickSpy2.reset();
clickSpy2.calls.reset();
aElem.off('click', clickSpy2);
browserTrigger(a, 'click');
@@ -1488,7 +1505,7 @@ describe('jqLite', function() {
it('should deregister specific listener within the listener and call subsequent listeners', function() {
var aElem = jqLite(a),
clickSpy = jasmine.createSpy('click'),
clickOnceSpy = jasmine.createSpy('clickOnce').andCallFake(function() {
clickOnceSpy = jasmine.createSpy('clickOnce').and.callFake(function() {
aElem.off('click', clickOnceSpy);
});
@@ -1501,7 +1518,7 @@ describe('jqLite', function() {
browserTrigger(a, 'click');
expect(clickOnceSpy).toHaveBeenCalledOnce();
expect(clickSpy.callCount).toBe(2);
expect(clickSpy).toHaveBeenCalledTimes(2);
});
@@ -1516,11 +1533,11 @@ describe('jqLite', function() {
browserTrigger(a, 'click');
browserTrigger(a, 'mouseover');
expect(masterSpy.callCount).toBe(2);
expect(masterSpy).toHaveBeenCalledTimes(2);
expect(extraSpy).toHaveBeenCalledOnce();
masterSpy.reset();
extraSpy.reset();
masterSpy.calls.reset();
extraSpy.calls.reset();
aElem.off('click mouseover', masterSpy);
@@ -1536,15 +1553,15 @@ describe('jqLite', function() {
it('should deregister the native listener when all jqLite listeners for given type are gone ' +
'after off("eventName", listener) call', function() {
var aElem = jqLite(a);
var addEventListenerSpy = spyOn(aElem[0], 'addEventListener').andCallThrough();
var removeEventListenerSpy = spyOn(aElem[0], 'removeEventListener').andCallThrough();
var addEventListenerSpy = spyOn(aElem[0], 'addEventListener').and.callThrough();
var removeEventListenerSpy = spyOn(aElem[0], 'removeEventListener').and.callThrough();
var nativeListenerFn;
var jqLiteListener = function() {};
aElem.on('click', jqLiteListener);
expect(addEventListenerSpy).toHaveBeenCalledOnceWith('click', jasmine.any(Function), false);
nativeListenerFn = addEventListenerSpy.mostRecentCall.args[1];
nativeListenerFn = addEventListenerSpy.calls.mostRecent().args[1];
expect(removeEventListenerSpy).not.toHaveBeenCalled();
aElem.off('click', jqLiteListener);
@@ -1555,13 +1572,13 @@ describe('jqLite', function() {
it('should deregister the native listener when all jqLite listeners for given type are gone ' +
'after off("eventName") call', function() {
var aElem = jqLite(a);
var addEventListenerSpy = spyOn(aElem[0], 'addEventListener').andCallThrough();
var removeEventListenerSpy = spyOn(aElem[0], 'removeEventListener').andCallThrough();
var addEventListenerSpy = spyOn(aElem[0], 'addEventListener').and.callThrough();
var removeEventListenerSpy = spyOn(aElem[0], 'removeEventListener').and.callThrough();
var nativeListenerFn;
aElem.on('click', function() {});
expect(addEventListenerSpy).toHaveBeenCalledOnceWith('click', jasmine.any(Function), false);
nativeListenerFn = addEventListenerSpy.mostRecentCall.args[1];
nativeListenerFn = addEventListenerSpy.calls.mostRecent().args[1];
expect(removeEventListenerSpy).not.toHaveBeenCalled();
aElem.off('click');
@@ -1572,14 +1589,14 @@ describe('jqLite', function() {
it('should deregister the native listener when all jqLite listeners for given type are gone ' +
'after off("eventName1 eventName2") call', function() {
var aElem = jqLite(a);
var addEventListenerSpy = spyOn(aElem[0], 'addEventListener').andCallThrough();
var removeEventListenerSpy = spyOn(aElem[0], 'removeEventListener').andCallThrough();
var addEventListenerSpy = spyOn(aElem[0], 'addEventListener').and.callThrough();
var removeEventListenerSpy = spyOn(aElem[0], 'removeEventListener').and.callThrough();
var nativeListenerFn;
aElem.on('click', function() {});
expect(addEventListenerSpy).toHaveBeenCalledOnceWith('click', jasmine.any(Function), false);
nativeListenerFn = addEventListenerSpy.mostRecentCall.args[1];
addEventListenerSpy.reset();
nativeListenerFn = addEventListenerSpy.calls.mostRecent().args[1];
addEventListenerSpy.calls.reset();
aElem.on('dblclick', function() {});
expect(addEventListenerSpy).toHaveBeenCalledOnceWith('dblclick', nativeListenerFn, false);
@@ -1590,21 +1607,21 @@ describe('jqLite', function() {
expect(removeEventListenerSpy).toHaveBeenCalledWith('click', nativeListenerFn, false);
expect(removeEventListenerSpy).toHaveBeenCalledWith('dblclick', nativeListenerFn, false);
expect(removeEventListenerSpy.callCount).toBe(2);
expect(removeEventListenerSpy).toHaveBeenCalledTimes(2);
});
it('should deregister the native listener when all jqLite listeners for given type are gone ' +
'after off() call', function() {
var aElem = jqLite(a);
var addEventListenerSpy = spyOn(aElem[0], 'addEventListener').andCallThrough();
var removeEventListenerSpy = spyOn(aElem[0], 'removeEventListener').andCallThrough();
var addEventListenerSpy = spyOn(aElem[0], 'addEventListener').and.callThrough();
var removeEventListenerSpy = spyOn(aElem[0], 'removeEventListener').and.callThrough();
var nativeListenerFn;
aElem.on('click', function() {});
expect(addEventListenerSpy).toHaveBeenCalledOnceWith('click', jasmine.any(Function), false);
nativeListenerFn = addEventListenerSpy.mostRecentCall.args[1];
addEventListenerSpy.reset();
nativeListenerFn = addEventListenerSpy.calls.mostRecent().args[1];
addEventListenerSpy.calls.reset();
aElem.on('dblclick', function() {});
expect(addEventListenerSpy).toHaveBeenCalledOnceWith('dblclick', nativeListenerFn, false);
@@ -1613,7 +1630,7 @@ describe('jqLite', function() {
expect(removeEventListenerSpy).toHaveBeenCalledWith('click', nativeListenerFn, false);
expect(removeEventListenerSpy).toHaveBeenCalledWith('dblclick', nativeListenerFn, false);
expect(removeEventListenerSpy.callCount).toBe(2);
expect(removeEventListenerSpy).toHaveBeenCalledTimes(2);
});
});
@@ -1625,7 +1642,7 @@ describe('jqLite', function() {
aElem.on('click', noop);
expect(function() {
aElem.off('click', noop, '.test');
}).toThrowMatching(/\[jqLite:offargs\]/);
}).toThrowError(/\[jqLite:offargs\]/);
});
}
});
@@ -1725,7 +1742,7 @@ describe('jqLite', function() {
expect(contents[1].data).toEqual('before-');
});
it('should select all types iframe contents', function() {
it('should select all types iframe contents', function(done) {
var iframe_ = document.createElement('iframe');
var tested = false;
var iframe = jqLite(iframe_);
@@ -1752,7 +1769,9 @@ describe('jqLite', function() {
// This test is potentially flaky on CI cloud instances, so there is a generous
// wait period...
waitsFor(function() { return tested; }, 'iframe to load', 5000);
var job = createAsync(done);
job.waitsFor(function() { return tested; }, 'iframe to load', 5000).done();
job.start();
});
});
@@ -1999,7 +2018,7 @@ describe('jqLite', function() {
element.on('click', pokeSpy);
element.triggerHandler('click');
event = pokeSpy.mostRecentCall.args[0];
event = pokeSpy.calls.mostRecent().args[0];
expect(event.preventDefault).toBeDefined();
expect(event.target).toEqual(element[0]);
expect(event.type).toEqual('click');
@@ -2013,7 +2032,7 @@ describe('jqLite', function() {
element.on('click', pokeSpy);
element.triggerHandler('click', [{hello: "world"}]);
data = pokeSpy.mostRecentCall.args[1];
data = pokeSpy.calls.mostRecent().args[1];
expect(data.hello).toBe("world");
});
@@ -2024,7 +2043,7 @@ describe('jqLite', function() {
element.on('click', pokeSpy);
element.triggerHandler('click');
event = pokeSpy.mostRecentCall.args[0];
event = pokeSpy.calls.mostRecent().args[0];
expect(event.isDefaultPrevented()).toBe(false);
event.preventDefault();
@@ -2034,7 +2053,7 @@ describe('jqLite', function() {
it('should support handlers that deregister themselves', function() {
var element = jqLite('<a>poke</a>'),
clickSpy = jasmine.createSpy('click'),
clickOnceSpy = jasmine.createSpy('clickOnce').andCallFake(function() {
clickOnceSpy = jasmine.createSpy('clickOnce').and.callFake(function() {
element.off('click', clickOnceSpy);
});
@@ -2047,7 +2066,7 @@ describe('jqLite', function() {
element.triggerHandler('click');
expect(clickOnceSpy).toHaveBeenCalledOnce();
expect(clickSpy.callCount).toBe(2);
expect(clickSpy).toHaveBeenCalledTimes(2);
});
it("should accept a custom event instead of eventName", function() {
@@ -2061,7 +2080,7 @@ describe('jqLite', function() {
element.on('click', pokeSpy);
element.triggerHandler(customEvent);
actualEvent = pokeSpy.mostRecentCall.args[0];
actualEvent = pokeSpy.calls.mostRecent().args[0];
expect(actualEvent.preventDefault).toBeDefined();
expect(actualEvent.someProp).toEqual('someValue');
expect(actualEvent.target).toEqual(element[0]);
@@ -2071,7 +2090,7 @@ describe('jqLite', function() {
it('should stop triggering handlers when stopImmediatePropagation is called', function() {
var element = jqLite(a),
clickSpy1 = jasmine.createSpy('clickSpy1'),
clickSpy2 = jasmine.createSpy('clickSpy2').andCallFake(function(event) { event.stopImmediatePropagation(); }),
clickSpy2 = jasmine.createSpy('clickSpy2').and.callFake(function(event) { event.stopImmediatePropagation(); }),
clickSpy3 = jasmine.createSpy('clickSpy3');
element.on('click', clickSpy1);
@@ -2092,7 +2111,7 @@ describe('jqLite', function() {
element.on('click', clickSpy);
element.triggerHandler('click');
event = clickSpy.mostRecentCall.args[0];
event = clickSpy.calls.mostRecent().args[0];
expect(event.isImmediatePropagationStopped()).toBe(false);
event.stopImmediatePropagation();
@@ -2143,7 +2162,7 @@ describe('jqLite', function() {
jqLiteDocumentLoaded(onLoadCallback, mockWindow);
expect(mockWindow.addEventListener).not.toHaveBeenCalled();
expect(mockWindow.setTimeout.mostRecentCall.args[0]).toBe(onLoadCallback);
expect(mockWindow.setTimeout.calls.mostRecent().args[0]).toBe(onLoadCallback);
});
+12 -12
View File
@@ -47,20 +47,20 @@ describe('module loader', function() {
expect(myModule.requires).toEqual(['other']);
expect(myModule._invokeQueue).toEqual([
['$provide', 'constant', ['abc', 123]],
['$provide', 'decorator', ['dk', 'dv']],
['$provide', 'provider', ['sk', 'sv']],
['$provide', 'factory', ['fk', 'fv']],
['$provide', 'service', ['a', 'aa']],
['$provide', 'value', ['k', 'v']],
['$filterProvider', 'register', ['f', 'ff']],
['$compileProvider', 'directive', ['d', 'dd']],
['$compileProvider', 'component', ['c', 'cc']],
['$controllerProvider', 'register', ['ctrl', 'ccc']]
['$provide', 'constant', jasmine.objectContaining(['abc', 123])],
['$provide', 'decorator', jasmine.objectContaining(['dk', 'dv'])],
['$provide', 'provider', jasmine.objectContaining(['sk', 'sv'])],
['$provide', 'factory', jasmine.objectContaining(['fk', 'fv'])],
['$provide', 'service', jasmine.objectContaining(['a', 'aa'])],
['$provide', 'value', jasmine.objectContaining(['k', 'v'])],
['$filterProvider', 'register', jasmine.objectContaining(['f', 'ff'])],
['$compileProvider', 'directive', jasmine.objectContaining(['d', 'dd'])],
['$compileProvider', 'component', jasmine.objectContaining(['c', 'cc'])],
['$controllerProvider', 'register', jasmine.objectContaining(['ctrl', 'ccc'])]
]);
expect(myModule._configBlocks).toEqual([
['$injector', 'invoke', ['config']],
['$injector', 'invoke', ['init2']]
['$injector', 'invoke', jasmine.objectContaining(['config'])],
['$injector', 'invoke', jasmine.objectContaining(['init2'])]
]);
expect(myModule._runBlocks).toEqual(['runBlock']);
});
+3
View File
@@ -0,0 +1,3 @@
// When runnint the modules test, then the page should not be bootstrapped.
window.name = "NG_DEFER_BOOTSTRAP!";
+13 -5
View File
@@ -88,7 +88,11 @@ describe('$anchorScroll', function() {
return function($window) {
forEach(elmSpy, function(spy, id) {
var count = map[id] || 0;
expect(spy.callCount).toBe(count);
if (count > 0) {
expect(spy).toHaveBeenCalledTimes(count);
} else {
expect(spy).not.toHaveBeenCalled();
}
});
expect($window.scrollTo).not.toHaveBeenCalled();
};
@@ -106,7 +110,7 @@ describe('$anchorScroll', function() {
return function() {
spyOn(window, 'jqLiteDocumentLoaded');
if (fake) {
window.jqLiteDocumentLoaded.andCallFake(fake);
window.jqLiteDocumentLoaded.and.callFake(fake);
}
};
}
@@ -123,7 +127,7 @@ describe('$anchorScroll', function() {
function fireWindowLoadEvent() {
return function($browser) {
var callback = window.jqLiteDocumentLoaded.mostRecentCall.args[0];
var callback = window.jqLiteDocumentLoaded.calls.mostRecent().args[0];
callback();
$browser.defer.flush();
};
@@ -382,10 +386,14 @@ describe('$anchorScroll', function() {
return function($rootScope, $window) {
inject(expectScrollingTo(identifierCountMap));
expect($window.scrollBy.callCount).toBe(list.length);
if (list.length > 0) {
expect($window.scrollBy).toHaveBeenCalledTimes(list.length);
} else {
expect($window.scrollBy).not.toHaveBeenCalled();
}
forEach(list, function(offset, idx) {
// Due to sub-pixel rendering, there is a +/-1 error margin in the actual offset
var args = $window.scrollBy.calls[idx].args;
var args = $window.scrollBy.calls.argsFor(idx);
expect(args[0]).toBe(0);
expect(Math.abs(offset + args[1])).toBeLessThan(1);
});
+15 -15
View File
@@ -313,7 +313,7 @@ describe("$animate", function() {
$rootScope.$digest();
}).not.toThrow();
var optionsArg = captureSpy.mostRecentCall.args[2];
var optionsArg = captureSpy.calls.mostRecent().args[2];
expect(optionsArg).not.toBe(invalidOptions);
expect(isObject(optionsArg)).toBeTruthy();
});
@@ -322,7 +322,7 @@ describe("$animate", function() {
it('should not issue a call to addClass if the provided class value is not a string or array', function() {
inject(function($animate, $rootScope, $rootElement) {
var spy = spyOn(window, 'jqLiteAddClass').andCallThrough();
var spy = spyOn(window, 'jqLiteAddClass').and.callThrough();
var element = jqLite('<div></div>');
var parent = $rootElement;
@@ -358,7 +358,7 @@ describe("$animate", function() {
it('should not issue a call to removeClass if the provided class value is not a string or array', function() {
inject(function($animate, $rootScope, $rootElement) {
var spy = spyOn(window, 'jqLiteRemoveClass').andCallThrough();
var spy = spyOn(window, 'jqLiteRemoveClass').and.callThrough();
var element = jqLite('<div></div>');
var parent = $rootElement;
@@ -412,15 +412,15 @@ describe("$animate", function() {
function setupClassManipulationSpies() {
inject(function($animate) {
addClass = spyOn(window, 'jqLiteAddClass').andCallThrough();
removeClass = spyOn(window, 'jqLiteRemoveClass').andCallThrough();
addClass = spyOn(window, 'jqLiteAddClass').and.callThrough();
removeClass = spyOn(window, 'jqLiteRemoveClass').and.callThrough();
});
}
function setupClassManipulationLogger(log) {
inject(function() {
var _addClass = jqLiteAddClass;
addClass = spyOn(window, 'jqLiteAddClass').andCallFake(function(element, classes) {
addClass = spyOn(window, 'jqLiteAddClass').and.callFake(function(element, classes) {
var names = classes;
if (Object.prototype.toString.call(classes) === '[object Array]') names = classes.join(' ');
log('addClass(' + names + ')');
@@ -428,7 +428,7 @@ describe("$animate", function() {
});
var _removeClass = jqLiteRemoveClass;
removeClass = spyOn(window, 'jqLiteRemoveClass').andCallFake(function(element, classes) {
removeClass = spyOn(window, 'jqLiteRemoveClass').and.callFake(function(element, classes) {
var names = classes;
if (Object.prototype.toString.call(classes) === '[object Array]') names = classes.join(' ');
log('removeClass(' + names + ')');
@@ -461,8 +461,8 @@ describe("$animate", function() {
expect(element).toHaveClass('test-class2');
expect(element).toHaveClass('test-class3');
expect(log).toEqual(['addClass(test-class2 test-class3)']);
expect(addClass.callCount).toBe(1);
expect(removeClass.callCount).toBe(0);
expect(addClass).toHaveBeenCalledTimes(1);
expect(removeClass).not.toHaveBeenCalled();
}));
@@ -483,8 +483,8 @@ describe("$animate", function() {
expect(element).not.toHaveClass('test-class1');
expect(element).toHaveClass('test-class2');
expect(element).toHaveClass('test-class3');
expect(addClass.callCount).toBe(1);
expect(removeClass.callCount).toBe(1);
expect(addClass).toHaveBeenCalledTimes(1);
expect(removeClass).toHaveBeenCalledTimes(1);
}));
@@ -552,8 +552,8 @@ describe("$animate", function() {
expect(target).not.toHaveClass('test-class1');
expect(target).toHaveClass('test-class2');
expect(addClass.callCount).toBe(1);
expect(removeClass.callCount).toBe(0);
expect(addClass).toHaveBeenCalledTimes(1);
expect(removeClass).not.toHaveBeenCalled();
}));
@@ -575,8 +575,8 @@ describe("$animate", function() {
expect(target).not.toHaveClass('test-class1');
expect(target).toHaveClass('test-class2');
expect(target).toHaveClass('test-class3');
expect(addClass.callCount).toBe(1);
expect(removeClass.callCount).toBe(1);
expect(addClass).toHaveBeenCalledTimes(1);
expect(removeClass).toHaveBeenCalledTimes(1);
}));
+50 -19
View File
@@ -48,7 +48,13 @@ function MockWindow(options) {
this.fire = function(name) {
forEach(events[name], function(fn) {
fn({type: name}); // type to make jQuery happy
// type/target to make jQuery happy
fn({
type: name,
target: {
nodeType: 1
}
});
});
};
@@ -285,7 +291,7 @@ describe('browser', function() {
browser.url('http://new.org');
expect(pushState).toHaveBeenCalledOnce();
expect(pushState.argsForCall[0][2]).toEqual('http://new.org');
expect(pushState.calls.argsFor(0)[2]).toEqual('http://new.org');
expect(replaceState).not.toHaveBeenCalled();
expect(locationReplace).not.toHaveBeenCalled();
@@ -297,7 +303,7 @@ describe('browser', function() {
browser.url('http://new.org', true);
expect(replaceState).toHaveBeenCalledOnce();
expect(replaceState.argsForCall[0][2]).toEqual('http://new.org');
expect(replaceState.calls.argsFor(0)[2]).toEqual('http://new.org');
expect(pushState).not.toHaveBeenCalled();
expect(locationReplace).not.toHaveBeenCalled();
@@ -398,7 +404,7 @@ describe('browser', function() {
expect(fakeWindow.location.href).toBe('http://server/someOtherUrl');
});
it('assumes that changes to location.hash occur in sync', function() {
it('assumes that changes to location.hash occur in sync', function(done) {
// This is an asynchronous integration test that changes the
// hash in all possible ways and checks
// - whether the change to the hash can be read out in sync
@@ -407,7 +413,8 @@ describe('browser', function() {
$realWin = jqLite(realWin),
hashInHashChangeEvent = [];
runs(function() {
var job = createAsync(done);
job.runs(function() {
$realWin.on('hashchange', hashListener);
realWin.location.hash = '1';
@@ -416,17 +423,18 @@ describe('browser', function() {
realWin.location.assign(realWin.location.href + '4');
expect(realWin.location.hash).toBe('#1234');
});
waitsFor(function() {
})
.waitsFor(function() {
return hashInHashChangeEvent.length > 3;
});
runs(function() {
})
.runs(function() {
$realWin.off('hashchange', hashListener);
forEach(hashInHashChangeEvent, function(hash) {
expect(hash).toBe('#1234');
});
});
}).done();
job.start();
function hashListener() {
hashInHashChangeEvent.push(realWin.location.hash);
@@ -451,9 +459,9 @@ describe('browser', function() {
fakeWindow = new MockWindow({msie: options.msie});
currentHref = fakeWindow.location.href;
pushState = spyOn(fakeWindow.history, 'pushState').andCallThrough();
replaceState = spyOn(fakeWindow.history, 'replaceState').andCallThrough();
locationReplace = spyOn(fakeWindow.location, 'replace').andCallThrough();
pushState = spyOn(fakeWindow.history, 'pushState').and.callThrough();
replaceState = spyOn(fakeWindow.history, 'replaceState').and.callThrough();
locationReplace = spyOn(fakeWindow.location, 'replace').and.callThrough();
browser = new Browser(fakeWindow, fakeDocument, fakeLog, sniffer);
browser.onUrlChange(function() {});
@@ -516,9 +524,9 @@ describe('browser', function() {
it('should not do pushState with the same URL and state from $browser.state()', function() {
browser.url(currentHref, false, {prop: 'val'});
pushState.reset();
replaceState.reset();
locationReplace.reset();
pushState.calls.reset();
replaceState.calls.reset();
locationReplace.calls.reset();
browser.url(currentHref, false, browser.state());
expect(pushState).not.toHaveBeenCalled();
@@ -537,9 +545,32 @@ describe('browser', function() {
currentHref = fakeWindow.location.href;
});
it('should not access `history.state` when `$sniffer.history` is false', function() {
// In the context of a Chrome Packaged App, although `history.state` is present, accessing it
// is not allowed and logs an error in the console. We should not try to access
// `history.state` in contexts where `$sniffer.history` is false.
var historyStateAccessed = false;
var mockSniffer = {histroy: false};
var mockWindow = new MockWindow();
var _state = mockWindow.history.state;
Object.defineProperty(mockWindow.history, 'state', {
get: function() {
historyStateAccessed = true;
return _state;
}
});
var browser = new Browser(mockWindow, fakeDocument, fakeLog, mockSniffer);
expect(historyStateAccessed).toBe(false);
});
describe('in IE', runTests({msie: true}));
describe('not in IE', runTests({msie: false}));
function runTests(options) {
return function() {
beforeEach(function() {
@@ -720,10 +751,10 @@ describe('browser', function() {
module(function($provide, $locationProvider) {
spyOn(fakeWindow.history, 'pushState').andCallFake(function(stateObj, title, newUrl) {
spyOn(fakeWindow.history, 'pushState').and.callFake(function(stateObj, title, newUrl) {
fakeWindow.location.href = newUrl;
});
spyOn(fakeWindow.location, 'replace').andCallFake(function(newUrl) {
spyOn(fakeWindow.location, 'replace').and.callFake(function(newUrl) {
fakeWindow.location.href = newUrl;
});
$provide.value('$browser', browser);
@@ -830,7 +861,7 @@ describe('browser', function() {
}
return _url.call(this, newUrl, replace);
};
spyOn(browser, 'url').andCallThrough();
spyOn(browser, 'url').and.callThrough();
inject(function($rootScope, $location) {
$rootScope.$digest();
$rootScope.$digest();
+423 -97
View File
@@ -1,6 +1,5 @@
'use strict';
describe('$compile', function() {
function isUnknownElement(el) {
return !!el.toString().match(/Unknown/);
@@ -207,7 +206,7 @@ describe('$compile', function() {
module(function() {
expect(function() {
directive('BadDirectiveName', function() { });
}).toThrowMinErr('$compile','baddir', "Directive name 'BadDirectiveName' is invalid. The first character must be a lowercase letter");
}).toThrowMinErr('$compile','baddir', "Directive/Component name 'BadDirectiveName' is invalid. The first character must be a lowercase letter");
});
inject(function($compile) {});
});
@@ -217,7 +216,7 @@ describe('$compile', function() {
expect(function() {
directive(name, function() { });
}).toThrowMinErr(
'$compile','baddir', 'Directive name \'' + name + '\' is invalid. ' +
'$compile','baddir', 'Directive/Component name \'' + name + '\' is invalid. ' +
"The name should not contain leading or trailing whitespaces");
}
assertLeadingOrTrailingWhitespaceInDirectiveName(' leadingWhitespaceDirectiveName');
@@ -372,28 +371,27 @@ describe('$compile', function() {
expect($document.scope()).toBe($rootScope);
}));
it('should wrap root text nodes in spans', inject(function($compile, $rootScope) {
element = jqLite('<div>A&lt;a&gt;B&lt;/a&gt;C</div>');
var text = element.contents();
expect(text[0].nodeName).toEqual('#text');
text = $compile(text)($rootScope);
expect(text[0].nodeName).toEqual('SPAN');
expect(element.find('span').text()).toEqual('A<a>B</a>C');
}));
it('should not wrap root text nodes in spans', function() {
it('should not wrap root whitespace text nodes in spans', function() {
element = jqLite(
'<div> <div>A</div>\n ' +
'<div>B</div>C\t\n ' +
'<div> <div>A</div>\n ' + // The spaces and newlines here should not get wrapped
'<div>B</div>C\t\n ' + // The "C", tabs and spaces here will be wrapped
'</div>');
$compile(element.contents())($rootScope);
var spans = element.find('span');
expect(spans.length).toEqual(0);
expect(spans.length).toEqual(1);
expect(spans.text().indexOf('C')).toEqual(0);
});
it('should be able to compile text nodes at the root', inject(function($rootScope) {
element = jqLite('<div>Name: {{name}}<br />\nColor: {{color}}</div>');
$rootScope.name = 'Lucas';
$rootScope.color = 'blue';
$compile(element.contents())($rootScope);
$rootScope.$digest();
expect(element.text()).toEqual('Name: Lucas\nColor: blue');
}));
it('should not leak memory when there are top level empty text nodes', function() {
// We compile the contents of element (i.e. not element itself)
// Then delete these contents and check the cache has been reset to zero
@@ -1927,7 +1925,7 @@ describe('$compile', function() {
$rootScope.$apply();
expect(controllerSpy.callCount).toBe(2);
expect(controllerSpy).toHaveBeenCalledTimes(2);
expect(element.text()).toBe('boom!1|boom!2|');
});
});
@@ -3060,7 +3058,7 @@ describe('$compile', function() {
it('should delegate exceptions to $exceptionHandler', function() {
observeSpy = jasmine.createSpy('$observe attr').andThrow('ERROR');
observeSpy = jasmine.createSpy('$observe attr').and.throwError('ERROR');
module(function($exceptionHandlerProvider) {
$exceptionHandlerProvider.mode('log');
@@ -3077,8 +3075,8 @@ describe('$compile', function() {
$rootScope.$digest();
expect(observeSpy).toHaveBeenCalled();
expect(observeSpy.callCount).toBe(2);
expect($exceptionHandler.errors).toEqual(['ERROR', 'ERROR']);
expect(observeSpy).toHaveBeenCalledTimes(2);
expect($exceptionHandler.errors).toEqual([new Error('ERROR'), new Error('ERROR')]);
});
});
@@ -3445,8 +3443,8 @@ describe('$compile', function() {
expect(spies[0]).toHaveBeenCalledOnceWith('id_1');
expect(spies[1]).toHaveBeenCalledOnceWith('id_2');
spies[0].reset();
spies[1].reset();
spies[0].calls.reset();
spies[1].calls.reset();
$rootScope.$apply(function() {
$rootScope.items[0].id = 5;
@@ -3516,6 +3514,391 @@ describe('$compile', function() {
});
});
describe('controller lifecycle hooks', function() {
describe('$onInit', function() {
it('should call `$onInit`, if provided, after all the controllers on the element have been initialized', function() {
function check() {
/*jshint validthis:true */
expect(this.element.controller('d1').id).toEqual(1);
expect(this.element.controller('d2').id).toEqual(2);
}
function Controller1($element) { this.id = 1; this.element = $element; }
Controller1.prototype.$onInit = jasmine.createSpy('$onInit').and.callFake(check);
function Controller2($element) { this.id = 2; this.element = $element; }
Controller2.prototype.$onInit = jasmine.createSpy('$onInit').and.callFake(check);
angular.module('my', [])
.directive('d1', valueFn({ controller: Controller1 }))
.directive('d2', valueFn({ controller: Controller2 }));
module('my');
inject(function($compile, $rootScope) {
element = $compile('<div d1 d2></div>')($rootScope);
expect(Controller1.prototype.$onInit).toHaveBeenCalledOnce();
expect(Controller2.prototype.$onInit).toHaveBeenCalledOnce();
});
});
});
describe('$onDestroy', function() {
it('should call `$onDestroy`, if provided, on the controller when its scope is destroyed', function() {
function TestController() { this.count = 0; }
TestController.prototype.$onDestroy = function() { this.count++; };
angular.module('my', [])
.directive('d1', valueFn({ scope: true, controller: TestController }))
.directive('d2', valueFn({ scope: {}, controller: TestController }))
.directive('d3', valueFn({ controller: TestController }));
module('my');
inject(function($compile, $rootScope) {
element = $compile('<div><d1 ng-if="show[0]"></d1><d2 ng-if="show[1]"></d2><div ng-if="show[2]"><d3></d3></div></div>')($rootScope);
$rootScope.$apply('show = [true, true, true]');
var d1Controller = element.find('d1').controller('d1');
var d2Controller = element.find('d2').controller('d2');
var d3Controller = element.find('d3').controller('d3');
expect([d1Controller.count, d2Controller.count, d3Controller.count]).toEqual([0,0,0]);
$rootScope.$apply('show = [false, true, true]');
expect([d1Controller.count, d2Controller.count, d3Controller.count]).toEqual([1,0,0]);
$rootScope.$apply('show = [false, false, true]');
expect([d1Controller.count, d2Controller.count, d3Controller.count]).toEqual([1,1,0]);
$rootScope.$apply('show = [false, false, false]');
expect([d1Controller.count, d2Controller.count, d3Controller.count]).toEqual([1,1,1]);
});
});
it('should call `$onDestroy` top-down (the same as `scope.$broadcast`)', function() {
var log = [];
function ParentController() { log.push('parent created'); }
ParentController.prototype.$onDestroy = function() { log.push('parent destroyed'); };
function ChildController() { log.push('child created'); }
ChildController.prototype.$onDestroy = function() { log.push('child destroyed'); };
function GrandChildController() { log.push('grand child created'); }
GrandChildController.prototype.$onDestroy = function() { log.push('grand child destroyed'); };
angular.module('my', [])
.directive('parent', valueFn({ scope: true, controller: ParentController }))
.directive('child', valueFn({ scope: true, controller: ChildController }))
.directive('grandChild', valueFn({ scope: true, controller: GrandChildController }));
module('my');
inject(function($compile, $rootScope) {
element = $compile('<parent ng-if="show"><child><grand-child></grand-child></child></parent>')($rootScope);
$rootScope.$apply('show = true');
expect(log).toEqual(['parent created', 'child created', 'grand child created']);
log = [];
$rootScope.$apply('show = false');
expect(log).toEqual(['parent destroyed', 'child destroyed', 'grand child destroyed']);
});
});
});
describe('$postLink', function() {
it('should call `$postLink`, if provided, after the element has completed linking (i.e. post-link)', function() {
var log = [];
function Controller1() { }
Controller1.prototype.$postLink = function() { log.push('d1 view init'); };
function Controller2() { }
Controller2.prototype.$postLink = function() { log.push('d2 view init'); };
angular.module('my', [])
.directive('d1', valueFn({
controller: Controller1,
link: { pre: function(s, e) { log.push('d1 pre: ' + e.text()); }, post: function(s, e) { log.push('d1 post: ' + e.text()); } },
template: '<d2></d2>'
}))
.directive('d2', valueFn({
controller: Controller2,
link: { pre: function(s, e) { log.push('d2 pre: ' + e.text()); }, post: function(s, e) { log.push('d2 post: ' + e.text()); } },
template: 'loaded'
}));
module('my');
inject(function($compile, $rootScope) {
element = $compile('<d1></d1>')($rootScope);
expect(log).toEqual([
'd1 pre: loaded',
'd2 pre: loaded',
'd2 post: loaded',
'd2 view init',
'd1 post: loaded',
'd1 view init'
]);
});
});
});
describe('$onChanges', function() {
it('should call `$onChanges`, if provided, when a one-way (`<`) or interpolation (`@`) bindings are updated', function() {
var log = [];
function TestController() { }
TestController.prototype.$onChanges = function(change) { log.push(change); };
angular.module('my', [])
.component('c1', {
controller: TestController,
bindings: { 'prop1': '<', 'prop2': '<', 'other': '=', 'attr': '@' }
});
module('my');
inject(function($compile, $rootScope) {
// Setup a watch to indicate some complicated updated logic
$rootScope.$watch('val', function(val, oldVal) { $rootScope.val2 = val * 2; });
// Setup the directive with two bindings
element = $compile('<c1 prop1="val" prop2="val2" other="val3" attr="{{val4}}"></c1>')($rootScope);
// There should be no changes initially
expect(log).toEqual([]);
// Update val to trigger the onChanges
$rootScope.$apply('val = 42');
// Now we should have a single changes entry in the log
expect(log).toEqual([
{
prop1: {previousValue: undefined, currentValue: 42},
prop2: {previousValue: undefined, currentValue: 84}
}
]);
// Clear the log
log = [];
// Update val to trigger the onChanges
$rootScope.$apply('val = 17');
// Now we should have a single changes entry in the log
expect(log).toEqual([
{
prop1: {previousValue: 42, currentValue: 17},
prop2: {previousValue: 84, currentValue: 34}
}
]);
// Clear the log
log = [];
// Update val3 to trigger the "other" two-way binding
$rootScope.$apply('val3 = 63');
// onChanges should not have been called
expect(log).toEqual([]);
// Update val4 to trigger the "attr" interpolation binding
$rootScope.$apply('val4 = 22');
// onChanges should not have been called
expect(log).toEqual([
{
attr: {previousValue: '', currentValue: '22'}
}
]);
});
});
it('should pass the original value as `previousValue` even if there were multiple changes in a single digest', function() {
var log = [];
function TestController() { }
TestController.prototype.$onChanges = function(change) { log.push(change); };
angular.module('my', [])
.component('c1', {
controller: TestController,
bindings: { 'prop': '<' }
});
module('my');
inject(function($compile, $rootScope) {
element = $compile('<c1 prop="a + b"></c1>')($rootScope);
// We add this watch after the compilation to ensure that it will run after the binding watchers
// therefore triggering the thing that this test is hoping to enfore
$rootScope.$watch('a', function(val) { $rootScope.b = val * 2; });
// There should be no changes initially
expect(log).toEqual([]);
// Update val to trigger the onChanges
$rootScope.$apply('a = 42');
// Now the change should have the real previous value (undefined), not the intermediate one (42)
expect(log).toEqual([{prop: {previousValue: undefined, currentValue: 126}}]);
// Clear the log
log = [];
// Update val to trigger the onChanges
$rootScope.$apply('a = 7');
// Now the change should have the real previous value (126), not the intermediate one, (91)
expect(log).toEqual([{ prop: {previousValue: 126, currentValue: 21}}]);
});
});
it('should only trigger one extra digest however many controllers have changes', function() {
var log = [];
function TestController1() { }
TestController1.prototype.$onChanges = function(change) { log.push(['TestController1', change]); };
function TestController2() { }
TestController2.prototype.$onChanges = function(change) { log.push(['TestController2', change]); };
angular.module('my', [])
.component('c1', {
controller: TestController1,
bindings: {'prop': '<'}
})
.component('c2', {
controller: TestController2,
bindings: {'prop': '<'}
});
module('my');
inject(function($compile, $rootScope) {
// Create a watcher to count the number of digest cycles
var watchCount = 0;
$rootScope.$watch(function() { watchCount++; });
// Setup two sibling components with bindings that will change
element = $compile('<div><c1 prop="val1"></c1><c2 prop="val2"></c2></div>')($rootScope);
// There should be no changes initially
expect(log).toEqual([]);
// Update val to trigger the onChanges
$rootScope.$apply('val1 = 42; val2 = 17');
expect(log).toEqual([
['TestController1', {prop: {previousValue: undefined, currentValue: 42}}],
['TestController2', {prop: {previousValue: undefined, currentValue: 17}}]
]);
// A single apply should only trigger three turns of the digest loop
expect(watchCount).toEqual(3);
});
});
it('should cope with changes occuring inside `$onChanges()` hooks', function() {
var log = [];
function OuterController() { }
OuterController.prototype.$onChanges = function(change) {
log.push(['OuterController', change]);
// Make a change to the inner component
this.b = 72;
};
function InnerController() { }
InnerController.prototype.$onChanges = function(change) { log.push(['InnerController', change]); };
angular.module('my', [])
.component('outer', {
controller: OuterController,
bindings: {'prop1': '<'},
template: '<inner prop2="$ctrl.b"></inner>'
})
.component('inner', {
controller: InnerController,
bindings: {'prop2': '<'}
});
module('my');
inject(function($compile, $rootScope) {
// Setup the directive with two bindings
element = $compile('<outer prop1="a"></outer>')($rootScope);
// There should be no changes initially
expect(log).toEqual([]);
// Update val to trigger the onChanges
$rootScope.$apply('a = 42');
expect(log).toEqual([
['OuterController', {prop1: {previousValue: undefined, currentValue: 42}}],
['InnerController', {prop2: {previousValue: undefined, currentValue: 72}}]
]);
});
});
it('should throw an error if `$onChanges()` hooks are not stable', function() {
function TestController() {}
TestController.prototype.$onChanges = function(change) {
this.onChange();
};
angular.module('my', [])
.component('c1', {
controller: TestController,
bindings: {'prop': '<', onChange: '&'}
});
module('my');
inject(function($compile, $rootScope) {
// Setup the directive with bindings that will keep updating the bound value forever
element = $compile('<c1 prop="a" on-change="a = -a"></c1>')($rootScope);
// Update val to trigger the unstable onChanges, which will result in an error
expect(function() {
$rootScope.$apply('a = 42');
}).toThrowMinErr('$compile', 'infchng');
dealoc(element);
element = $compile('<c1 prop="b" on-change=""></c1>')($rootScope);
$rootScope.$apply('b = 24');
$rootScope.$apply('b = 48');
});
});
it('should log an error if `$onChanges()` hooks are not stable', function() {
function TestController() {}
TestController.prototype.$onChanges = function(change) {
this.onChange();
};
angular.module('my', [])
.component('c1', {
controller: TestController,
bindings: {'prop': '<', onChange: '&'}
})
.config(function($exceptionHandlerProvider) {
// We need to test with the exceptionHandler not rethrowing...
$exceptionHandlerProvider.mode('log');
});
module('my');
inject(function($compile, $rootScope, $exceptionHandler) {
// Setup the directive with bindings that will keep updating the bound value forever
element = $compile('<c1 prop="a" on-change="a = -a"></c1>')($rootScope);
// Update val to trigger the unstable onChanges, which will result in an error
$rootScope.$apply('a = 42');
expect($exceptionHandler.errors.length).toEqual(1);
expect($exceptionHandler.errors[0].toString()).toContain('[$compile:infchng] 10 $onChanges() iterations reached.');
});
});
});
});
describe('isolated locals', function() {
var componentScope, regularScope;
@@ -4598,7 +4981,7 @@ describe('$compile', function() {
" controllerCalled = true;\n" +
" }\n" +
"}");
spyOn(Controller.prototype, '$onInit').andCallThrough();
spyOn(Controller.prototype, '$onInit').and.callThrough();
module(function($compileProvider) {
$compileProvider.directive('fooDir', valueFn({
@@ -5325,32 +5708,6 @@ describe('$compile', function() {
});
});
it('should call `controller.$onInit`, if provided after all the controllers have been constructed', function() {
function check() {
/*jshint validthis:true */
expect(this.element.controller('d1').id).toEqual(1);
expect(this.element.controller('d2').id).toEqual(2);
}
function Controller1($element) { this.id = 1; this.element = $element; }
Controller1.prototype.$onInit = jasmine.createSpy('$onInit').andCallFake(check);
function Controller2($element) { this.id = 2; this.element = $element; }
Controller2.prototype.$onInit = jasmine.createSpy('$onInit').andCallFake(check);
angular.module('my', [])
.directive('d1', valueFn({ controller: Controller1 }))
.directive('d2', valueFn({ controller: Controller2 }));
module('my');
inject(function($compile, $rootScope) {
element = $compile('<div d1 d2></div>')($rootScope);
expect(Controller1.prototype.$onInit).toHaveBeenCalledOnce();
expect(Controller2.prototype.$onInit).toHaveBeenCalledOnce();
});
});
describe('should not overwrite @-bound property each digest when not present', function() {
it('when creating new scope', function() {
module(function($compileProvider) {
@@ -5740,7 +6097,7 @@ describe('$compile', function() {
parentController = this.container;
siblingController = this.friend;
};
spyOn(MeController.prototype, '$onInit').andCallThrough();
spyOn(MeController.prototype, '$onInit').and.callThrough();
angular.module('my', [])
.directive('me', function() {
@@ -5786,7 +6143,7 @@ describe('$compile', function() {
parentController = this.container;
siblingController = this.friend;
};
spyOn(MeController.prototype, '$onInit').andCallThrough();
spyOn(MeController.prototype, '$onInit').and.callThrough();
angular.module('my', [])
.directive('me', function() {
@@ -5832,7 +6189,7 @@ describe('$compile', function() {
siblingController = this.friend;
}
};
spyOn(meController, '$onInit').andCallThrough();
spyOn(meController, '$onInit').and.callThrough();
return meController;
}
@@ -6596,8 +6953,8 @@ describe('$compile', function() {
$rootScope.x = 'root';
$rootScope.$apply();
expect(element.text()).toEqual('W:iso-1-2;T:root-2-3;');
expect(jqLite(jqLite(element.find('li')[1]).contents()[0]).text()).toEqual('T:root-2-3');
expect(jqLite(element.find('span')[0]).text()).toEqual(';');
expect(jqLite(element.find('span')[0]).text()).toEqual('T:root-2-3');
expect(jqLite(element.find('span')[1]).text()).toEqual(';');
});
});
@@ -6635,37 +6992,6 @@ describe('$compile', function() {
});
it('should not merge text elements from transcluded content', function() {
module(function() {
directive('foo', valueFn({
transclude: 'content',
template: '<div>This is before {{before}}. </div>',
link: function(scope, element, attr, ctrls, $transclude) {
var futureParent = element.children().eq(0);
$transclude(function(clone) {
futureParent.append(clone);
}, futureParent);
},
scope: true
}));
});
inject(function($rootScope, $compile) {
element = $compile('<div><div foo>This is after {{after}}</div></div>')($rootScope);
$rootScope.before = "BEFORE";
$rootScope.after = "AFTER";
$rootScope.$apply();
expect(element.text()).toEqual('This is before BEFORE. This is after AFTER');
$rootScope.before = "Not-Before";
$rootScope.after = "AfTeR";
$rootScope.$$childHead.before = "BeFoRe";
$rootScope.$$childHead.after = "Not-After";
$rootScope.$apply();
expect(element.text()).toEqual('This is before BeFoRe. This is after AfTeR');
});
});
it('should only allow one content transclusion per element', function() {
module(function() {
directive('first', valueFn({
@@ -6964,11 +7290,11 @@ describe('$compile', function() {
transclude: true,
replace: true,
scope: true,
template: '<div><span>I:{{$$transcluded}}</span><span ng-transclude></span></div>'
template: '<div><span>I:{{$$transcluded}}</span><div ng-transclude></div></div>'
};
});
});
inject(function($rootScope, $compile) {
inject(function(log, $rootScope, $compile) {
element = $compile('<div><div trans>T:{{$$transcluded}}</div></div>')($rootScope);
$rootScope.$apply();
expect(jqLite(element.find('span')[0]).text()).toEqual('I:');
@@ -6987,10 +7313,10 @@ describe('$compile', function() {
};
});
});
inject(function($rootScope, $compile) {
inject(function(log, $rootScope, $compile) {
element = $compile('<div trans>unicorn!</div>')($rootScope);
$rootScope.$apply();
expect(sortedHtml(element.html())).toEqual('<div ng-transclude="">unicorn!</div>');
expect(sortedHtml(element.html())).toEqual('<div ng-transclude=""><span>unicorn!</span></div>');
});
});
@@ -8891,7 +9217,7 @@ describe('$compile', function() {
element = $compile('<img src="{{testUrl}}"></img>')($rootScope);
$rootScope.testUrl = "someUrl";
$$sanitizeUri.andReturn('someSanitizedUrl');
$$sanitizeUri.and.returnValue('someSanitizedUrl');
$rootScope.$apply();
expect(element.attr('src')).toBe('someSanitizedUrl');
expect($$sanitizeUri).toHaveBeenCalledWith($rootScope.testUrl, true);
@@ -8921,7 +9247,7 @@ describe('$compile', function() {
element = $compile('<img srcset="{{testUrl}}"></img>')($rootScope);
$rootScope.testUrl = "someUrl";
$$sanitizeUri.andReturn('someSanitizedUrl');
$$sanitizeUri.and.returnValue('someSanitizedUrl');
$rootScope.$apply();
expect(element.attr('srcset')).toBe('someSanitizedUrl');
expect($$sanitizeUri).toHaveBeenCalledWith($rootScope.testUrl, true);
@@ -9013,7 +9339,7 @@ describe('$compile', function() {
element = $compile('<a href="{{testUrl}}"></a>')($rootScope);
$rootScope.testUrl = "someUrl";
$$sanitizeUri.andReturn('someSanitizedUrl');
$$sanitizeUri.and.returnValue('someSanitizedUrl');
$rootScope.$apply();
expect(element.attr('href')).toBe('someSanitizedUrl');
expect($$sanitizeUri).toHaveBeenCalledWith($rootScope.testUrl, false);
@@ -9029,7 +9355,7 @@ describe('$compile', function() {
element = $compile('<a ng-href="{{testUrl}}"></a>')($rootScope);
$rootScope.testUrl = "someUrl";
$$sanitizeUri.andReturn('someSanitizedUrl');
$$sanitizeUri.and.returnValue('someSanitizedUrl');
$rootScope.$apply();
expect(element.attr('href')).toBe('someSanitizedUrl');
expect($$sanitizeUri).toHaveBeenCalledWith($rootScope.testUrl, false);
@@ -9045,7 +9371,7 @@ describe('$compile', function() {
element = $compile('<svg><a xlink:href="" ng-href="{{ testUrl }}"></a></svg>')($rootScope);
$rootScope.testUrl = "evilUrl";
$$sanitizeUri.andReturn('someSanitizedUrl');
$$sanitizeUri.and.returnValue('someSanitizedUrl');
$rootScope.$apply();
expect(element.find('a').prop('href').baseVal).toBe('someSanitizedUrl');
expect($$sanitizeUri).toHaveBeenCalledWith($rootScope.testUrl, false);
@@ -9062,7 +9388,7 @@ describe('$compile', function() {
element = $compile('<svg><a xlink:href="" ng-href="{{ testUrl }}"></a></svg>')($rootScope);
$rootScope.testUrl = "evilUrl";
$$sanitizeUri.andReturn('someSanitizedUrl');
$$sanitizeUri.and.returnValue('someSanitizedUrl');
$rootScope.$apply();
expect(element.find('a').prop('href').baseVal).toBe('someSanitizedUrl');
expect($$sanitizeUri).toHaveBeenCalledWith($rootScope.testUrl, false);
+2 -2
View File
@@ -83,7 +83,7 @@ describe('a', function() {
it('should not preventDefault if anchor element is replaced with href-containing element', function() {
spyOn(jqLite.prototype, 'on').andCallThrough();
spyOn(jqLite.prototype, 'on').and.callThrough();
element = $compile('<a link-to="https://www.google.com">')($rootScope);
$rootScope.$digest();
@@ -100,7 +100,7 @@ describe('a', function() {
it('should preventDefault if anchor element is replaced with element without href attribute', function() {
spyOn(jqLite.prototype, 'on').andCallThrough();
spyOn(jqLite.prototype, 'on').and.callThrough();
element = $compile('<a link-not="https://www.google.com">')($rootScope);
$rootScope.$digest();
+67 -63
View File
@@ -330,7 +330,7 @@ describe('form', function() {
var inputElm = form.find('input').eq(0);
changeInputValue(inputElm, 'a');
scope.submit = jasmine.createSpy('submit').andCallFake(function() {
scope.submit = jasmine.createSpy('submit').and.callFake(function() {
expect(scope.name).toEqual('a');
});
browserTrigger(form, 'submit');
@@ -377,7 +377,8 @@ describe('form', function() {
describe('preventing default submission', function() {
it('should prevent form submission', function() {
it('should prevent form submission', function(done) {
var job = createAsync(done);
var nextTurn = false,
submitted = false,
reloadPrevented;
@@ -402,77 +403,80 @@ describe('form', function() {
// let the browser process all events (and potentially reload the page)
setTimeout(function() { nextTurn = true;});
waitsFor(function() { return nextTurn; });
runs(function() {
job.waitsFor(function() { return nextTurn; })
.runs(function() {
expect(reloadPrevented).toBe(true);
expect(submitted).toBe(true);
// prevent mem leak in test
removeEventListenerFn(doc[0], 'submit', assertPreventDefaultListener);
})
.done();
job.start();
});
it('should prevent the default when the form is destroyed by a submission via a click event', function(done) {
inject(function($timeout) {
doc = jqLite('<div>' +
'<form ng-submit="submitMe()">' +
'<button ng-click="destroy()"></button>' +
'</form>' +
'</div>');
var form = doc.find('form'),
destroyed = false,
nextTurn = false,
submitted = false,
reloadPrevented;
scope.destroy = function() {
// yes, I know, scope methods should not do direct DOM manipulation, but I wanted to keep
// this test small. Imagine that the destroy action will cause a model change (e.g.
// $location change) that will cause some directive to destroy the dom (e.g. ngView+$route)
doc.empty();
destroyed = true;
};
scope.submitMe = function() {
submitted = true;
};
var assertPreventDefaultListener = function(e) {
reloadPrevented = e.defaultPrevented || (e.returnValue === false);
};
$compile(doc)(scope);
addEventListenerFn(form[0], 'submit', assertPreventDefaultListener);
browserTrigger(doc.find('button'), 'click');
// let the browser process all events (and potentially reload the page)
setTimeout(function() { nextTurn = true;}, 100);
var job = createAsync(done);
job.waitsFor(function() { return nextTurn; })
.runs(function() {
expect(doc.html()).toBe('');
expect(destroyed).toBe(true);
expect(submitted).toBe(false); // this is known corner-case that is not currently handled
// the issue is that the submit listener is destroyed before
// the event propagates there. we can fix this if we see
// the issue in the wild, I'm not going to bother to do it
// now. (i)
// prevent mem leak in test
removeEventListenerFn(form[0], 'submit', assertPreventDefaultListener);
})
.done();
job.start();
});
});
it('should prevent the default when the form is destroyed by a submission via a click event',
inject(function($timeout) {
doc = jqLite('<div>' +
'<form ng-submit="submitMe()">' +
'<button ng-click="destroy()"></button>' +
'</form>' +
'</div>');
var form = doc.find('form'),
destroyed = false,
nextTurn = false,
submitted = false,
reloadPrevented;
scope.destroy = function() {
// yes, I know, scope methods should not do direct DOM manipulation, but I wanted to keep
// this test small. Imagine that the destroy action will cause a model change (e.g.
// $location change) that will cause some directive to destroy the dom (e.g. ngView+$route)
doc.empty();
destroyed = true;
};
scope.submitMe = function() {
submitted = true;
};
var assertPreventDefaultListener = function(e) {
reloadPrevented = e.defaultPrevented || (e.returnValue === false);
};
$compile(doc)(scope);
addEventListenerFn(form[0], 'submit', assertPreventDefaultListener);
browserTrigger(doc.find('button'), 'click');
// let the browser process all events (and potentially reload the page)
setTimeout(function() { nextTurn = true;}, 100);
waitsFor(function() { return nextTurn; });
runs(function() {
expect(doc.html()).toBe('');
expect(destroyed).toBe(true);
expect(submitted).toBe(false); // this is known corner-case that is not currently handled
// the issue is that the submit listener is destroyed before
// the event propagates there. we can fix this if we see
// the issue in the wild, I'm not going to bother to do it
// now. (i)
// prevent mem leak in test
removeEventListenerFn(form[0], 'submit', assertPreventDefaultListener);
});
}));
it('should NOT prevent form submission if action attribute present', function() {
var callback = jasmine.createSpy('submit').andCallFake(function(event) {
var callback = jasmine.createSpy('submit').and.callFake(function(event) {
expect(event.isDefaultPrevented()).toBe(false);
event.preventDefault();
});
+18 -18
View File
@@ -1,18 +1,11 @@
'use strict';
/* globals getInputCompileHelper: false */
/* globals generateInputCompilerHelper: false */
describe('input', function() {
var helper, $compile, $rootScope, $browser, $sniffer, $timeout, $q;
beforeEach(function() {
helper = getInputCompileHelper(this);
});
afterEach(function() {
helper.dealoc();
});
var helper = {}, $compile, $rootScope, $browser, $sniffer, $timeout, $q;
generateInputCompilerHelper(helper);
beforeEach(inject(function(_$compile_, _$rootScope_, _$browser_, _$sniffer_, _$timeout_, _$q_) {
$compile = _$compile_;
@@ -34,15 +27,22 @@ describe('input', function() {
it('should not set readonly or disabled property on ie7', function() {
this.addMatchers({
toBeOff: function(attributeName) {
var actualValue = this.actual.attr(attributeName);
this.message = function() {
return "Attribute '" + attributeName + "' expected to be off but was '" + actualValue +
"' in: " + angular.mock.dump(this.actual);
};
jasmine.addMatchers({
toBeOff: function() {
return {
compare: function(actual, attributeName) {
var actualValue = actual.attr(attributeName);
var message = function() {
return "Attribute '" + attributeName + "' expected to be off but was '" + actualValue +
"' in: " + angular.mock.dump(actual);
};
return !actualValue || actualValue == 'false';
return {
pass: !actualValue || actualValue == 'false',
message: message
};
}
};
}
});
+4 -11
View File
@@ -1,19 +1,12 @@
'use strict';
/* globals getInputCompileHelper: false */
/* globals generateInputCompilerHelper: false */
describe('ngChange', function() {
var helper, $rootScope;
beforeEach(function() {
helper = getInputCompileHelper(this);
});
afterEach(function() {
helper.dealoc();
});
var helper = {}, $rootScope;
generateInputCompilerHelper(helper);
beforeEach(inject(function(_$rootScope_) {
$rootScope = _$rootScope_;
@@ -22,7 +15,7 @@ describe('ngChange', function() {
it('should $eval expression after new value is set in the model', function() {
helper.compileInput('<input type="text" ng-model="value" ng-change="change()" />');
$rootScope.change = jasmine.createSpy('change').andCallFake(function() {
$rootScope.change = jasmine.createSpy('change').and.callFake(function() {
expect($rootScope.value).toBe('new value');
});
+2 -2
View File
@@ -78,7 +78,7 @@ describe('event directives', function() {
it('should call the listener synchronously inside of $apply if outside of $apply',
inject(function($rootScope, $compile) {
element = $compile('<input type="text" ng-focus="focus()" ng-model="value">')($rootScope);
$rootScope.focus = jasmine.createSpy('focus').andCallFake(function() {
$rootScope.focus = jasmine.createSpy('focus').and.callFake(function() {
$rootScope.value = 'newValue';
});
@@ -139,7 +139,7 @@ describe('event directives', function() {
it('should call the listener synchronously inside of $apply if outside of $apply',
inject(function($rootScope, $compile) {
element = $compile('<input type="text" ng-blur="blur()" ng-model="value">')($rootScope);
$rootScope.blur = jasmine.createSpy('blur').andCallFake(function() {
$rootScope.blur = jasmine.createSpy('blur').and.callFake(function() {
$rootScope.value = 'newValue';
});
+5 -5
View File
@@ -111,7 +111,7 @@ describe('ngInclude', function() {
it('should fire $includeContentRequested event on scope after making the xhr call', inject(
function($rootScope, $compile, $httpBackend) {
var contentRequestedSpy = jasmine.createSpy('content requested').andCallFake(function(event) {
var contentRequestedSpy = jasmine.createSpy('content requested').and.callFake(function(event) {
expect(event.targetScope).toBe($rootScope);
});
@@ -128,7 +128,7 @@ describe('ngInclude', function() {
it('should fire $includeContentLoaded event on child scope after linking the content', inject(
function($rootScope, $compile, $templateCache) {
var contentLoadedSpy = jasmine.createSpy('content loaded').andCallFake(function(event) {
var contentLoadedSpy = jasmine.createSpy('content loaded').and.callFake(function(event) {
expect(event.targetScope.$parent).toBe($rootScope);
expect(element.text()).toBe('partial content');
});
@@ -401,7 +401,7 @@ describe('ngInclude', function() {
it('should not compile template if original scope is destroyed', function() {
module(function($provide) {
$provide.decorator('$compile', function($delegate) {
var result = jasmine.createSpy('$compile').andCallFake($delegate);
var result = jasmine.createSpy('$compile').and.callFake($delegate);
result.$$createComment = $delegate.$$createComment;
return result;
});
@@ -413,7 +413,7 @@ describe('ngInclude', function() {
$rootScope.$digest();
$rootScope.show = false;
$rootScope.$digest();
$compile.reset();
$compile.calls.reset();
$httpBackend.flush();
expect($compile).not.toHaveBeenCalled();
});
@@ -491,7 +491,7 @@ describe('ngInclude', function() {
$rootScope.$digest();
expect(autoScrollSpy).toHaveBeenCalled();
expect(autoScrollSpy.callCount).toBe(3);
expect(autoScrollSpy).toHaveBeenCalledTimes(3);
}));
+3 -10
View File
@@ -1,19 +1,12 @@
'use strict';
/* globals getInputCompileHelper: false */
/* globals generateInputCompilerHelper: false */
describe('ngList', function() {
var helper, $rootScope;
beforeEach(function() {
helper = getInputCompileHelper(this);
});
afterEach(function() {
helper.dealoc();
});
var helper = {}, $rootScope;
generateInputCompilerHelper(helper);
beforeEach(inject(function(_$rootScope_) {
$rootScope = _$rootScope_;
+16 -23
View File
@@ -1,6 +1,6 @@
'use strict';
/* globals getInputCompileHelper: false */
/* globals generateInputCompilerHelper: false */
describe('ngModel', function() {
@@ -220,7 +220,7 @@ describe('ngModel', function() {
ctrl.$viewChangeListeners.push(spy);
ctrl.$setViewValue('val');
expect(spy).toHaveBeenCalledOnce();
spy.reset();
spy.calls.reset();
// invalid
ctrl.$parsers.push(function() {return undefined;});
@@ -270,7 +270,7 @@ describe('ngModel', function() {
// further digests
scope.$apply('value = "aaa"');
expect(ctrl.$viewValue).toBe('aaa');
ctrl.$render.reset();
ctrl.$render.calls.reset();
ctrl.$setViewValue('cccc');
expect(ctrl.$modelValue).toBeUndefined();
@@ -286,7 +286,7 @@ describe('ngModel', function() {
expect(ctrl.$dirty).toBe(true);
expect(parentFormCtrl.$setDirty).toHaveBeenCalledOnce();
parentFormCtrl.$setDirty.reset();
parentFormCtrl.$setDirty.calls.reset();
ctrl.$setViewValue('');
expect(ctrl.$pristine).toBe(false);
expect(ctrl.$dirty).toBe(true);
@@ -474,7 +474,7 @@ describe('ngModel', function() {
scope.$apply('value = 3');
expect(ctrl.$render).toHaveBeenCalledOnce();
ctrl.$render.reset();
ctrl.$render.calls.reset();
ctrl.$formatters.push(function() {return 3;});
scope.$apply('value = 5');
@@ -756,7 +756,7 @@ describe('ngModel', function() {
it('should always perform validations using the parsed model value', function() {
var captures;
ctrl.$validators.raw = function() {
captures = arguments;
captures = Array.prototype.slice.call(arguments);
return captures[0];
};
@@ -773,7 +773,7 @@ describe('ngModel', function() {
it('should always perform validations using the formatted view value', function() {
var captures;
ctrl.$validators.raw = function() {
captures = arguments;
captures = Array.prototype.slice.call(arguments);
return captures[0];
};
@@ -1197,12 +1197,12 @@ describe('ngModel', function() {
return true;
};
spyOn(ctrl.$validators, 'mock').andCallThrough();
spyOn(ctrl.$validators, 'mock').and.callThrough();
ctrl.$setViewValue('ab');
expect(ctrl.$validators.mock).toHaveBeenCalledWith('a', 'a');
expect(ctrl.$validators.mock.calls.length).toEqual(2);
expect(ctrl.$validators.mock).toHaveBeenCalledTimes(2);
});
@@ -1219,17 +1219,17 @@ describe('ngModel', function() {
return true;
};
spyOn(ctrl.$validators, 'mock').andCallThrough();
spyOn(ctrl.$validators, 'mock').and.callThrough();
ctrl.$setViewValue('a');
expect(ctrl.$validators.mock).toHaveBeenCalledWith('a', 'a');
expect(ctrl.$validators.mock.calls.length).toEqual(1);
expect(ctrl.$validators.mock).toHaveBeenCalledTimes(1);
ctrl.$setViewValue('ab');
expect(ctrl.$validators.mock).toHaveBeenCalledWith('a', 'ab');
expect(ctrl.$validators.mock.calls.length).toEqual(2);
expect(ctrl.$validators.mock).toHaveBeenCalledTimes(2);
});
it('should validate correctly when $parser name equals $validator key', function() {
@@ -1772,16 +1772,9 @@ describe('ngModel', function() {
describe('ngModelOptions attributes', function() {
var helper, $rootScope, $compile, $timeout, $q;
beforeEach(function() {
helper = getInputCompileHelper(this);
});
afterEach(function() {
helper.dealoc();
});
var helper = {}, $rootScope, $compile, $timeout, $q;
generateInputCompilerHelper(helper);
beforeEach(inject(function(_$compile_, _$rootScope_, _$timeout_, _$q_) {
$compile = _$compile_;
@@ -2240,7 +2233,7 @@ describe('ngModelOptions attributes', function() {
'<input type="text" ng-model="name" ' +
'ng-model-options="{ getterSetter: true }" />');
var spy = $rootScope.name = jasmine.createSpy('setterSpy').andCallFake(function() {
var spy = $rootScope.name = jasmine.createSpy('setterSpy').and.callFake(function() {
return 'b';
});
$rootScope.$apply();
@@ -2291,7 +2284,7 @@ describe('ngModelOptions attributes', function() {
return this.value;
}
};
spyOn($rootScope.someService, 'getterSetter').andCallThrough();
spyOn($rootScope.someService, 'getterSetter').and.callThrough();
$rootScope.$apply();
expect(inputElm.val()).toBe('a');
+87 -67
View File
@@ -19,87 +19,107 @@ describe('ngOptions', function() {
beforeEach(function() {
this.addMatchers({
toEqualSelectValue: function(value, multiple) {
var errors = [];
var actual = this.actual.val();
jasmine.addMatchers({
toEqualSelectValue: function() {
return {
compare: function(_actual_, value, multiple) {
var errors = [];
var actual = _actual_.val();
if (multiple) {
value = value.map(function(val) { return hashKey(val); });
actual = actual || [];
} else {
value = hashKey(value);
}
if (multiple) {
value = value.map(function(val) { return hashKey(val); });
actual = actual || [];
} else {
value = hashKey(value);
}
if (!equals(actual, value)) {
errors.push('Expected select value "' + actual + '" to equal "' + value + '"');
}
this.message = function() {
return errors.join('\n');
if (!equals(actual, value)) {
errors.push('Expected select value "' + actual + '" to equal "' + value + '"');
}
var message = function() {
return errors.join('\n');
};
return { pass: errors.length === 0, message: message };
}
};
return errors.length === 0;
},
toEqualOption: function(value, text, label) {
var errors = [];
var hash = hashKey(value);
if (this.actual.attr('value') !== hash) {
errors.push('Expected option value "' + this.actual.attr('value') + '" to equal "' + hash + '"');
}
if (text && this.actual.text() !== text) {
errors.push('Expected option text "' + this.actual.text() + '" to equal "' + text + '"');
}
if (label && this.actual.attr('label') !== label) {
errors.push('Expected option label "' + this.actual.attr('label') + '" to equal "' + label + '"');
}
toEqualOption: function() {
return {
compare: function(actual, value, text, label) {
var errors = [];
var hash = hashKey(value);
if (actual.attr('value') !== hash) {
errors.push('Expected option value "' + actual.attr('value') + '" to equal "' + hash + '"');
}
if (text && actual.text() !== text) {
errors.push('Expected option text "' + actual.text() + '" to equal "' + text + '"');
}
if (label && actual.attr('label') !== label) {
errors.push('Expected option label "' + actual.attr('label') + '" to equal "' + label + '"');
}
this.message = function() {
return errors.join('\n');
var message = function() {
return errors.join('\n');
};
return { pass: errors.length === 0, message: message };
}
};
return errors.length === 0;
},
toEqualTrackedOption: function(value, text, label) {
var errors = [];
if (this.actual.attr('value') !== '' + value) {
errors.push('Expected option value "' + this.actual.attr('value') + '" to equal "' + value + '"');
}
if (text && this.actual.text() !== text) {
errors.push('Expected option text "' + this.actual.text() + '" to equal "' + text + '"');
}
if (label && this.actual.attr('label') !== label) {
errors.push('Expected option label "' + this.actual.attr('label') + '" to equal "' + label + '"');
}
toEqualTrackedOption: function() {
return {
compare: function(actual, value, text, label) {
var errors = [];
if (actual.attr('value') !== '' + value) {
errors.push('Expected option value "' + actual.attr('value') + '" to equal "' + value + '"');
}
if (text && actual.text() !== text) {
errors.push('Expected option text "' + actual.text() + '" to equal "' + text + '"');
}
if (label && actual.attr('label') !== label) {
errors.push('Expected option label "' + actual.attr('label') + '" to equal "' + label + '"');
}
this.message = function() {
return errors.join('\n');
var message = function() {
return errors.join('\n');
};
return { pass: errors.length === 0, message: message };
}
};
return errors.length === 0;
},
toEqualUnknownOption: function() {
var errors = [];
if (this.actual.attr('value') !== '?') {
errors.push('Expected option value "' + this.actual.attr('value') + '" to equal "?"');
}
return {
compare: function(actual) {
var errors = [];
if (actual.attr('value') !== '?') {
errors.push('Expected option value "' + actual.attr('value') + '" to equal "?"');
}
this.message = function() {
return errors.join('\n');
var message = function() {
return errors.join('\n');
};
return { pass: errors.length === 0, message: message };
}
};
return errors.length === 0;
},
toEqualUnknownValue: function(value) {
var errors = [];
if (this.actual !== '?') {
errors.push('Expected select value "' + this.actual + '" to equal "?"');
}
toEqualUnknownValue: function() {
return {
compare: function(actual, value) {
var errors = [];
if (actual !== '?') {
errors.push('Expected select value "' + actual + '" to equal "?"');
}
this.message = function() {
return errors.join('\n');
var message = function() {
return errors.join('\n');
};
return { pass: errors.length === 0, message: message };
}
};
return errors.length === 0;
}
});
});
@@ -534,7 +554,7 @@ describe('ngOptions', function() {
'ng-options': 'value as createLabel(value) for value in array',
'ng-model': 'selected'
});
scope.createLabel = jasmine.createSpy('createLabel').andCallFake(function(value) { return value; });
scope.createLabel = jasmine.createSpy('createLabel').and.callFake(function(value) { return value; });
scope.array = ['a', 'b', 'c'];
scope.array.$$private = 'do not watch';
scope.array.$property = 'do not watch';
@@ -556,7 +576,7 @@ describe('ngOptions', function() {
'ng-options': 'key as createLabel(key) for (key, value) in object',
'ng-model': 'selected'
});
scope.createLabel = jasmine.createSpy('createLabel').andCallFake(function(value) { return value; });
scope.createLabel = jasmine.createSpy('createLabel').and.callFake(function(value) { return value; });
scope.object = {'regularProperty': 'visible', '$$private': 'invisible', '$property': 'invisible'};
scope.selected = 'regularProperty';
scope.$digest();
+51 -37
View File
@@ -38,8 +38,8 @@ describe('select', function() {
pre: function(scope, element, attrs, ctrl) {
selectCtrl = ctrl;
renderSpy = jasmine.createSpy('renderSpy');
selectCtrl.ngModelCtrl.$render = renderSpy.andCallFake(selectCtrl.ngModelCtrl.$render);
spyOn(selectCtrl, 'writeValue').andCallThrough();
selectCtrl.ngModelCtrl.$render = renderSpy.and.callFake(selectCtrl.ngModelCtrl.$render);
spyOn(selectCtrl, 'writeValue').and.callThrough();
}
}
};
@@ -71,40 +71,54 @@ describe('select', function() {
beforeEach(function() {
this.addMatchers({
toEqualSelect: function(expected) {
var actualValues = [],
expectedValues = [].slice.call(arguments);
jasmine.addMatchers({
toEqualSelect: function() {
return {
compare: function(actual, expected) {
var actualValues = [],
expectedValues = [].slice.call(arguments, 1);
forEach(this.actual.find('option'), function(option) {
actualValues.push(option.selected ? [option.value] : option.value);
});
forEach(actual.find('option'), function(option) {
actualValues.push(option.selected ? [option.value] : option.value);
});
this.message = function() {
return 'Expected ' + toJson(actualValues) + ' to equal ' + toJson(expectedValues) + '.';
var message = function() {
return 'Expected ' + toJson(actualValues) + ' to equal ' + toJson(expectedValues) + '.';
};
return {
pass: equals(expectedValues, actualValues),
message: message
};
}
};
return equals(expectedValues, actualValues);
},
toEqualSelectWithOptions: function(expected) {
var actualValues = {};
var optionGroup;
var optionValue;
toEqualSelectWithOptions: function() {
return {
compare: function(actual, expected) {
var actualValues = {};
var optionGroup;
var optionValue;
forEach(this.actual.find('option'), function(option) {
optionGroup = option.parentNode.label || '';
actualValues[optionGroup] = actualValues[optionGroup] || [];
// IE9 doesn't populate the label property from the text property like other browsers
optionValue = option.label || option.text;
actualValues[optionGroup].push(option.selected ? [optionValue] : optionValue);
});
forEach(actual.find('option'), function(option) {
optionGroup = option.parentNode.label || '';
actualValues[optionGroup] = actualValues[optionGroup] || [];
// IE9 doesn't populate the label property from the text property like other browsers
optionValue = option.label || option.text;
actualValues[optionGroup].push(option.selected ? [optionValue] : optionValue);
});
this.message = function() {
return 'Expected ' + toJson(actualValues) + ' to equal ' + toJson(expected) + '.';
var message = function() {
return 'Expected ' + toJson(actualValues) + ' to equal ' + toJson(expected) + '.';
};
return {
pass: equals(expected, actualValues),
message: message
};
}
};
return equals(expected, actualValues);
}
});
@@ -1025,7 +1039,7 @@ describe('select', function() {
'</select>');
ngModelCtrl = element.controller('ngModel');
spyOn(ngModelCtrl, '$render').andCallThrough();
spyOn(ngModelCtrl, '$render').and.callThrough();
});
@@ -1033,17 +1047,17 @@ describe('select', function() {
scope.$apply(function() {
scope.selection = ['A'];
});
expect(ngModelCtrl.$render.calls.length).toBe(1);
expect(ngModelCtrl.$render).toHaveBeenCalledTimes(1);
scope.$apply(function() {
scope.selection = ['A', 'B'];
});
expect(ngModelCtrl.$render.calls.length).toBe(2);
expect(ngModelCtrl.$render).toHaveBeenCalledTimes(2);
scope.$apply(function() {
scope.selection = [];
});
expect(ngModelCtrl.$render.calls.length).toBe(3);
expect(ngModelCtrl.$render).toHaveBeenCalledTimes(3);
});
@@ -1051,17 +1065,17 @@ describe('select', function() {
scope.$apply(function() {
scope.selection = ['A'];
});
expect(ngModelCtrl.$render.calls.length).toBe(1);
expect(ngModelCtrl.$render).toHaveBeenCalledTimes(1);
scope.$apply(function() {
scope.selection.push('B');
});
expect(ngModelCtrl.$render.calls.length).toBe(2);
expect(ngModelCtrl.$render).toHaveBeenCalledTimes(2);
scope.$apply(function() {
scope.selection.length = 0;
});
expect(ngModelCtrl.$render.calls.length).toBe(3);
expect(ngModelCtrl.$render).toHaveBeenCalledTimes(3);
});
});
@@ -1122,7 +1136,7 @@ describe('select', function() {
'</select>');
var selectCtrl = element.controller('select');
spyOn(selectCtrl, 'removeOption').andCallThrough();
spyOn(selectCtrl, 'removeOption').and.callThrough();
scope.$digest();
expect(scope.selected).toBeUndefined();
@@ -1171,7 +1185,7 @@ describe('select', function() {
'</select>');
var selectCtrl = element.controller('select');
spyOn(selectCtrl, 'removeOption').andCallThrough();
spyOn(selectCtrl, 'removeOption').and.callThrough();
scope.$digest();
expect(scope.selected).toBeUndefined();
+8 -15
View File
@@ -1,19 +1,12 @@
'use strict';
/* globals getInputCompileHelper: false */
/* globals generateInputCompilerHelper: false */
describe('validators', function() {
var helper, $rootScope;
beforeEach(function() {
helper = getInputCompileHelper(this);
});
afterEach(function() {
helper.dealoc();
});
var helper = {}, $rootScope;
generateInputCompilerHelper(helper);
beforeEach(inject(function(_$rootScope_) {
$rootScope = _$rootScope_;
@@ -143,7 +136,7 @@ describe('validators', function() {
expect(function() {
var inputElm = helper.compileInput('<input type="text" ng-model="foo" ng-pattern="fooRegexp" />');
$rootScope.$apply("foo = 'bar'");
}).not.toThrowMatching(/^\[ngPattern:noregexp\] Expected fooRegexp to be a RegExp but was/);
}).not.toThrowError(/^\[ngPattern:noregexp\] Expected fooRegexp to be a RegExp but was/);
});
@@ -154,7 +147,7 @@ describe('validators', function() {
$rootScope.fooRegexp = {};
$rootScope.foo = 'bar';
});
}).toThrowMatching(/^\[ngPattern:noregexp\] Expected fooRegexp to be a RegExp but was/);
}).toThrowError(/^\[ngPattern:noregexp\] Expected fooRegexp to be a RegExp but was/);
});
@@ -292,7 +285,7 @@ describe('validators', function() {
var inputElm = helper.compileInput('<input type="text" name="input" ng-model="value" minlength="3" />');
var ctrl = inputElm.controller('ngModel');
spyOn(ctrl, '$isEmpty').andCallThrough();
spyOn(ctrl, '$isEmpty').and.callThrough();
ctrl.$parsers.push(function(value) {
return value + '678';
@@ -480,7 +473,7 @@ describe('validators', function() {
var inputElm = helper.compileInput('<input type="text" name="input" ng-model="value" maxlength="10" />');
var ctrl = inputElm.controller('ngModel');
spyOn(ctrl, '$isEmpty').andCallThrough();
spyOn(ctrl, '$isEmpty').and.callThrough();
ctrl.$parsers.push(function(value) {
return value + '678';
@@ -606,7 +599,7 @@ describe('validators', function() {
var inputElm = helper.compileInput('<input type="text" name="input" ng-model="value" required />');
var ctrl = inputElm.controller('ngModel');
spyOn(ctrl, '$isEmpty').andCallThrough();
spyOn(ctrl, '$isEmpty').and.callThrough();
ctrl.$parsers.push(function(value) {
return value + '678';
+5 -1
View File
@@ -35,7 +35,11 @@ describe('filters', function() {
it('should format according to different patterns', function() {
pattern.gSize = 2;
var num = formatNumber(1234567.89, pattern, ',', '.');
var num = formatNumber(99, pattern, ',', '.');
expect(num).toBe('99');
num = formatNumber(888, pattern, ',', '.');
expect(num).toBe('888');
num = formatNumber(1234567.89, pattern, ',', '.');
expect(num).toBe('12,34,567.89');
num = formatNumber(1234.56, pattern, ',', '.');
expect(num).toBe('1,234.56');
+24 -24
View File
@@ -12,15 +12,15 @@ describe('$httpBackend', function() {
$browser = $injector.get('$browser');
fakeDocument = {
$$scripts: [],
createElement: jasmine.createSpy('createElement').andCallFake(function() {
createElement: jasmine.createSpy('createElement').and.callFake(function() {
// Return a proper script element...
return document.createElement(arguments[0]);
}),
body: {
appendChild: jasmine.createSpy('body.appendChild').andCallFake(function(script) {
appendChild: jasmine.createSpy('body.appendChild').and.callFake(function(script) {
fakeDocument.$$scripts.push(script);
}),
removeChild: jasmine.createSpy('body.removeChild').andCallFake(function(script) {
removeChild: jasmine.createSpy('body.removeChild').and.callFake(function(script) {
var index = fakeDocument.$$scripts.indexOf(script);
if (index != -1) {
fakeDocument.$$scripts.splice(index, 1);
@@ -70,7 +70,7 @@ describe('$httpBackend', function() {
});
it('should call completion function with xhr.statusText if present', function() {
callback.andCallFake(function(status, response, headers, statusText) {
callback.and.callFake(function(status, response, headers, statusText) {
expect(statusText).toBe('OK');
});
@@ -82,7 +82,7 @@ describe('$httpBackend', function() {
});
it('should call completion function with empty string if not present', function() {
callback.andCallFake(function(status, response, headers, statusText) {
callback.and.callFake(function(status, response, headers, statusText) {
expect(statusText).toBe('');
});
@@ -94,7 +94,7 @@ describe('$httpBackend', function() {
it('should normalize IE\'s 1223 status code into 204', function() {
callback.andCallFake(function(status) {
callback.and.callFake(function(status) {
expect(status).toBe(204);
});
@@ -135,7 +135,7 @@ describe('$httpBackend', function() {
});
it('should not try to read response data when request is aborted', function() {
callback.andCallFake(function(status, response, headers) {
callback.and.callFake(function(status, response, headers) {
expect(status).toBe(-1);
expect(response).toBe(null);
expect(headers).toBe(null);
@@ -153,7 +153,7 @@ describe('$httpBackend', function() {
});
it('should abort request on timeout', function() {
callback.andCallFake(function(status, response) {
callback.and.callFake(function(status, response) {
expect(status).toBe(-1);
});
@@ -173,7 +173,7 @@ describe('$httpBackend', function() {
it('should abort request on timeout promise resolution', inject(function($timeout) {
callback.andCallFake(function(status, response) {
callback.and.callFake(function(status, response) {
expect(status).toBe(-1);
});
@@ -191,7 +191,7 @@ describe('$httpBackend', function() {
it('should not abort resolved request on timeout promise resolution', inject(function($timeout) {
callback.andCallFake(function(status, response) {
callback.and.callFake(function(status, response) {
expect(status).toBe(200);
});
@@ -209,7 +209,7 @@ describe('$httpBackend', function() {
it('should cancel timeout on completion', function() {
callback.andCallFake(function(status, response) {
callback.and.callFake(function(status, response) {
expect(status).toBe(200);
});
@@ -234,7 +234,7 @@ describe('$httpBackend', function() {
});
it('should call $xhrFactory with method and url', function() {
var mockXhrFactory = jasmine.createSpy('mockXhrFactory').andCallFake(createMockXhr);
var mockXhrFactory = jasmine.createSpy('mockXhrFactory').and.callFake(createMockXhr);
$backend = createHttpBackend($browser, mockXhrFactory, $browser.defer, callbacks, fakeDocument);
$backend('GET', '/some-url', 'some-data', noop);
expect(mockXhrFactory).toHaveBeenCalledWith('GET', '/some-url');
@@ -249,7 +249,7 @@ describe('$httpBackend', function() {
var xhrInstance = MockXhr.$$lastInstance;
expect(xhrInstance.responseType).toBe('blob');
callback.andCallFake(function(status, response) {
callback.and.callFake(function(status, response) {
expect(response).toBe(xhrInstance.response);
});
@@ -269,7 +269,7 @@ describe('$httpBackend', function() {
var responseText = '{"some": "object"}';
expect(xhrInstance.responseType).toBe('blob');
callback.andCallFake(function(status, response) {
callback.and.callFake(function(status, response) {
expect(response).toBe(responseText);
});
@@ -287,7 +287,7 @@ describe('$httpBackend', function() {
it('should add script tag for JSONP request', function() {
callback.andCallFake(function(status, response) {
callback.and.callFake(function(status, response) {
expect(status).toBe(200);
expect(response).toBe('some-data');
});
@@ -333,7 +333,7 @@ describe('$httpBackend', function() {
it('should abort request on timeout and replace callback with noop', function() {
callback.andCallFake(function(status, response) {
callback.and.callFake(function(status, response) {
expect(status).toBe(-1);
});
@@ -373,7 +373,7 @@ describe('$httpBackend', function() {
respond(0, 'SOME CONTENT');
expect(callback).toHaveBeenCalled();
expect(callback.mostRecentCall.args[0]).toBe(200);
expect(callback.calls.mostRecent().args[0]).toBe(200);
});
it('should convert 0 to 200 if content for protocols other than file', function() {
@@ -383,7 +383,7 @@ describe('$httpBackend', function() {
respond(0, 'SOME CONTENT');
expect(callback).toHaveBeenCalled();
expect(callback.mostRecentCall.args[0]).toBe(200);
expect(callback.calls.mostRecent().args[0]).toBe(200);
});
it('should convert 0 to 404 if no content and file protocol', function() {
@@ -393,7 +393,7 @@ describe('$httpBackend', function() {
respond(0, '');
expect(callback).toHaveBeenCalled();
expect(callback.mostRecentCall.args[0]).toBe(404);
expect(callback.calls.mostRecent().args[0]).toBe(404);
});
it('should not convert 0 to 404 if no content for protocols other than file', function() {
@@ -403,7 +403,7 @@ describe('$httpBackend', function() {
respond(0, '');
expect(callback).toHaveBeenCalled();
expect(callback.mostRecentCall.args[0]).toBe(0);
expect(callback.calls.mostRecent().args[0]).toBe(0);
});
it('should convert 0 to 404 if no content - relative url', function() {
@@ -431,7 +431,7 @@ describe('$httpBackend', function() {
respond(0, '');
expect(callback).toHaveBeenCalled();
expect(callback.mostRecentCall.args[0]).toBe(404);
expect(callback.calls.mostRecent().args[0]).toBe(404);
} finally {
urlParsingNode = originalUrlParsingNode;
@@ -447,7 +447,7 @@ describe('$httpBackend', function() {
respond(201, '');
expect(callback).toHaveBeenCalled();
expect(callback.mostRecentCall.args[0]).toBe(201);
expect(callback.calls.mostRecent().args[0]).toBe(201);
// request to file://
@@ -455,14 +455,14 @@ describe('$httpBackend', function() {
respond(201, '');
expect(callback).toHaveBeenCalled();
expect(callback.mostRecentCall.args[0]).toBe(201);
expect(callback.calls.mostRecent().args[0]).toBe(201);
// request to file:// with HTTP status >= 300
$backend('POST', 'file://rest_api/create_whatever', null, callback);
respond(503, '');
expect(callback).toHaveBeenCalled();
expect(callback.mostRecentCall.args[0]).toBe(503);
expect(callback.calls.mostRecent().args[0]).toBe(503);
});
});
});
+73 -73
View File
@@ -293,7 +293,7 @@ describe('$http', function() {
$httpBackend = $hb;
$http = $h;
$rootScope = $rs;
spyOn($rootScope, '$apply').andCallThrough();
spyOn($rootScope, '$apply').and.callThrough();
}]));
it('should throw error if the request configuration is not an object', function() {
@@ -394,7 +394,7 @@ describe('$http', function() {
$http({url: '/url', method: 'GET'}).then(function(response) {
expect(response.data).toBe('my content');
expect(response.status).toBe(207);
expect(response.headers()).toEqual({'content-encoding': 'smurf'});
expect(response.headers()).toEqual(extend(Object.create(null), {'content-encoding': 'smurf'}));
expect(response.config.url).toBe('/url');
callback();
});
@@ -433,7 +433,7 @@ describe('$http', function() {
$http({url: '/url', method: 'GET'}).then(null, function(response) {
expect(response.data).toBe('bad error');
expect(response.status).toBe(543);
expect(response.headers()).toEqual({'request-id': '123'});
expect(response.headers()).toEqual(extend(Object.create(null), {'request-id': '123'}));
expect(response.config.url).toBe('/url');
callback();
});
@@ -449,7 +449,7 @@ describe('$http', function() {
$http({url: '/url', method: 'GET'}).success(function(data, status, headers, config) {
expect(data).toBe('my content');
expect(status).toBe(207);
expect(headers()).toEqual({'content-encoding': 'smurf'});
expect(headers()).toEqual(extend(Object.create(null), {'content-encoding': 'smurf'}));
expect(config.url).toBe('/url');
callback();
});
@@ -501,7 +501,7 @@ describe('$http', function() {
$http({url: '/url', method: 'GET'}).error(function(data, status, headers, config) {
expect(data).toBe('bad error');
expect(status).toBe(543);
expect(headers()).toEqual({'request-id': '123'});
expect(headers()).toEqual(extend(Object.create(null), {'request-id': '123'}));
expect(config.url).toBe('/url');
callback();
});
@@ -552,7 +552,7 @@ describe('$http', function() {
it('should return single header', function() {
$httpBackend.expect('GET', '/url').respond('', {'date': 'date-val'});
callback.andCallFake(function(r) {
callback.and.callFake(function(r) {
expect(r.headers('date')).toBe('date-val');
});
@@ -565,7 +565,7 @@ describe('$http', function() {
it('should return null when single header does not exist', function() {
$httpBackend.expect('GET', '/url').respond('', {'Some-Header': 'Fake'});
callback.andCallFake(function(r) {
callback.and.callFake(function(r) {
r.headers(); // we need that to get headers parsed first
expect(r.headers('nothing')).toBe(null);
});
@@ -583,8 +583,8 @@ describe('$http', function() {
'server': 'Apache'
});
callback.andCallFake(function(r) {
expect(r.headers()).toEqual({'content-encoding': 'gzip', 'server': 'Apache'});
callback.and.callFake(function(r) {
expect(r.headers()).toEqual(extend(Object.create(null), {'content-encoding': 'gzip', 'server': 'Apache'}));
});
$http({url: '/url', method: 'GET'}).then(callback);
@@ -595,8 +595,8 @@ describe('$http', function() {
it('should return empty object for jsonp request', function() {
callback.andCallFake(function(r) {
expect(r.headers()).toEqual({});
callback.and.callFake(function(r) {
expect(r.headers()).toEqual(Object.create(null));
});
$httpBackend.expect('JSONP', '/some').respond(200);
@@ -660,7 +660,7 @@ describe('$http', function() {
it('should parse CRLF as delimiter', function() {
// IE does use CRLF
expect(parseHeaders('a: b\r\nc: d\r\n')).toEqual({a: 'b', c: 'd'});
expect(parseHeaders('a: b\r\nc: d\r\n')).toEqual(extend(Object.create(null), {a: 'b', c: 'd'}));
expect(parseHeaders('a: b\r\nc: d\r\n').a).toBe('b');
});
@@ -904,7 +904,7 @@ describe('$http', function() {
it('should check the cache before checking the XSRF cookie', inject(function($cacheFactory) {
var testCache = $cacheFactory('testCache');
spyOn(testCache, 'get').andCallFake(function() {
spyOn(testCache, 'get').and.callFake(function() {
mockedCookies['XSRF-TOKEN'] = 'foo';
});
@@ -943,7 +943,7 @@ describe('$http', function() {
$http.get('/url').then(callback);
$httpBackend.flush();
expect(callback).toHaveBeenCalledOnce();
var headers = callback.mostRecentCall.args[0].headers;
var headers = callback.calls.mostRecent().args[0].headers;
expect(headers('custom-empty-response-Header')).toEqual('');
expect(headers('ToString')).toBe(null);
expect(headers('Constructor')).toBe('');
@@ -1039,7 +1039,7 @@ describe('$http', function() {
it('should $apply even if exception thrown during callback', inject(function($exceptionHandler) {
$httpBackend.when('GET').respond(200);
callback.andThrow('error in callback');
callback.and.throwError('error in callback');
$http({method: 'GET', url: '/some'}).then(callback);
$httpBackend.flush();
@@ -1086,7 +1086,7 @@ describe('$http', function() {
// I'm really sorry for doing this :-D
// Unfortunately I don't know how to trick toString.apply(obj) comparison
spyOn(window, 'isFile').andReturn(true);
spyOn(window, 'isFile').and.returnValue(true);
$httpBackend.expect('POST', '/some', file).respond('');
$http({method: 'POST', url: '/some', data: file});
@@ -1182,7 +1182,7 @@ describe('$http', function() {
$httpBackend.flush();
expect(callback).toHaveBeenCalledOnce();
expect(callback.mostRecentCall.args[0]).toEqual({foo: 'bar', baz: 23});
expect(callback.calls.mostRecent().args[0]).toEqual({foo: 'bar', baz: 23});
});
@@ -1192,7 +1192,7 @@ describe('$http', function() {
$httpBackend.flush();
expect(callback).toHaveBeenCalledOnce();
expect(callback.mostRecentCall.args[0]).toEqual([1, 'abc', {foo: 'bar'}]);
expect(callback.calls.mostRecent().args[0]).toEqual([1, 'abc', {foo: 'bar'}]);
});
@@ -1202,7 +1202,7 @@ describe('$http', function() {
$httpBackend.flush();
expect(callback).toHaveBeenCalledOnce();
expect(callback.mostRecentCall.args[0]).toEqual({foo: 'bar', baz: 23});
expect(callback.calls.mostRecent().args[0]).toEqual({foo: 'bar', baz: 23});
});
@@ -1213,7 +1213,7 @@ describe('$http', function() {
$httpBackend.flush();
expect(callback).toHaveBeenCalledOnce();
expect(callback.mostRecentCall.args[0]).toEqual(123);
expect(callback.calls.mostRecent().args[0]).toEqual(123);
});
@@ -1224,7 +1224,7 @@ describe('$http', function() {
$httpBackend.flush();
expect(callback).toHaveBeenCalledOnce();
expect(callback.mostRecentCall.args[0]).toEqual('asdf');
expect(callback.calls.mostRecent().args[0]).toEqual('asdf');
});
@@ -1235,7 +1235,7 @@ describe('$http', function() {
$httpBackend.flush();
expect(callback).toHaveBeenCalledOnce();
expect(callback.mostRecentCall.args[0]).toEqual(null);
expect(callback.calls.mostRecent().args[0]).toEqual(null);
});
@@ -1246,7 +1246,7 @@ describe('$http', function() {
$httpBackend.flush();
expect(callback).toHaveBeenCalledOnce();
expect(callback.mostRecentCall.args[0]).toEqual(true);
expect(callback.calls.mostRecent().args[0]).toEqual(true);
});
@@ -1257,7 +1257,7 @@ describe('$http', function() {
$httpBackend.flush();
expect(callback).toHaveBeenCalledOnce();
expect(callback.mostRecentCall.args[0]).toEqual(false);
expect(callback.calls.mostRecent().args[0]).toEqual(false);
});
@@ -1268,7 +1268,7 @@ describe('$http', function() {
$httpBackend.flush();
expect(callback).toHaveBeenCalledOnce();
expect(callback.mostRecentCall.args[0]).toEqual('');
expect(callback.calls.mostRecent().args[0]).toEqual('');
});
@@ -1278,7 +1278,7 @@ describe('$http', function() {
$httpBackend.flush();
expect(callback).toHaveBeenCalledOnce();
expect(callback.mostRecentCall.args[0]).toEqual([1, 'abc', {foo:'bar'}]);
expect(callback.calls.mostRecent().args[0]).toEqual([1, 'abc', {foo:'bar'}]);
});
@@ -1288,7 +1288,7 @@ describe('$http', function() {
$httpBackend.flush();
expect(callback).toHaveBeenCalledOnce();
expect(callback.mostRecentCall.args[0]).toEqual([1, 'abc', {foo:'bar'}]);
expect(callback.calls.mostRecent().args[0]).toEqual([1, 'abc', {foo:'bar'}]);
});
@@ -1298,7 +1298,7 @@ describe('$http', function() {
$httpBackend.flush();
expect(callback).toHaveBeenCalledOnce();
expect(callback.mostRecentCall.args[0]).toEqual(')]}\',\n This is not JSON !');
expect(callback.calls.mostRecent().args[0]).toEqual(')]}\',\n This is not JSON !');
});
@@ -1310,7 +1310,7 @@ describe('$http', function() {
$httpBackend.flush();
expect(callback).toHaveBeenCalledOnce();
expect(callback.mostRecentCall.args[0]).toEqual('');
expect(callback.calls.mostRecent().args[0]).toEqual('');
});
it('should not attempt to deserialize json for an empty response whose header contains application/json', function() {
@@ -1321,7 +1321,7 @@ describe('$http', function() {
$httpBackend.flush();
expect(callback).toHaveBeenCalledOnce();
expect(callback.mostRecentCall.args[0]).toEqual('');
expect(callback.calls.mostRecent().args[0]).toEqual('');
});
it('should not attempt to deserialize json for a blank response whose header contains application/json', function() {
@@ -1332,7 +1332,7 @@ describe('$http', function() {
$httpBackend.flush();
expect(callback).toHaveBeenCalledOnce();
expect(callback.mostRecentCall.args[0]).toEqual(' ');
expect(callback.calls.mostRecent().args[0]).toEqual(' ');
});
it('should not deserialize tpl beginning with ng expression', function() {
@@ -1341,7 +1341,7 @@ describe('$http', function() {
$httpBackend.flush();
expect(callback).toHaveBeenCalledOnce();
expect(callback.mostRecentCall.args[0]).toEqual('{{some}}');
expect(callback.calls.mostRecent().args[0]).toEqual('{{some}}');
});
it('should not deserialize json when the opening and closing brackets do not match',
@@ -1352,9 +1352,9 @@ describe('$http', function() {
$http.get('/url2').success(callback);
$httpBackend.flush();
expect(callback.calls.length).toBe(2);
expect(callback.calls[0].args[0]).toEqual('[Code](url): function() {}');
expect(callback.calls[1].args[0]).toEqual('{"is": "not"} ["json"]');
expect(callback).toHaveBeenCalledTimes(2);
expect(callback.calls.argsFor(0)[0]).toEqual('[Code](url): function() {}');
expect(callback.calls.argsFor(1)[0]).toEqual('{"is": "not"} ["json"]');
}
);
});
@@ -1370,7 +1370,7 @@ describe('$http', function() {
$httpBackend.flush();
expect(callback).toHaveBeenCalledOnce();
expect(callback.mostRecentCall.args[0]).toBe('header1');
expect(callback.calls.mostRecent().args[0]).toBe('header1');
});
it('should have access to response status', function() {
@@ -1383,7 +1383,7 @@ describe('$http', function() {
$httpBackend.flush();
expect(callback).toHaveBeenCalledOnce();
expect(callback.mostRecentCall.args[0]).toBe(200);
expect(callback.calls.mostRecent().args[0]).toBe(200);
});
@@ -1396,11 +1396,11 @@ describe('$http', function() {
$httpBackend.flush();
expect(callback).toHaveBeenCalledOnce();
expect(callback.mostRecentCall.args[0]).toBe('RESP-FIRST:V1');
expect(callback.calls.mostRecent().args[0]).toBe('RESP-FIRST:V1');
});
it('should apply `transformResponse` even if the response data is empty', function(data) {
it('should apply `transformResponse` even if the response data is empty', function() {
var callback = jasmine.createSpy('transformResponse');
var config = {transformResponse: callback};
@@ -1412,10 +1412,10 @@ describe('$http', function() {
$http.get('/url3', config);
$httpBackend.flush();
expect(callback.callCount).toBe(3);
expect(callback.calls[0].args[0]).toBeUndefined();
expect(callback.calls[1].args[0]).toBe(null);
expect(callback.calls[2].args[0]).toBe('');
expect(callback).toHaveBeenCalledTimes(3);
expect(callback.calls.argsFor(0)[0]).toBeUndefined();
expect(callback.calls.argsFor(1)[0]).toBe(null);
expect(callback.calls.argsFor(2)[0]).toBe('');
});
});
});
@@ -1444,7 +1444,7 @@ describe('$http', function() {
$rootScope.$digest();
expect(callback).toHaveBeenCalledOnce();
expect(callback.mostRecentCall.args[0]).toBe('content');
expect(callback.calls.mostRecent().args[0]).toBe('content');
}));
it('should cache JSONP request when cache is provided', inject(function($rootScope) {
@@ -1456,7 +1456,7 @@ describe('$http', function() {
$rootScope.$digest();
expect(callback).toHaveBeenCalledOnce();
expect(callback.mostRecentCall.args[0]).toBe('content');
expect(callback.calls.mostRecent().args[0]).toBe('content');
}));
it('should cache request when cache is provided and no method specified', function() {
@@ -1466,7 +1466,7 @@ describe('$http', function() {
$rootScope.$digest();
expect(callback).toHaveBeenCalledOnce();
expect(callback.mostRecentCall.args[0]).toBe('content');
expect(callback.calls.mostRecent().args[0]).toBe('content');
});
@@ -1503,7 +1503,7 @@ describe('$http', function() {
$httpBackend.flush();
expect(callback).toHaveBeenCalledOnce();
expect(callback.mostRecentCall.args[0]).toBe('content2');
expect(callback.calls.mostRecent().args[0]).toBe('content2');
});
@@ -1515,7 +1515,7 @@ describe('$http', function() {
$httpBackend.flush();
expect(callback).toHaveBeenCalledOnce();
expect(callback.mostRecentCall.args[0]).toBe('content2');
expect(callback.calls.mostRecent().args[0]).toBe('content2');
});
@@ -1538,14 +1538,14 @@ describe('$http', function() {
$httpBackend.flush();
expect(callback).toHaveBeenCalledOnce();
expect(callback.mostRecentCall.args[0]).toBe('content2');
expect(callback.calls.mostRecent().args[0]).toBe('content2');
});
it('should cache the headers as well', inject(function($rootScope) {
doFirstCacheRequest('GET', 200, {'content-encoding': 'gzip', 'server': 'Apache'});
callback.andCallFake(function(r, s, headers) {
expect(headers()).toEqual({'content-encoding': 'gzip', 'server': 'Apache'});
callback.and.callFake(function(r, s, headers) {
expect(headers()).toEqual(extend(Object.create(null), {'content-encoding': 'gzip', 'server': 'Apache'}));
expect(headers('server')).toBe('Apache');
});
@@ -1557,7 +1557,7 @@ describe('$http', function() {
it('should not share the cached headers object instance', inject(function($rootScope) {
doFirstCacheRequest('GET', 200, {'content-encoding': 'gzip', 'server': 'Apache'});
callback.andCallFake(function(r, s, headers) {
callback.and.callFake(function(r, s, headers) {
expect(headers()).toEqual(cache.get('/url')[2]);
expect(headers()).not.toBe(cache.get('/url')[2]);
});
@@ -1570,7 +1570,7 @@ describe('$http', function() {
it('should not share the pending cached headers object instance', inject(function($rootScope) {
var firstResult;
callback.andCallFake(function(result) {
callback.and.callFake(function(result) {
expect(result.headers()).toEqual(firstResult.headers());
expect(result.headers()).not.toBe(firstResult.headers());
});
@@ -1588,7 +1588,7 @@ describe('$http', function() {
it('should cache status code as well', inject(function($rootScope) {
doFirstCacheRequest('GET', 201);
callback.andCallFake(function(r, status, h) {
callback.and.callFake(function(r, status, h) {
expect(status).toBe(201);
});
@@ -1601,7 +1601,7 @@ describe('$http', function() {
it('should use cache even if second request was made before the first returned', function() {
$httpBackend.expect('GET', '/url').respond(201, 'fake-response');
callback.andCallFake(function(response, status, headers) {
callback.and.callFake(function(response, status, headers) {
expect(response).toBe('fake-response');
expect(status).toBe(201);
});
@@ -1611,7 +1611,7 @@ describe('$http', function() {
$httpBackend.flush();
expect(callback).toHaveBeenCalled();
expect(callback.callCount).toBe(2);
expect(callback).toHaveBeenCalledTimes(2);
});
@@ -1623,7 +1623,7 @@ describe('$http', function() {
$http({method: 'GET', url: '/url', cache: cache, headers: {foo: 'baz'}}).then(callback);
$rootScope.$digest();
expect(callback.mostRecentCall.args[0].config.headers.foo).toBe('baz');
expect(callback.calls.mostRecent().args[0].config.headers.foo).toBe('baz');
});
@@ -1634,7 +1634,7 @@ describe('$http', function() {
$http({method: 'GET', url: '/url', cache: cache, headers: {foo: 'baz'}}).then(callback);
$httpBackend.flush();
expect(callback.mostRecentCall.args[0].config.headers.foo).toBe('baz');
expect(callback.calls.mostRecent().args[0].config.headers.foo).toBe('baz');
});
@@ -1645,14 +1645,14 @@ describe('$http', function() {
$http({method: 'GET', url: '/url', cache: cache, headers: {foo: 'baz'}}).catch(callback);
$httpBackend.flush();
expect(callback.mostRecentCall.args[0].config.headers.foo).toBe('baz');
expect(callback.calls.mostRecent().args[0].config.headers.foo).toBe('baz');
});
it('should allow the cached value to be an empty string', function() {
cache.put('/abc', '');
callback.andCallFake(function(response, status, headers) {
callback.and.callFake(function(response, status, headers) {
expect(response).toBe('');
expect(status).toBe(200);
});
@@ -1669,7 +1669,7 @@ describe('$http', function() {
$http.get('/myurl', {cache: cache}).success(function(data, status, headers) {
expect(data).toBe('simple response');
expect(status).toBe(200);
expect(headers()).toEqual({});
expect(headers()).toEqual(Object.create(null));
callback();
});
@@ -1697,7 +1697,7 @@ describe('$http', function() {
$rootScope.$digest();
expect(callback).toHaveBeenCalledOnce();
expect(callback.mostRecentCall.args[0]).toBe('content');
expect(callback.calls.mostRecent().args[0]).toBe('content');
// Invalidate cache entry.
$http.defaults.cache.remove("/url");
@@ -1726,14 +1726,14 @@ describe('$http', function() {
$http({method: 'get', url: '/url'}).success(callback);
$rootScope.$digest();
expect(callback).toHaveBeenCalledOnce();
expect(callback.mostRecentCall.args[0]).toBe('content-default-cache');
callback.reset();
expect(callback.calls.mostRecent().args[0]).toBe('content-default-cache');
callback.calls.reset();
// Serve request from local cache when it is given (but default filled too).
$http({method: 'get', url: '/url', cache: localCache}).success(callback);
$rootScope.$digest();
expect(callback).toHaveBeenCalledOnce();
expect(callback.mostRecentCall.args[0]).toBe('content-local-cache');
expect(callback.calls.mostRecent().args[0]).toBe('content-local-cache');
}));
it('should be skipped if {cache: false} is passed in request config', function() {
@@ -1762,7 +1762,7 @@ describe('$http', function() {
function(response) {
expect(response.data).toBeUndefined();
expect(response.status).toBe(-1);
expect(response.headers()).toEqual({});
expect(response.headers()).toEqual(Object.create(null));
expect(response.config.url).toBe('/some');
callback();
});
@@ -1819,7 +1819,7 @@ describe('$http', function() {
expect($http.pendingRequests.length).toBe(0);
$http({method: 'get', url: '/cached', cache: true});
spyOn($http.pendingRequests, 'push').andCallThrough();
spyOn($http.pendingRequests, 'push').and.callThrough();
$rootScope.$digest();
expect($http.pendingRequests.push).toHaveBeenCalledOnce();
@@ -1872,7 +1872,7 @@ describe('$http', function() {
it('should pass timeout, withCredentials and responseType', function() {
var $httpBackend = jasmine.createSpy('$httpBackend');
$httpBackend.andCallFake(function(m, u, d, c, h, timeout, withCredentials, responseType) {
$httpBackend.and.callFake(function(m, u, d, c, h, timeout, withCredentials, responseType) {
expect(timeout).toBe(12345);
expect(withCredentials).toBe(true);
expect(responseType).toBe('json');
@@ -1901,7 +1901,7 @@ describe('$http', function() {
it('should use withCredentials from default', function() {
var $httpBackend = jasmine.createSpy('$httpBackend');
$httpBackend.andCallFake(function(m, u, d, c, h, timeout, withCredentials, responseType) {
$httpBackend.and.callFake(function(m, u, d, c, h, timeout, withCredentials, responseType) {
expect(withCredentials).toBe(true);
});
@@ -1938,10 +1938,10 @@ describe('$http with $applyAsync', function() {
$httpBackend = backend;
$rootScope = scope;
$browser = browser;
spyOn($rootScope, '$apply').andCallThrough();
spyOn($rootScope, '$applyAsync').andCallThrough();
spyOn($rootScope, '$digest').andCallThrough();
spyOn($browser.defer, 'cancel').andCallThrough();
spyOn($rootScope, '$apply').and.callThrough();
spyOn($rootScope, '$applyAsync').and.callThrough();
spyOn($rootScope, '$digest').and.callThrough();
spyOn($browser.defer, 'cancel').and.callThrough();
log = logger;
}]));
+2 -2
View File
@@ -133,7 +133,7 @@ describe('$interpolate', function() {
$rootScope.$digest();
expect($rootScope.$countWatchers()).toBe(0);
expect(spy).toHaveBeenCalledWith('foo', 'foo', $rootScope);
expect(spy.calls.length).toBe(1);
expect(spy).toHaveBeenCalledTimes(1);
})
);
@@ -144,7 +144,7 @@ describe('$interpolate', function() {
$rootScope.$digest();
expect($rootScope.$countWatchers()).toBe(0);
expect(spy).toHaveBeenCalledWith('foo 42', 'foo 42', $rootScope);
expect(spy.calls.length).toBe(1);
expect(spy).toHaveBeenCalledTimes(1);
})
);
});
+11 -11
View File
@@ -69,7 +69,7 @@ describe('$interval', function() {
it('should call $apply after each task is executed',
inject(function($interval, $rootScope, $window) {
var applySpy = spyOn($rootScope, '$apply').andCallThrough();
var applySpy = spyOn($rootScope, '$apply').and.callThrough();
$interval(noop, 1000);
expect(applySpy).not.toHaveBeenCalled();
@@ -77,18 +77,18 @@ describe('$interval', function() {
$window.flush(1000);
expect(applySpy).toHaveBeenCalledOnce();
applySpy.reset();
applySpy.calls.reset();
$interval(noop, 1000);
$interval(noop, 1000);
$window.flush(1000);
expect(applySpy.callCount).toBe(3);
expect(applySpy).toHaveBeenCalledTimes(3);
}));
it('should NOT call $apply if invokeApply is set to false',
inject(function($interval, $rootScope, $window) {
var applySpy = spyOn($rootScope, '$apply').andCallThrough();
var applySpy = spyOn($rootScope, '$apply').and.callThrough();
$interval(noop, 1000, 0, false);
expect(applySpy).not.toHaveBeenCalled();
@@ -100,8 +100,8 @@ describe('$interval', function() {
it('should NOT call $evalAsync or $digest if invokeApply is set to false',
inject(function($interval, $rootScope, $window, $timeout) {
var evalAsyncSpy = spyOn($rootScope, '$evalAsync').andCallThrough();
var digestSpy = spyOn($rootScope, '$digest').andCallThrough();
var evalAsyncSpy = spyOn($rootScope, '$evalAsync').and.callThrough();
var digestSpy = spyOn($rootScope, '$digest').and.callThrough();
var notifySpy = jasmine.createSpy('notify');
$interval(notifySpy, 1000, 1, false);
@@ -184,9 +184,9 @@ describe('$interval', function() {
expect(task2).toHaveBeenCalledWith('Task2');
expect(task3).toHaveBeenCalledWith('I', 'am', 'a', 'Task3', 'spy');
task1.reset();
task2.reset();
task3.reset();
task1.calls.reset();
task2.calls.reset();
task3.calls.reset();
$window.flush(1000);
expect(task1).toHaveBeenCalledWith('Task1');
@@ -256,7 +256,7 @@ describe('$interval', function() {
it('should call $apply even if an exception is thrown in callback', inject(
function($interval, $rootScope, $window) {
var applySpy = spyOn($rootScope, '$apply').andCallThrough();
var applySpy = spyOn($rootScope, '$apply').and.callThrough();
$interval(function() { throw "Test Error"; }, 1000);
expect(applySpy).not.toHaveBeenCalled();
@@ -356,7 +356,7 @@ describe('$interval', function() {
$window.flush(1000);
expect(clearIntervalSpy).toHaveBeenCalled();
clearIntervalSpy.reset();
clearIntervalSpy.calls.reset();
$interval.cancel($interval(noop, 1000));
expect(clearIntervalSpy).toHaveBeenCalled();
}));
+35 -35
View File
@@ -387,7 +387,7 @@ describe('$location', function() {
describe('state', function() {
it('should set $$state and return itself', function() {
var locationUrl = createLocationHtml5Url();
expect(locationUrl.$$state).toEqual(null);
expect(locationUrl.$$state).toEqual(undefined);
var returned = locationUrl.state({a: 2});
expect(locationUrl.$$state).toEqual({a: 2});
@@ -647,7 +647,7 @@ describe('$location', function() {
mockUpBrowser({initialUrl:'http://new.com/a/b#', baseHref:'/base/'});
inject(function($rootScope, $browser, $location) {
$browser.url('http://new.com/a/b');
var $browserUrl = spyOnlyCallsWithArgs($browser, 'url').andCallThrough();
var $browserUrl = spyOnlyCallsWithArgs($browser, 'url').and.callThrough();
$rootScope.$digest();
expect($browserUrl).not.toHaveBeenCalled();
});
@@ -710,7 +710,7 @@ describe('$location', function() {
initService({html5Mode: true, supportHistory: false});
mockUpBrowser({initialUrl:'http://server/app/', baseHref:'/app/'});
inject(function($rootScope, $location, $browser) {
var $browserUrl = spyOnlyCallsWithArgs($browser, 'url').andCallThrough();
var $browserUrl = spyOnlyCallsWithArgs($browser, 'url').and.callThrough();
updatePathOnLocationChangeSuccessTo('/Home');
@@ -718,7 +718,7 @@ describe('$location', function() {
expect($browser.url()).toEqual('http://server/app/#/Home');
expect($location.path()).toEqual('/Home');
expect($browserUrl.calls.length).toEqual(1);
expect($browserUrl).toHaveBeenCalledTimes(1);
});
});
@@ -726,7 +726,7 @@ describe('$location', function() {
initService({html5Mode: true, supportHistory: false});
mockUpBrowser({initialUrl:'http://server/app/Home', baseHref:'/app/'});
inject(function($rootScope, $location, $browser, $window) {
var $browserUrl = spyOnlyCallsWithArgs($browser, 'url').andCallThrough();
var $browserUrl = spyOnlyCallsWithArgs($browser, 'url').and.callThrough();
updatePathOnLocationChangeSuccessTo('/');
@@ -734,8 +734,8 @@ describe('$location', function() {
expect($browser.url()).toEqual('http://server/app/#/');
expect($location.path()).toEqual('/');
expect($browserUrl.calls.length).toEqual(1);
expect($browserUrl.calls[0].args).toEqual(['http://server/app/#/', false, null]);
expect($browserUrl).toHaveBeenCalledTimes(1);
expect($browserUrl.calls.argsFor(0)).toEqual(['http://server/app/#/', false, null]);
});
});
@@ -743,15 +743,15 @@ describe('$location', function() {
initService({html5Mode: true, supportHistory: false});
mockUpBrowser({initialUrl:'http://server/app/', baseHref:'/app/'});
inject(function($rootScope, $location, $browser) {
var $browserUrl = spyOnlyCallsWithArgs($browser, 'url').andCallThrough();
var $browserUrl = spyOnlyCallsWithArgs($browser, 'url').and.callThrough();
updatePathOnLocationChangeSuccessTo('/Home');
$rootScope.$digest();
expect($browser.url()).toEqual('http://server/app/#/Home');
expect($location.path()).toEqual('/Home');
expect($browserUrl.calls.length).toEqual(1);
expect($browserUrl.calls[0].args).toEqual(['http://server/app/#/Home', false, null]);
expect($browserUrl).toHaveBeenCalledTimes(1);
expect($browserUrl.calls.argsFor(0)).toEqual(['http://server/app/#/Home', false, null]);
});
});
@@ -759,14 +759,14 @@ describe('$location', function() {
initService({html5Mode: true, supportHistory: false});
mockUpBrowser({initialUrl:'http://server/app/', baseHref:'/app/'});
inject(function($rootScope, $location, $browser) {
var $browserUrl = spyOnlyCallsWithArgs($browser, 'url').andCallThrough();
var $browserUrl = spyOnlyCallsWithArgs($browser, 'url').and.callThrough();
updatePathOnLocationChangeSuccessTo('/');
$rootScope.$digest();
expect($browser.url()).toEqual('http://server/app/#/');
expect($location.path()).toEqual('/');
expect($browserUrl.calls.length).toEqual(1);
expect($browserUrl).toHaveBeenCalledTimes(1);
});
});
});
@@ -778,7 +778,7 @@ describe('$location', function() {
initService({html5Mode: true, supportHistory: true});
mockUpBrowser({initialUrl:'http://server/app/', baseHref:'/app/'});
inject(function($rootScope, $injector, $browser) {
var $browserUrl = spyOnlyCallsWithArgs($browser, 'url').andCallThrough();
var $browserUrl = spyOnlyCallsWithArgs($browser, 'url').and.callThrough();
var $location = $injector.get('$location');
updatePathOnLocationChangeSuccessTo('/Home');
@@ -787,7 +787,7 @@ describe('$location', function() {
expect($browser.url()).toEqual('http://server/app/Home');
expect($location.path()).toEqual('/Home');
expect($browserUrl.calls.length).toEqual(1);
expect($browserUrl).toHaveBeenCalledTimes(1);
});
});
@@ -795,7 +795,7 @@ describe('$location', function() {
initService({html5Mode: true, supportHistory: true});
mockUpBrowser({initialUrl:'http://server/app/', baseHref:'/app/'});
inject(function($rootScope, $injector, $browser) {
var $browserUrl = spyOnlyCallsWithArgs($browser, 'url').andCallThrough();
var $browserUrl = spyOnlyCallsWithArgs($browser, 'url').and.callThrough();
var $location = $injector.get('$location');
updatePathOnLocationChangeSuccessTo('/');
@@ -804,7 +804,7 @@ describe('$location', function() {
expect($browser.url()).toEqual('http://server/app/');
expect($location.path()).toEqual('/');
expect($browserUrl.calls.length).toEqual(0);
expect($browserUrl).not.toHaveBeenCalled();
});
});
@@ -812,7 +812,7 @@ describe('$location', function() {
initService({html5Mode: true, supportHistory: true});
mockUpBrowser({initialUrl:'http://server/app/', baseHref:'/app/'});
inject(function($rootScope, $injector, $browser) {
var $browserUrl = spyOnlyCallsWithArgs($browser, 'url').andCallThrough();
var $browserUrl = spyOnlyCallsWithArgs($browser, 'url').and.callThrough();
var $location = $injector.get('$location');
updatePathOnLocationChangeSuccessTo('/Home');
@@ -821,7 +821,7 @@ describe('$location', function() {
expect($browser.url()).toEqual('http://server/app/Home');
expect($location.path()).toEqual('/Home');
expect($browserUrl.calls.length).toEqual(1);
expect($browserUrl).toHaveBeenCalledTimes(1);
});
});
@@ -829,7 +829,7 @@ describe('$location', function() {
initService({html5Mode: true, supportHistory: true});
mockUpBrowser({initialUrl:'http://server/app/', baseHref:'/app/'});
inject(function($rootScope, $injector, $browser) {
var $browserUrl = spyOnlyCallsWithArgs($browser, 'url').andCallThrough();
var $browserUrl = spyOnlyCallsWithArgs($browser, 'url').and.callThrough();
var $location = $injector.get('$location');
updatePathOnLocationChangeSuccessTo('/');
@@ -838,7 +838,7 @@ describe('$location', function() {
expect($browser.url()).toEqual('http://server/app/');
expect($location.path()).toEqual('/');
expect($browserUrl.calls.length).toEqual(0);
expect($browserUrl).not.toHaveBeenCalled();
});
});
});
@@ -851,7 +851,7 @@ describe('$location', function() {
initService({html5Mode:false,hashPrefix: '!',supportHistory: true});
mockUpBrowser({initialUrl:'http://new.com/a/b#!', baseHref:'/a/b'});
inject(function($window, $browser, $location, $rootScope) {
spyOn($location, '$$parse').andCallThrough();
spyOn($location, '$$parse').and.callThrough();
$window.location.href = 'http://new.com/a/b#!/aaa';
$browser.$$checkUrlChange();
expect($location.absUrl()).toBe('http://new.com/a/b#!/aaa');
@@ -906,7 +906,7 @@ describe('$location', function() {
initService({html5Mode:false,hashPrefix: '!',supportHistory: true});
mockUpBrowser({initialUrl:'http://new.com/a/b#!', baseHref:'/a/b'});
inject(function($rootScope, $browser, $location) {
var $browserUrl = spyOnlyCallsWithArgs($browser, 'url').andCallThrough();
var $browserUrl = spyOnlyCallsWithArgs($browser, 'url').and.callThrough();
$location.path('/new/path');
expect($browserUrl).not.toHaveBeenCalled();
$rootScope.$apply();
@@ -921,7 +921,7 @@ describe('$location', function() {
initService({html5Mode:false,hashPrefix: '!',supportHistory: true});
mockUpBrowser({initialUrl:'http://new.com/a/b#!', baseHref:'/a/b'});
inject(function($rootScope, $browser, $location) {
var $browserUrl = spyOnlyCallsWithArgs($browser, 'url').andCallThrough();
var $browserUrl = spyOnlyCallsWithArgs($browser, 'url').and.callThrough();
$location.path('/new/path');
$rootScope.$watch(function() {
@@ -939,12 +939,12 @@ describe('$location', function() {
initService({html5Mode:false,hashPrefix: '!',supportHistory: true});
mockUpBrowser({initialUrl:'http://new.com/a/b#!', baseHref:'/a/b'});
inject(function($rootScope, $browser, $location) {
var $browserUrl = spyOnlyCallsWithArgs($browser, 'url').andCallThrough();
var $browserUrl = spyOnlyCallsWithArgs($browser, 'url').and.callThrough();
$location.path('/n/url').replace();
$rootScope.$apply();
expect($browserUrl).toHaveBeenCalledOnce();
expect($browserUrl.mostRecentCall.args).toEqual(['http://new.com/a/b#!/n/url', true, null]);
expect($browserUrl.calls.mostRecent().args).toEqual(['http://new.com/a/b#!/n/url', true, null]);
expect($location.$$replace).toBe(false);
});
});
@@ -1025,12 +1025,12 @@ describe('$location', function() {
initService({html5Mode:true, supportHistory: true});
mockUpBrowser({initialUrl:'http://new.com/a/b/', baseHref:'/a/b/'});
inject(function($rootScope, $location, $browser) {
var $browserUrl = spyOnlyCallsWithArgs($browser, 'url').andCallThrough();
var $browserUrl = spyOnlyCallsWithArgs($browser, 'url').and.callThrough();
$location.path('/n/url').state({a: 2}).replace();
$rootScope.$apply();
expect($browserUrl).toHaveBeenCalledOnce();
expect($browserUrl.mostRecentCall.args).toEqual(['http://new.com/a/b/n/url', true, {a: 2}]);
expect($browserUrl.calls.mostRecent().args).toEqual(['http://new.com/a/b/n/url', true, {a: 2}]);
expect($location.$$replace).toBe(false);
expect($location.$$state).toEqual({a: 2});
});
@@ -1041,12 +1041,12 @@ describe('$location', function() {
mockUpBrowser({initialUrl:'http://new.com/a/b/', baseHref:'/a/b/'});
inject(function($rootScope, $location, $browser) {
var $browserUrl = spyOnlyCallsWithArgs($browser, 'url').andCallThrough();
var $browserUrl = spyOnlyCallsWithArgs($browser, 'url').and.callThrough();
$location.path('/n/url').state({a: 2}).replace().state({b: 3}).path('/o/url');
$rootScope.$apply();
expect($browserUrl).toHaveBeenCalledOnce();
expect($browserUrl.mostRecentCall.args).toEqual(['http://new.com/a/b/o/url', true, {b: 3}]);
expect($browserUrl.calls.mostRecent().args).toEqual(['http://new.com/a/b/o/url', true, {b: 3}]);
expect($location.$$replace).toBe(false);
expect($location.$$state).toEqual({b: 3});
});
@@ -1057,12 +1057,12 @@ describe('$location', function() {
mockUpBrowser({initialUrl:'http://new.com/a/b/', baseHref:'/a/b/'});
inject(function($rootScope, $location, $browser) {
var $browserUrl = spyOnlyCallsWithArgs($browser, 'url').andCallThrough();
var $browserUrl = spyOnlyCallsWithArgs($browser, 'url').and.callThrough();
$location.state({a: 2}).replace().state({b: 3});
$rootScope.$apply();
expect($browserUrl).toHaveBeenCalledOnce();
expect($browserUrl.mostRecentCall.args).toEqual(['http://new.com/a/b/', true, {b: 3}]);
expect($browserUrl.calls.mostRecent().args).toEqual(['http://new.com/a/b/', true, {b: 3}]);
expect($location.$$replace).toBe(false);
expect($location.$$state).toEqual({b: 3});
});
@@ -1141,12 +1141,12 @@ describe('$location', function() {
$location.url('/foo').state({a: 2});
$rootScope.$apply();
var $browserUrl = spyOnlyCallsWithArgs($browser, 'url').andCallThrough();
var $browserUrl = spyOnlyCallsWithArgs($browser, 'url').and.callThrough();
$location.url('/bar');
$rootScope.$apply();
expect($browserUrl).toHaveBeenCalledOnce();
expect($browserUrl.mostRecentCall.args).toEqual(['http://new.com/a/b/bar', false, null]);
expect($browserUrl.calls.mostRecent().args).toEqual(['http://new.com/a/b/bar', false, null]);
});
});
@@ -1890,7 +1890,7 @@ describe('$location', function() {
var event = {
target: jqLite(window.document.body).find('a')[0],
preventDefault: jasmine.createSpy('preventDefault'),
isDefaultPrevented: jasmine.createSpy().andReturn(false)
isDefaultPrevented: jasmine.createSpy().and.returnValue(false)
};
@@ -1921,7 +1921,7 @@ describe('$location', function() {
var event = {
target: jqLite(window.document.body).find('a')[0],
preventDefault: jasmine.createSpy('preventDefault'),
isDefaultPrevented: jasmine.createSpy().andReturn(false)
isDefaultPrevented: jasmine.createSpy().and.returnValue(false)
};
+6 -6
View File
@@ -1724,7 +1724,7 @@ describe('parser', function() {
expect(scope.$eval("+'1'")).toEqual(+'1');
expect(scope.$eval("-'1'")).toEqual(-'1');
expect(scope.$eval("+undefined")).toEqual(0);
expect(scope.$eval("-undefined")).toEqual(0);
expect(scope.$eval("-undefined")).toBe(0);
expect(scope.$eval("+null")).toEqual(+null);
expect(scope.$eval("-null")).toEqual(-null);
expect(scope.$eval("+false")).toEqual(+false);
@@ -3090,7 +3090,7 @@ describe('parser', function() {
$rootScope.$digest();
$rootScope.foo = 'foo';
$rootScope.$digest();
expect(fn()).toEqual(null);
expect(fn()).toEqual(undefined);
}));
describe('literal expressions', function() {
@@ -3459,16 +3459,16 @@ describe('parser', function() {
var value = 'foo';
var spy = jasmine.createSpy();
spy.andCallFake(function() { return value; });
spy.and.callFake(function() { return value; });
scope.foo = spy;
scope.$watch("foo() | uppercase");
scope.$digest();
expect(spy.calls.length).toEqual(2);
expect(spy).toHaveBeenCalledTimes(2);
scope.$digest();
expect(spy.calls.length).toEqual(3);
expect(spy).toHaveBeenCalledTimes(3);
value = 'bar';
scope.$digest();
expect(spy.calls.length).toEqual(5);
expect(spy).toHaveBeenCalledTimes(5);
}));
it('should invoke all statements in multi-statement expressions', inject(function($parse) {
+2 -2
View File
@@ -833,7 +833,7 @@ describe('q', function() {
expect(resolveSpy).not.toHaveBeenCalled();
expect(rejectSpy).toHaveBeenCalled();
expect(rejectSpy.calls[0].args[0].message).
expect(rejectSpy.calls.argsFor(0)[0].message).
toMatch(/\[\$q\:qcycle\] Expected promise to be resolved with value other than itself/);
});
@@ -2090,7 +2090,7 @@ describe('q', function() {
// Set up spies
exceptionExceptionSpy = jasmine.createSpy('rethrowExceptionHandler')
.andCallFake(function rethrowExceptionHandler(e) {
.and.callFake(function rethrowExceptionHandler(e) {
throw e;
});
errorSpy = jasmine.createSpy('errorSpy');
+1 -1
View File
@@ -54,7 +54,7 @@ describe('$$rAF', function() {
expect(message).toBeUndefined();
expect(timeoutSpy).toHaveBeenCalled();
timeoutSpy.mostRecentCall.args[0]();
timeoutSpy.calls.mostRecent().args[0]();
expect(message).toBe('on');
});
+32 -32
View File
@@ -106,14 +106,14 @@ describe('Scope', function() {
var spy = jasmine.createSpy();
$rootScope.$watch('name', spy);
$rootScope.$digest();
spy.reset();
spy.calls.reset();
expect(spy).not.wasCalled();
expect(spy).not.toHaveBeenCalled();
$rootScope.$digest();
expect(spy).not.wasCalled();
expect(spy).not.toHaveBeenCalled();
$rootScope.name = 'misko';
$rootScope.$digest();
expect(spy).wasCalledWith('misko', undefined, $rootScope);
expect(spy).toHaveBeenCalledWith('misko', undefined, $rootScope);
}));
@@ -135,15 +135,15 @@ describe('Scope', function() {
var spy = jasmine.createSpy();
$rootScope.$watch('name.first', spy);
$rootScope.$digest();
spy.reset();
spy.calls.reset();
$rootScope.name = {};
expect(spy).not.wasCalled();
expect(spy).not.toHaveBeenCalled();
$rootScope.$digest();
expect(spy).not.wasCalled();
expect(spy).not.toHaveBeenCalled();
$rootScope.name.first = 'misko';
$rootScope.$digest();
expect(spy).wasCalled();
expect(spy).toHaveBeenCalled();
}));
it('should not keep constant expressions on watch queue', inject(function($rootScope) {
@@ -543,12 +543,12 @@ describe('Scope', function() {
expect(listener).toHaveBeenCalled();
expect(listenerRemove).toBeDefined();
listener.reset();
listener.calls.reset();
$rootScope.foo = 'bar';
$rootScope.$digest(); //triger
expect(listener).toHaveBeenCalledOnce();
listener.reset();
listener.calls.reset();
$rootScope.foo = 'baz';
listenerRemove();
$rootScope.$digest(); //trigger
@@ -829,11 +829,11 @@ describe('Scope', function() {
$rootScope.obj.c = temp;
$rootScope.$digest();
expect(log.empty()).
toEqual([{newVal: {a: [], b: {}, c: 'B'}, oldVal: {a: 'B', b: [], c: {}}}]);
toEqual([{newVal: {a: [], b: [], c: 'B'}, oldVal: {a: 'B', b: [], c: {}}}]);
delete $rootScope.obj.a;
$rootScope.$digest();
expect(log.empty()).toEqual([{newVal: {b: {}, c: 'B'}, oldVal: {a: [], b: {}, c: 'B'}}]);
expect(log.empty()).toEqual([{newVal: {b: [], c: 'B'}, oldVal: {a: [], b: [], c: 'B'}}]);
});
@@ -850,11 +850,11 @@ describe('Scope', function() {
$rootScope.obj.a = 'a';
$rootScope.obj.b = 'b';
$rootScope.$digest();
expect(log.empty()[0].newVal).toEqual({a: 'a', b: 'b'});
expect(log.empty()[0].newVal).toEqual(extend(Object.create(null), {a: 'a', b: 'b'}));
delete $rootScope.obj.b;
$rootScope.$digest();
expect(log.empty()[0].newVal).toEqual({a: 'a'});
expect(log.empty()[0].newVal).toEqual(extend(Object.create(null), {a: 'a'}));
});
});
});
@@ -1155,7 +1155,7 @@ describe('Scope', function() {
expect(first.$$listenerCount[EVENT]).toBeUndefined();
$rootScope.$broadcast(EVENT);
expect(spy.callCount).toBe(1);
expect(spy).toHaveBeenCalledTimes(1);
}));
@@ -1412,9 +1412,9 @@ describe('Scope', function() {
expect(childScope.$$asyncQueue).toBe($rootScope.$$asyncQueue);
expect(isolateScope.$$asyncQueue).toBeUndefined();
expect($rootScope.$$asyncQueue).toEqual([
{scope: $rootScope, expression: $parse('rootExpression')},
{scope: childScope, expression: $parse('childExpression')},
{scope: isolateScope, expression: $parse('isolateExpression')}
{scope: $rootScope, expression: $parse('rootExpression'), locals: undefined},
{scope: childScope, expression: $parse('childExpression'), locals: undefined},
{scope: isolateScope, expression: $parse('isolateExpression'), locals: undefined}
]);
}));
@@ -1684,16 +1684,16 @@ describe('Scope', function() {
it('should be cancelled if a $rootScope digest occurs before the next tick', inject(function($rootScope, $browser) {
var apply = spyOn($rootScope, '$apply').andCallThrough();
var cancel = spyOn($browser.defer, 'cancel').andCallThrough();
var apply = spyOn($rootScope, '$apply').and.callThrough();
var cancel = spyOn($browser.defer, 'cancel').and.callThrough();
var expression = jasmine.createSpy('expr');
$rootScope.$applyAsync(expression);
$rootScope.$digest();
expect(expression).toHaveBeenCalledOnce();
expect(cancel).toHaveBeenCalledOnce();
expression.reset();
cancel.reset();
expression.calls.reset();
cancel.calls.reset();
// assert that we no longer are waiting to execute
expect($browser.deferredFns.length).toBe(0);
@@ -1905,7 +1905,7 @@ describe('Scope', function() {
var remove2 = child.$on('evt', spy2);
var remove3 = child.$on('evt', spy3);
spy1.andCallFake(remove1);
spy1.and.callFake(remove1);
expect(child.$$listeners['evt'].length).toBe(3);
@@ -1916,12 +1916,12 @@ describe('Scope', function() {
expect(spy3).toHaveBeenCalledOnce();
expect(child.$$listeners['evt'].length).toBe(3); // cleanup will happen on next $emit
spy1.reset();
spy2.reset();
spy3.reset();
spy1.calls.reset();
spy2.calls.reset();
spy3.calls.reset();
// should call only 2nd because 1st was already removed and 2nd removes 3rd
spy2.andCallFake(remove3);
spy2.and.callFake(remove3);
child.$emit('evt');
expect(spy1).not.toHaveBeenCalled();
expect(spy2).toHaveBeenCalledOnce();
@@ -1939,7 +1939,7 @@ describe('Scope', function() {
var remove2 = child.$on('evt', spy2);
var remove3 = child.$on('evt', spy3);
spy1.andCallFake(remove1);
spy1.and.callFake(remove1);
expect(child.$$listeners['evt'].length).toBe(3);
@@ -1950,12 +1950,12 @@ describe('Scope', function() {
expect(spy3).toHaveBeenCalledOnce();
expect(child.$$listeners['evt'].length).toBe(3); //cleanup will happen on next $broadcast
spy1.reset();
spy2.reset();
spy3.reset();
spy1.calls.reset();
spy2.calls.reset();
spy3.calls.reset();
// should call only 2nd because 1st was already removed and 2nd removes 3rd
spy2.andCallFake(remove3);
spy2.and.callFake(remove3);
child.$broadcast('evt');
expect(spy1).not.toHaveBeenCalled();
expect(spy2).toHaveBeenCalledOnce();
+1 -1
View File
@@ -198,7 +198,7 @@ describe('SCE', function() {
});
describe('$sce.parseAs', function($sce) {
describe('$sce.parseAs', function() {
it('should parse constant literals as trusted', inject(function($sce) {
expect($sce.parseAsJs('1')()).toBe(1);
expect($sce.parseAsJs('1', $sce.ANY)()).toBe(1);
+219 -218
View File
@@ -1,10 +1,9 @@
'use strict';
describe('$sniffer', function() {
function sniffer($window, $document) {
/* global $SnifferProvider: false */
$window.navigator = {};
$window.navigator = $window.navigator || {};
$document = jqLite($document || {});
if (!$document[0].body) {
$document[0].body = window.document.body;
@@ -12,14 +11,84 @@ describe('$sniffer', function() {
return new $SnifferProvider().$get[2]($window, $document);
}
describe('history', function() {
it('should be true if history.pushState defined', function() {
expect(sniffer({history: {pushState: noop, replaceState: noop}}).history).toBe(true);
var mockWindow = {
history: {
pushState: noop,
replaceState: noop
}
};
expect(sniffer(mockWindow).history).toBe(true);
});
it('should be false if history or pushState not defined', function() {
expect(sniffer({history: {}}).history).toBe(false);
expect(sniffer({}).history).toBe(false);
expect(sniffer({history: {}}).history).toBe(false);
});
it('should be false on Boxee box with an older version of Webkit', function() {
var mockWindow = {
history: {
pushState: noop
},
navigator: {
userAgent: 'boxee (alpha/Darwin 8.7.1 i386 - 0.9.11.5591)'
}
};
expect(sniffer(mockWindow).history).toBe(false);
});
it('should be false on Chrome Packaged Apps', function() {
// Chrome Packaged Apps are not allowed to access `window.history.pushState`.
// In Chrome, `window.app` might be available in "normal" webpages, but `window.app.runtime`
// only exists in the context of a packaged app.
expect(sniffer(createMockWindow()).history).toBe(true);
expect(sniffer(createMockWindow(true)).history).toBe(true);
expect(sniffer(createMockWindow(true, true)).history).toBe(false);
function createMockWindow(isChrome, isPackagedApp) {
var mockWindow = {
history: {
pushState: noop
}
};
if (isChrome) {
var chromeAppObj = isPackagedApp ? {runtime: {}} : {};
mockWindow.chrome = {app: chromeAppObj};
}
return mockWindow;
}
});
it('should not try to access `history.pushState` in Chrome Packaged Apps', function() {
var pushStateAccessCount = 0;
var mockHistory = Object.create(Object.prototype, {
pushState: {get: function() { pushStateAccessCount++; return noop; }}
});
var mockWindow = {
chrome: {
app: {
runtime: {}
}
},
history: mockHistory
};
sniffer(mockWindow);
expect(pushStateAccessCount).toBe(0);
});
});
@@ -28,11 +97,10 @@ describe('$sniffer', function() {
var mockDocument, mockDivElement, $sniffer;
beforeEach(function() {
mockDocument = {createElement: jasmine.createSpy('createElement')};
mockDocument.createElement.andCallFake(function(elm) {
if (elm === 'div') return mockDivElement;
});
var mockCreateElementFn = function(elm) { if (elm === 'div') return mockDivElement; };
var createElementSpy = jasmine.createSpy('createElement').and.callFake(mockCreateElementFn);
mockDocument = {createElement: createElementSpy};
$sniffer = sniffer({}, mockDocument);
});
@@ -83,7 +151,6 @@ describe('$sniffer', function() {
describe('vendorPrefix', function() {
it('should return the correct vendor prefix based on the browser', function() {
inject(function($sniffer, $window) {
var expectedPrefix;
@@ -101,237 +168,171 @@ describe('$sniffer', function() {
});
});
it('should still work for an older version of Webkit', function() {
module(function($provide) {
var doc = {
body: {
style: {
WebkitOpacity: '0'
}
}
};
$provide.value('$document', jqLite(doc));
});
inject(function($sniffer) {
expect($sniffer.vendorPrefix).toBe('webkit');
});
});
it('should still work for an older version of Webkit', function() {
var mockDocument = {
body: {
style: {
WebkitOpacity: '0'
}
}
};
expect(sniffer({}, mockDocument).vendorPrefix).toBe('webkit');
});
});
describe('animations', function() {
it('should be either true or false', function() {
inject(function($sniffer) {
expect($sniffer.animations).not.toBeUndefined();
});
});
it('should be either true or false', inject(function($sniffer) {
expect($sniffer.animations).toBeDefined();
}));
it('should be false when there is no animation style', function() {
module(function($provide) {
var doc = {
body: {
style: {}
}
};
$provide.value('$document', jqLite(doc));
});
inject(function($sniffer) {
expect($sniffer.animations).toBe(false);
});
var mockDocument = {
body: {
style: {}
}
};
expect(sniffer({}, mockDocument).animations).toBe(false);
});
it('should be true with vendor-specific animations', function() {
module(function($provide) {
var animationStyle = 'some_animation 2s linear';
var doc = {
body: {
style: {
WebkitAnimation: animationStyle,
MozAnimation: animationStyle
}
var animationStyle = 'some_animation 2s linear';
var mockDocument = {
body: {
style: {
WebkitAnimation: animationStyle,
MozAnimation: animationStyle
}
};
$provide.value('$document', jqLite(doc));
});
inject(function($sniffer) {
expect($sniffer.animations).toBe(true);
});
}
};
expect(sniffer({}, mockDocument).animations).toBe(true);
});
it('should be true with w3c-style animations', function() {
module(function($provide) {
var doc = {
body: {
style: {
animation: 'some_animation 2s linear'
}
var mockDocument = {
body: {
style: {
animation: 'some_animation 2s linear'
}
};
$provide.value('$document', jqLite(doc));
});
inject(function($sniffer) {
expect($sniffer.animations).toBe(true);
});
}
};
expect(sniffer({}, mockDocument).animations).toBe(true);
});
it('should be true on android with older body style properties', function() {
module(function($provide) {
var doc = {
body: {
style: {
webkitAnimation: ''
}
}
};
var win = {
navigator: {
userAgent: 'android 2'
}
};
$provide.value('$document', jqLite(doc));
$provide.value('$window', win);
});
inject(function($sniffer) {
expect($sniffer.animations).toBe(true);
});
});
it('should be true when an older version of Webkit is used', function() {
module(function($provide) {
var doc = {
body: {
style: {
WebkitOpacity: '0'
}
}
};
$provide.value('$document', jqLite(doc));
});
inject(function($sniffer) {
expect($sniffer.animations).toBe(false);
});
});
});
describe('transitions', function() {
it('should be either true or false', function() {
inject(function($sniffer) {
expect($sniffer.transitions).not.toBeUndefined();
});
});
it('should be false when there is no transition style', function() {
module(function($provide) {
var doc = {
body: {
style: {}
}
};
$provide.value('$document', jqLite(doc));
});
inject(function($sniffer) {
expect($sniffer.transitions).toBe(false);
});
});
it('should be true with vendor-specific transitions', function() {
module(function($provide) {
var transitionStyle = '1s linear all';
var doc = {
body: {
style: {
WebkitTransition: transitionStyle,
MozTransition: transitionStyle
}
}
};
$provide.value('$document', jqLite(doc));
});
inject(function($sniffer) {
expect($sniffer.transitions).toBe(true);
});
});
it('should be true with w3c-style transitions', function() {
module(function($provide) {
var doc = {
body: {
style: {
transition: '1s linear all'
}
}
};
$provide.value('$document', jqLite(doc));
});
inject(function($sniffer) {
expect($sniffer.transitions).toBe(true);
});
});
it('should be true on android with older body style properties', function() {
module(function($provide) {
var doc = {
body: {
style: {
webkitTransition: ''
}
}
};
var win = {
navigator: {
userAgent: 'android 2'
}
};
$provide.value('$document', jqLite(doc));
$provide.value('$window', win);
});
inject(function($sniffer) {
expect($sniffer.transitions).toBe(true);
});
});
});
describe('history', function() {
it('should be true on Boxee box with an older version of Webkit', function() {
module(function($provide) {
var doc = {
body: {
style: {}
}
};
var win = {
history: {
pushState: noop
},
navigator: {
userAgent: 'boxee (alpha/Darwin 8.7.1 i386 - 0.9.11.5591)'
}
};
$provide.value('$document', jqLite(doc));
$provide.value('$window', win);
});
inject(function($sniffer) {
expect($sniffer.history).toBe(false);
});
});
});
it('should provide the android version', function() {
module(function($provide) {
var win = {
var mockWindow = {
navigator: {
userAgent: 'android 2'
}
};
$provide.value('$document', jqLite({}));
$provide.value('$window', win);
var mockDocument = {
body: {
style: {
webkitAnimation: ''
}
}
};
expect(sniffer(mockWindow, mockDocument).animations).toBe(true);
});
inject(function($sniffer) {
expect($sniffer.android).toBe(2);
it('should be true when an older version of Webkit is used', function() {
var mockDocument = {
body: {
style: {
WebkitOpacity: '0'
}
}
};
expect(sniffer({}, mockDocument).animations).toBe(false);
});
});
describe('transitions', function() {
it('should be either true or false', inject(function($sniffer) {
expect($sniffer.transitions).toBeOneOf(true, false);
}));
it('should be false when there is no transition style', function() {
var mockDocument = {
body: {
style: {}
}
};
expect(sniffer({}, mockDocument).transitions).toBe(false);
});
it('should be true with vendor-specific transitions', function() {
var transitionStyle = '1s linear all';
var mockDocument = {
body: {
style: {
WebkitTransition: transitionStyle,
MozTransition: transitionStyle
}
}
};
expect(sniffer({}, mockDocument).transitions).toBe(true);
});
it('should be true with w3c-style transitions', function() {
var mockDocument = {
body: {
style: {
transition: '1s linear all'
}
}
};
expect(sniffer({}, mockDocument).transitions).toBe(true);
});
it('should be true on android with older body style properties', function() {
var mockWindow = {
navigator: {
userAgent: 'android 2'
}
};
var mockDocument = {
body: {
style: {
webkitTransition: ''
}
}
};
expect(sniffer(mockWindow, mockDocument).transitions).toBe(true);
});
});
describe('android', function() {
it('should provide the android version', function() {
var mockWindow = {
navigator: {
userAgent: 'android 2'
}
};
expect(sniffer(mockWindow).android).toBe(2);
});
});
});
+3 -3
View File
@@ -15,7 +15,7 @@ describe('$templateRequest', function() {
});
inject(function($templateRequest, $http, $templateCache) {
spyOn($http, 'get').andCallThrough();
spyOn($http, 'get').and.callThrough();
$templateRequest('tpl.html');
@@ -41,7 +41,7 @@ describe('$templateRequest', function() {
});
inject(function($templateRequest, $http, $templateCache) {
spyOn($http, 'get').andCallThrough();
spyOn($http, 'get').and.callThrough();
$templateRequest('tpl.html');
@@ -63,7 +63,7 @@ describe('$templateRequest', function() {
});
inject(function($templateRequest, $http, $cacheFactory) {
spyOn($http, 'get').andCallThrough();
spyOn($http, 'get').and.callThrough();
var customCache = $cacheFactory('customCache');
httpOptions.cache = customCache;
+13 -13
View File
@@ -14,13 +14,13 @@ describe('$timeout', function() {
$browser.defer.flush();
expect(counter).toBe(1);
expect(function() {$browser.defer.flush();}).toThrow('No deferred tasks to be flushed');
expect(function() {$browser.defer.flush();}).toThrowError('No deferred tasks to be flushed');
expect(counter).toBe(1);
}));
it('should call $apply after each callback is executed', inject(function($timeout, $rootScope) {
var applySpy = spyOn($rootScope, '$apply').andCallThrough();
var applySpy = spyOn($rootScope, '$apply').and.callThrough();
$timeout(noop);
expect(applySpy).not.toHaveBeenCalled();
@@ -28,17 +28,17 @@ describe('$timeout', function() {
$timeout.flush();
expect(applySpy).toHaveBeenCalledOnce();
applySpy.reset();
applySpy.calls.reset();
$timeout(noop);
$timeout(noop);
$timeout.flush();
expect(applySpy.callCount).toBe(2);
expect(applySpy).toHaveBeenCalledTimes(2);
}));
it('should NOT call $apply if skipApply is set to true', inject(function($timeout, $rootScope) {
var applySpy = spyOn($rootScope, '$apply').andCallThrough();
var applySpy = spyOn($rootScope, '$apply').and.callThrough();
$timeout(noop, 12, false);
expect(applySpy).not.toHaveBeenCalled();
@@ -50,8 +50,8 @@ describe('$timeout', function() {
it('should NOT call $evalAsync or $digest if invokeApply is set to false',
inject(function($timeout, $rootScope) {
var evalAsyncSpy = spyOn($rootScope, '$evalAsync').andCallThrough();
var digestSpy = spyOn($rootScope, '$digest').andCallThrough();
var evalAsyncSpy = spyOn($rootScope, '$evalAsync').and.callThrough();
var digestSpy = spyOn($rootScope, '$digest').and.callThrough();
var fulfilledSpy = jasmine.createSpy('fulfilled');
$timeout(fulfilledSpy, 1000, false);
@@ -67,8 +67,8 @@ describe('$timeout', function() {
it('should allow you to specify the delay time', inject(function($timeout, $browser) {
var defer = spyOn($browser, 'defer');
$timeout(noop, 123);
expect(defer.callCount).toEqual(1);
expect(defer.mostRecentCall.args[1]).toEqual(123);
expect(defer).toHaveBeenCalledTimes(1);
expect(defer.calls.mostRecent().args[1]).toEqual(123);
}));
@@ -87,7 +87,7 @@ describe('$timeout', function() {
it('should forget references to deferreds when callback called even if skipApply is true',
inject(function($timeout, $browser) {
// $browser.defer.cancel is only called on cancel if the deferred object is still referenced
var cancelSpy = spyOn($browser.defer, 'cancel').andCallThrough();
var cancelSpy = spyOn($browser.defer, 'cancel').and.callThrough();
var promise1 = $timeout(noop, 0, false);
var promise2 = $timeout(noop, 100, false);
@@ -171,7 +171,7 @@ describe('$timeout', function() {
it('should call $apply even if an exception is thrown in callback', inject(
function($timeout, $rootScope) {
var applySpy = spyOn($rootScope, '$apply').andCallThrough();
var applySpy = spyOn($rootScope, '$apply').and.callThrough();
$timeout(function() { throw "Test Error"; });
expect(applySpy).not.toHaveBeenCalled();
@@ -214,7 +214,7 @@ describe('$timeout', function() {
it('should forget references to relevant deferred even when exception is thrown',
inject(function($timeout, $browser) {
// $browser.defer.cancel is only called on cancel if the deferred object is still referenced
var cancelSpy = spyOn($browser.defer, 'cancel').andCallThrough();
var cancelSpy = spyOn($browser.defer, 'cancel').and.callThrough();
var promise = $timeout(function() { throw "Test Error"; }, 0, false);
$timeout.flush();
@@ -287,7 +287,7 @@ describe('$timeout', function() {
it('should forget references to relevant deferred', inject(function($timeout, $browser) {
// $browser.defer.cancel is only called on cancel if the deferred object is still referenced
var cancelSpy = spyOn($browser.defer, 'cancel').andCallThrough();
var cancelSpy = spyOn($browser.defer, 'cancel').and.callThrough();
var promise = $timeout(noop, 0, false);
+14 -14
View File
@@ -1171,11 +1171,11 @@ describe("ngAnimate $animateCss", function() {
inject(function($animateCss, $document, $rootElement, $timeout, $animate) {
var now = 0;
spyOn(Date, 'now').andCallFake(function() {
spyOn(Date, 'now').and.callFake(function() {
return now;
});
var cancelSpy = spyOn($timeout, 'cancel').andCallThrough();
var cancelSpy = spyOn($timeout, 'cancel').and.callThrough();
var doneSpy = jasmine.createSpy();
ss.addRule('.elm', 'transition:1s linear all;');
@@ -1198,7 +1198,7 @@ describe("ngAnimate $animateCss", function() {
expect(doneSpy).not.toHaveBeenCalled();
expect(cancelSpy).toHaveBeenCalled();
cancelSpy.reset();
cancelSpy.calls.reset();
// timeout will not be set again since the former animation is longer
animate(element, 'green', doneSpy);
@@ -1210,7 +1210,7 @@ describe("ngAnimate $animateCss", function() {
$animate.flush();
expect(doneSpy).toHaveBeenCalled();
expect(doneSpy.callCount).toBe(3);
expect(doneSpy).toHaveBeenCalledTimes(3);
function fastForwardClock(time) {
now += time;
@@ -1458,8 +1458,8 @@ describe("ngAnimate $animateCss", function() {
$rootElement.append(element);
jqLite($document[0].body).append($rootElement);
elementOnSpy = spyOn(element, 'on').andCallThrough();
elementOffSpy = spyOn(element, 'off').andCallThrough();
elementOnSpy = spyOn(element, 'on').and.callThrough();
elementOffSpy = spyOn(element, 'off').and.callThrough();
}));
they('should remove the $prop event listeners on cancel',
@@ -1477,12 +1477,12 @@ describe("ngAnimate $animateCss", function() {
triggerAnimationStartFrame();
expect(elementOnSpy).toHaveBeenCalledOnce();
expect(elementOnSpy.mostRecentCall.args[0]).toBe(event);
expect(elementOnSpy.calls.mostRecent().args[0]).toBe(event);
runner.cancel();
expect(elementOffSpy).toHaveBeenCalledOnce();
expect(elementOffSpy.mostRecentCall.args[0]).toBe(event);
expect(elementOffSpy.calls.mostRecent().args[0]).toBe(event);
});
});
@@ -1501,12 +1501,12 @@ describe("ngAnimate $animateCss", function() {
triggerAnimationStartFrame();
expect(elementOnSpy).toHaveBeenCalledOnce();
expect(elementOnSpy.mostRecentCall.args[0]).toBe(event);
expect(elementOnSpy.calls.mostRecent().args[0]).toBe(event);
progress(element, 10);
expect(elementOffSpy).toHaveBeenCalledOnce();
expect(elementOffSpy.mostRecentCall.args[0]).toBe(event);
expect(elementOffSpy.calls.mostRecent().args[0]).toBe(event);
});
});
@@ -1525,12 +1525,12 @@ describe("ngAnimate $animateCss", function() {
triggerAnimationStartFrame();
expect(elementOnSpy).toHaveBeenCalledOnce();
expect(elementOnSpy.mostRecentCall.args[0]).toBe(event);
expect(elementOnSpy.calls.mostRecent().args[0]).toBe(event);
$timeout.flush(15000);
expect(elementOffSpy).toHaveBeenCalledOnce();
expect(elementOffSpy.mostRecentCall.args[0]).toBe(event);
expect(elementOffSpy.calls.mostRecent().args[0]).toBe(event);
});
});
@@ -1545,7 +1545,7 @@ describe("ngAnimate $animateCss", function() {
element.on(event, otherEndSpy);
expect(elementOnSpy).toHaveBeenCalledOnce();
elementOnSpy.reset();
elementOnSpy.calls.reset();
var animator = $animateCss(element, {
event: 'enter',
@@ -1789,7 +1789,7 @@ describe("ngAnimate $animateCss", function() {
data.event = event;
}
var blockSpy = spyOn($window, 'blockTransitions').andCallThrough();
var blockSpy = spyOn($window, 'blockTransitions').and.callThrough();
data.skipBlocking = true;
var animator = $animateCss(element, data);
+68 -14
View File
@@ -142,7 +142,8 @@ describe("animations", function() {
from: { height: '50px' },
to: { width: '50px' },
addClass: 'one',
removeClass: 'two'
removeClass: 'two',
domOperation: undefined
};
var copiedOptions = copy(initialOptions);
@@ -1103,7 +1104,8 @@ describe("animations", function() {
$animate.removeClass(element, 'active-class');
$rootScope.$digest();
expect(doneHandler).toHaveBeenCalled();
// true = rejected
expect(doneHandler).toHaveBeenCalledWith(true);
}));
it('should cancel the previously running removeClass animation if a follow-up addClass animation is using the same class value',
@@ -1122,7 +1124,8 @@ describe("animations", function() {
$animate.addClass(element, 'active-class');
$rootScope.$digest();
expect(doneHandler).toHaveBeenCalled();
// true = rejected
expect(doneHandler).toHaveBeenCalledWith(true);
}));
it('should merge a follow-up animation that does not add classes into the previous animation (pre-digest)',
@@ -1197,6 +1200,29 @@ describe("animations", function() {
expect(capturedAnimation[2].addClass).toBe('blue');
}));
it('should NOT cancel a previously joined addClass+structural animation if a follow-up ' +
'removeClass animation is using the same class value (pre-digest)',
inject(function($animate, $rootScope) {
var runner = $animate.enter(element, parent);
$animate.addClass(element, 'active-class');
var doneHandler = jasmine.createSpy('enter done');
runner.done(doneHandler);
expect(doneHandler).not.toHaveBeenCalled();
$animate.removeClass(element, 'active-class');
$rootScope.$digest();
expect(capturedAnimation[1]).toBe('enter');
expect(capturedAnimation[2].addClass).toBe(null);
expect(capturedAnimation[2].removeClass).toBe(null);
expect(doneHandler).not.toHaveBeenCalled();
}));
});
describe('should merge', function() {
@@ -1911,14 +1937,16 @@ describe("animations", function() {
expect(capturedElement).toBe(element);
}));
it('should remove the event listener if the element is removed',
it('should remove all event listeners when the element is removed',
inject(function($animate, $rootScope, $rootElement) {
element = jqLite('<div></div>');
var count = 0;
var runner;
$animate.on('enter', element, counter);
$animate.on('addClass', element, counter);
$animate.on('addClass', element[0], counter);
function counter(element, phase) {
if (phase === 'start') {
@@ -1926,18 +1954,45 @@ describe("animations", function() {
}
}
$animate.enter(element, $rootElement);
runner = $animate.enter(element, $rootElement);
$rootScope.$digest();
$animate.flush();
expect(capturedAnimation).toBeTruthy();
$animate.flush();
runner.end(); // Otherwise the class animation won't run because enter is still in progress
expect(count).toBe(1);
capturedAnimation = null;
$animate.addClass(element, 'blue');
$rootScope.$digest();
expect(capturedAnimation).toBeTruthy();
$animate.flush();
expect(count).toBe(2);
capturedAnimation = null;
element.remove();
$animate.addClass(element, 'viljami');
runner = $animate.enter(element, $rootElement);
$rootScope.$digest();
expect(capturedAnimation).toBeTruthy();
$animate.flush();
expect(count).toBe(1);
runner.end(); // Otherwise the class animation won't run because enter is still in progress
expect(count).toBe(2);
capturedAnimation = null;
$animate.addClass(element, 'red');
$rootScope.$digest();
expect(capturedAnimation).toBeTruthy();
$animate.flush();
expect(count).toBe(2);
}));
it('should always detect registered callbacks after one postDigest has fired',
@@ -1954,15 +2009,15 @@ describe("animations", function() {
$rootScope.$digest();
registerCallback();
expect(spy.callCount).toBe(0);
expect(spy).not.toHaveBeenCalled();
$animate.flush();
// this is not 3 since the 3rd callback
// was added after the first callback
// was fired
expect(spy.callCount).toBe(2);
expect(spy).toHaveBeenCalledTimes(2);
spy.reset();
spy.calls.reset();
runner.end();
$animate.flush();
@@ -1970,7 +2025,7 @@ describe("animations", function() {
// now we expect all three callbacks
// to fire when the animation ends since
// the callback detection happens again
expect(spy.callCount).toBe(3);
expect(spy).toHaveBeenCalledTimes(3);
function registerCallback() {
$animate.on('enter', element, spy);
@@ -2115,7 +2170,6 @@ describe("animations", function() {
var callbackTriggered = false;
$animate.on($event, $document[0], function() {
callbackTriggered = true;
});
+4 -4
View File
@@ -477,16 +477,16 @@ describe('$$animation', function() {
it('should remove the $destroy event listener when the animation is closed',
inject(function($$animation, $rootScope) {
var addListen = spyOn(element, 'on').andCallThrough();
var removeListen = spyOn(element, 'off').andCallThrough();
var addListen = spyOn(element, 'on').and.callThrough();
var removeListen = spyOn(element, 'off').and.callThrough();
var runner = $$animation(element, 'someEvent');
var args = addListen.mostRecentCall.args[0];
var args = addListen.calls.mostRecent().args[0];
expect(args).toBe('$destroy');
runner.end();
args = removeListen.mostRecentCall.args[0];
args = removeListen.calls.mostRecent().args[0];
expect(args).toBe('$destroy');
}));
+42 -3
View File
@@ -425,12 +425,12 @@ describe('ngAnimate integration tests', function() {
$animate.flush();
expect(spy.callCount).toBe(1);
expect(spy).toHaveBeenCalledTimes(1);
browserTrigger(element, 'transitionend', { timeStamp: Date.now(), elapsedTime: 2 });
$animate.flush();
expect(spy.callCount).toBe(2);
expect(spy).toHaveBeenCalledTimes(2);
dealoc(element);
}));
@@ -732,7 +732,8 @@ describe('ngAnimate integration tests', function() {
from: { height: '50px' },
to: { width: '100px' },
addClass: 'one',
removeClass: 'two'
removeClass: 'two',
domOperation: undefined
};
var copiedOptions = copy(initialOptions);
@@ -755,5 +756,43 @@ describe('ngAnimate integration tests', function() {
expect(child.attr('style')).toContain('50px');
});
});
it('should execute the enter animation on a <form> with ngIf that has an ' +
'<input type="email" required>', function() {
var animationSpy = jasmine.createSpy();
module(function($animateProvider) {
$animateProvider.register('.animate-me', function() {
return {
enter: function(element, done) {
animationSpy();
done();
}
};
});
});
inject(function($animate, $rootScope, $compile) {
element = jqLite(
'<div>' +
'<form class="animate-me" ng-if="show">' +
'<input ng-model="myModel" type="email" required />' +
'</form>' +
'</div>');
html(element);
$compile(element)($rootScope);
$rootScope.show = true;
$rootScope.$digest();
$animate.flush();
expect(animationSpy).toHaveBeenCalled();
});
});
});
});
+1 -1
View File
@@ -654,7 +654,7 @@ describe('$aria', function() {
it('should update bindings when keypress handled', function() {
compileElement('<div ng-click="text = \'clicked!\'">{{text}}</div>');
expect(element.text()).toBe('');
spyOn(scope.$root, '$digest').andCallThrough();
spyOn(scope.$root, '$digest').and.callThrough();
element.triggerHandler({ type: 'keypress', keyCode: 13 });
expect(element.text()).toBe('clicked!');
expect(scope.$root.$digest).toHaveBeenCalledOnce();
+1 -1
View File
@@ -8,7 +8,7 @@ describe('$cookieStore', function() {
it('should get cookie', inject(function($cookieStore, $cookies) {
$cookies.getObject.andReturn('value');
$cookies.getObject.and.returnValue('value');
expect($cookieStore.get('name')).toBe('value');
expect($cookies.getObject).toHaveBeenCalledWith('name');
}));
+1 -1
View File
@@ -6,7 +6,7 @@ describe('$cookies', function() {
beforeEach(function() {
mockedCookies = {};
module('ngCookies', {
$$cookieWriter: jasmine.createSpy('$$cookieWriter').andCallFake(function(name, value) {
$$cookieWriter: jasmine.createSpy('$$cookieWriter').and.callFake(function(name, value) {
mockedCookies[name] = value;
}),
$$cookieReader: function() {
+4 -4
View File
@@ -100,7 +100,7 @@ describe('$$ngMessageFormat', function() {
$rootScope.$digest();
expect($rootScope.$countWatchers()).toBe(0);
expect(spy).toHaveBeenCalledWith('foo', 'foo', $rootScope);
expect(spy.calls.length).toBe(1);
expect(spy).toHaveBeenCalledTimes(1);
});
it('should stop watching strings with only constant expressions after first execution', function() {
@@ -109,7 +109,7 @@ describe('$$ngMessageFormat', function() {
$rootScope.$digest();
expect($rootScope.$countWatchers()).toBe(0);
expect(spy).toHaveBeenCalledWith('foo 42', 'foo 42', $rootScope);
expect(spy.calls.length).toBe(1);
expect(spy).toHaveBeenCalledTimes(1);
});
@@ -408,7 +408,7 @@ describe('$$ngMessageFormat', function() {
$rootScope.$digest();
expect($rootScope.$countWatchers()).toBe(0);
expect(spy).toHaveBeenCalledWith('foo', 'foo', $rootScope);
expect(spy.calls.length).toBe(1);
expect(spy).toHaveBeenCalledTimes(1);
})
);
@@ -419,7 +419,7 @@ describe('$$ngMessageFormat', function() {
$rootScope.$digest();
expect($rootScope.$countWatchers()).toBe(0);
expect(spy).toHaveBeenCalledWith('foo 42', 'foo 42', $rootScope);
expect(spy.calls.length).toBe(1);
expect(spy).toHaveBeenCalledTimes(1);
})
);
});
+153 -2
View File
@@ -437,9 +437,9 @@ describe('ngMessages', function() {
// Since we have spied on the `leave` method, the message node is still in the DOM
expect($animate.leave).toHaveBeenCalledOnce();
var nodeToRemove = $animate.leave.mostRecentCall.args[0][0];
var nodeToRemove = $animate.leave.calls.mostRecent().args[0][0];
expect(nodeToRemove).toBe(oldMessageNode);
$animate.leave.reset();
$animate.leave.calls.reset();
// Add the message back in
$rootScope.col = { primary: true };
@@ -485,6 +485,126 @@ describe('ngMessages', function() {
});
});
describe('ngMessage nested nested inside elements', function() {
it('should not crash or leak memory when the messages are transcluded, the first message is ' +
'visible, and ngMessages is removed by ngIf', function() {
module(function($compileProvider) {
$compileProvider.directive('messageWrap', function() {
return {
transclude: true,
scope: {
col: '=col'
},
template: '<div ng-messages="col"><ng-transclude></ng-transclude></div>'
};
});
});
inject(function($rootScope, $compile) {
element = $compile('<div><div ng-if="show"><div message-wrap col="col">' +
' <div ng-message="a">A</div>' +
' <div ng-message="b">B</div>' +
'</div></div></div>')($rootScope);
$rootScope.$apply(function() {
$rootScope.show = true;
$rootScope.col = {
a: true,
b: true
};
});
expect(messageChildren(element).length).toBe(1);
expect(trim(element.text())).toEqual('A');
$rootScope.$apply('show = false');
expect(messageChildren(element).length).toBe(0);
});
});
it('should not crash when the first of two nested messages is removed', function() {
inject(function($rootScope, $compile) {
element = $compile(
'<div ng-messages="col">' +
'<div class="wrapper">' +
'<div remove-me ng-message="a">A</div>' +
'<div ng-message="b">B</div>' +
'</div>' +
'</div>'
)($rootScope);
$rootScope.$apply(function() {
$rootScope.col = {
a: true,
b: false
};
});
expect(messageChildren(element).length).toBe(1);
expect(trim(element.text())).toEqual('A');
var ctrl = element.controller('ngMessages');
var deregisterSpy = spyOn(ctrl, 'deregister').and.callThrough();
var nodeA = element[0].querySelector('[ng-message="a"]');
jqLite(nodeA).remove();
$rootScope.$digest(); // The next digest triggers the error
// Make sure removing the element triggers the deregistration in ngMessages
expect(trim(deregisterSpy.calls.mostRecent().args[0].nodeValue)).toBe('ngMessage: a');
expect(messageChildren(element).length).toBe(0);
});
});
it('should not crash, but show deeply nested messages correctly after a message ' +
'has been removed', function() {
inject(function($rootScope, $compile) {
element = $compile(
'<div ng-messages="col" ng-messages-multiple>' +
'<div class="another-wrapper">' +
'<div ng-message="a">A</div>' +
'<div class="wrapper">' +
'<div ng-message="b">B</div>' +
'<div ng-message="c">C</div>' +
'</div>' +
'<div ng-message="d">D</div>' +
'</div>' +
'</div>'
)($rootScope);
$rootScope.$apply(function() {
$rootScope.col = {
a: true,
b: true
};
});
expect(messageChildren(element).length).toBe(2);
expect(trim(element.text())).toEqual('AB');
var ctrl = element.controller('ngMessages');
var deregisterSpy = spyOn(ctrl, 'deregister').and.callThrough();
var nodeB = element[0].querySelector('[ng-message="b"]');
jqLite(nodeB).remove();
$rootScope.$digest(); // The next digest triggers the error
// Make sure removing the element triggers the deregistration in ngMessages
expect(trim(deregisterSpy.calls.mostRecent().args[0].nodeValue)).toBe('ngMessage: b');
expect(messageChildren(element).length).toBe(1);
expect(trim(element.text())).toEqual('A');
});
});
});
describe('when including templates', function() {
they('should work with a dynamic collection model which is managed by ngRepeat',
{'<div ng-messages-include="...">': '<div ng-messages="item">' +
@@ -691,6 +811,37 @@ describe('ngMessages', function() {
expect(trim(element.text())).toEqual("C");
}));
it('should properly detect a previous message, even if it was registered later',
inject(function($compile, $rootScope, $templateCache) {
$templateCache.put('include.html', '<div ng-message="a">A</div>');
var html =
'<div ng-messages="items">' +
'<div ng-include="\'include.html\'"></div>' +
'<div ng-message="b">B</div>' +
'<div ng-message="c">C</div>' +
'</div>';
element = $compile(html)($rootScope);
$rootScope.$apply('items = {b: true, c: true}');
expect(element.text()).toBe('B');
var ctrl = element.controller('ngMessages');
var deregisterSpy = spyOn(ctrl, 'deregister').and.callThrough();
var nodeB = element[0].querySelector('[ng-message="b"]');
jqLite(nodeB).remove();
// Make sure removing the element triggers the deregistration in ngMessages
expect(trim(deregisterSpy.calls.mostRecent().args[0].nodeValue)).toBe('ngMessage: b');
$rootScope.$apply('items.a = true');
expect(element.text()).toBe('A');
})
);
});
describe('when multiple', function() {
+78 -96
View File
@@ -26,17 +26,19 @@ describe('ngMock', function() {
it('should fake getLocalDateString method', function() {
//0 in -3h
var t0 = new angular.mock.TzDate(-3, 0);
expect(t0.toLocaleDateString()).toMatch('1970');
var millenium = new Date('2000').getTime();
//0 in +0h
var t1 = new angular.mock.TzDate(0, 0);
expect(t1.toLocaleDateString()).toMatch('1970');
// millenium in -3h
var t0 = new angular.mock.TzDate(-3, millenium);
expect(t0.toLocaleDateString()).toMatch('2000');
//0 in +3h
var t2 = new angular.mock.TzDate(3, 0);
expect(t2.toLocaleDateString()).toMatch('1969');
// millenium in +0h
var t1 = new angular.mock.TzDate(0, millenium);
expect(t1.toLocaleDateString()).toMatch('2000');
// millenium in +3h
var t2 = new angular.mock.TzDate(3, millenium);
expect(t2.toLocaleDateString()).toMatch('1999');
});
@@ -65,7 +67,7 @@ describe('ngMock', function() {
//0:00 in +3h
var t2 = new angular.mock.TzDate(3, jan2);
expect(t2.getHours()).toMatch(21);
expect(t2.getHours()).toMatch('21');
});
@@ -88,11 +90,11 @@ describe('ngMock', function() {
//0:15 in +3h
var t2 = new angular.mock.TzDate(3, minutes(15));
expect(t2.getMinutes()).toMatch(15);
expect(t2.getMinutes()).toMatch('15');
//0:15 in +3.25h
var t2a = new angular.mock.TzDate(3.25, minutes(15));
expect(t2a.getMinutes()).toMatch(0);
expect(t2a.getMinutes()).toMatch('0');
});
@@ -107,7 +109,7 @@ describe('ngMock', function() {
//0 in +3h
var t2 = new angular.mock.TzDate(3, 0);
expect(t2.getSeconds()).toMatch(0);
expect(t2.getSeconds()).toMatch('0');
});
@@ -154,7 +156,7 @@ describe('ngMock', function() {
it('should throw error when no third param but toString called', function() {
expect(function() { new angular.mock.TzDate(0,0).toString(); }).
toThrow('Method \'toString\' is not implemented in the TzDate mock');
toThrowError('Method \'toString\' is not implemented in the TzDate mock');
});
});
@@ -300,7 +302,7 @@ describe('ngMock', function() {
it('should call $apply after each task is executed', inject(function($interval, $rootScope) {
var applySpy = spyOn($rootScope, '$apply').andCallThrough();
var applySpy = spyOn($rootScope, '$apply').and.callThrough();
$interval(noop, 1000);
expect(applySpy).not.toHaveBeenCalled();
@@ -308,18 +310,18 @@ describe('ngMock', function() {
$interval.flush(1000);
expect(applySpy).toHaveBeenCalledOnce();
applySpy.reset();
applySpy.calls.reset();
$interval(noop, 1000);
$interval(noop, 1000);
$interval.flush(1000);
expect(applySpy.callCount).toBe(3);
expect(applySpy).toHaveBeenCalledTimes(3);
}));
it('should NOT call $apply if invokeApply is set to false',
inject(function($interval, $rootScope) {
var applySpy = spyOn($rootScope, '$apply').andCallThrough();
var applySpy = spyOn($rootScope, '$apply').and.callThrough();
var counter = 0;
$interval(function increment() { counter++; }, 1000, 0, false);
@@ -437,7 +439,7 @@ describe('ngMock', function() {
it('should call $apply even if an exception is thrown in callback', inject(
function($interval, $rootScope) {
var applySpy = spyOn($rootScope, '$apply').andCallThrough();
var applySpy = spyOn($rootScope, '$apply').and.callThrough();
$interval(function() { throw "Test Error"; }, 1000);
expect(applySpy).not.toHaveBeenCalled();
@@ -581,7 +583,7 @@ describe('ngMock', function() {
});
it('should throw an exception if there is nothing to be flushed', function() {
expect(function() {browser.defer.flush();}).toThrow('No deferred tasks to be flushed');
expect(function() {browser.defer.flush();}).toThrowError('No deferred tasks to be flushed');
});
});
@@ -622,7 +624,7 @@ describe('ngMock', function() {
module(function($exceptionHandlerProvider) {
expect(function() {
$exceptionHandlerProvider.mode('XXX');
}).toThrow("Unknown mode 'XXX', only 'log'/'rethrow' modes are allowed!");
}).toThrowError("Unknown mode 'XXX', only 'log'/'rethrow' modes are allowed!");
});
inject(); // Trigger the tests in `module`
@@ -651,7 +653,7 @@ describe('ngMock', function() {
$timeout(noop);
var expectedError = 'Deferred tasks to flush (1): {id: 0, time: 0}';
expect(function() {$timeout.verifyNoPendingTasks();}).toThrow(expectedError);
expect(function() {$timeout.verifyNoPendingTasks();}).toThrowError(expectedError);
}));
@@ -897,32 +899,12 @@ describe('ngMock', function() {
});
});
describe('this', function() {
it('should set `this` to be the jasmine context', inject(function() {
expect(this instanceof jasmine.Spec).toBe(true);
}));
it('should set `this` to be the jasmine context when inlined in a test', function() {
var tested = false;
inject(function() {
expect(this instanceof jasmine.Spec).toBe(true);
tested = true;
});
expect(tested).toBe(true);
});
});
it('should not change thrown Errors', inject(function($sniffer) {
expect(function() {
inject(function() {
throw new Error('test message');
});
}).toThrow('test message');
}).toThrow(jasmine.objectContaining({message: 'test message'}));
}));
it('should not change thrown strings', inject(function($sniffer) {
@@ -973,7 +955,7 @@ describe('ngMock', function() {
hb.when('GET', '/url1').respond(200, 'content', {});
hb.when('GET', '/url1').respond(201, 'another', {});
callback.andCallFake(function(status, response) {
callback.and.callFake(function(status, response) {
expect(status).toBe(200);
expect(response).toBe('content');
});
@@ -990,7 +972,7 @@ describe('ngMock', function() {
hb.when('GET', '/url1').respond(200, mockObject, {});
callback.andCallFake(function(status, response) {
callback.and.callFake(function(status, response) {
expect(status).toBe(200);
expect(response).toEqual({a: 'b'});
expect(response).not.toBe(mockObject);
@@ -1003,7 +985,7 @@ describe('ngMock', function() {
// Fire it again and verify that the returned mock data has not been
// modified.
callback.reset();
callback.calls.reset();
hb('GET', '/url1', null, callback);
hb.flush();
expect(callback).toHaveBeenCalledOnce();
@@ -1017,7 +999,7 @@ describe('ngMock', function() {
hb.when('GET', '/url1').respond(200, mockBlob, {});
callback.andCallFake(function(status, response) {
callback.and.callFake(function(status, response) {
expect(response).not.toBe(mockBlob);
expect(response.size).toBe(13);
expect(response.type).toBe('application/json');
@@ -1035,7 +1017,7 @@ describe('ngMock', function() {
hb.when('GET', '/url1').respond(200, 'content');
expect(function() {
hb('GET', '/xxx');
}).toThrow('Unexpected request: GET /xxx\nNo more request expected');
}).toThrowError('Unexpected request: GET /xxx\nNo more request expected');
});
@@ -1106,7 +1088,7 @@ describe('ngMock', function() {
it('should match only method', function() {
hb.when('GET').respond(202, 'c');
callback.andCallFake(function(status, response) {
callback.and.callFake(function(status, response) {
expect(status).toBe(202);
expect(response).toBe('c');
});
@@ -1129,9 +1111,9 @@ describe('ngMock', function() {
hb.flush();
expect(callback.callCount).toBe(2);
expect(callback.argsForCall[0]).toEqual([201, 'second', '', '']);
expect(callback.argsForCall[1]).toEqual([200, 'first', '', '']);
expect(callback).toHaveBeenCalledTimes(2);
expect(callback.calls.argsFor(0)).toEqual([201, 'second', '', '']);
expect(callback.calls.argsFor(1)).toEqual([200, 'first', '', '']);
});
@@ -1145,7 +1127,7 @@ describe('ngMock', function() {
});
it('should default status code to 200', function() {
callback.andCallFake(function(status, response) {
callback.and.callFake(function(status, response) {
expect(status).toBe(200);
expect(response).toBe('some-data');
});
@@ -1156,7 +1138,7 @@ describe('ngMock', function() {
hb('GET', '/url2', null, callback);
hb.flush();
expect(callback).toHaveBeenCalled();
expect(callback.callCount).toBe(2);
expect(callback).toHaveBeenCalledTimes(2);
});
it('should default status code to 200 and provide status text', function() {
@@ -1211,9 +1193,9 @@ describe('ngMock', function() {
hb.flush();
expect(callback.callCount).toBe(2);
expect(callback.argsForCall[0]).toEqual([200, 'first', '', '']);
expect(callback.argsForCall[1]).toEqual([200, 'second', '', '']);
expect(callback).toHaveBeenCalledTimes(2);
expect(callback.calls.argsFor(0)).toEqual([200, 'first', '', '']);
expect(callback.calls.argsFor(1)).toEqual([200, 'second', '', '']);
});
it('should be able to override response of expect definition', function() {
@@ -1263,12 +1245,12 @@ describe('ngMock', function() {
expect(function() {
hb('GET', '/url2', null, noop, {});
}).toThrow('Unexpected request: GET /url2\nExpected GET /url1');
}).toThrowError('Unexpected request: GET /url2\nExpected GET /url1');
});
it('should have precedence over when()', function() {
callback.andCallFake(function(status, response) {
callback.and.callFake(function(status, response) {
expect(status).toBe(300);
expect(response).toBe('expect');
});
@@ -1288,8 +1270,8 @@ describe('ngMock', function() {
expect(function() {
hb('GET', '/match', null, noop, {});
}).toThrow('Expected GET /match with different headers\n' +
'EXPECTED: {"Content-Type":"application/json"}\nGOT: {}');
}).toThrowError('Expected GET /match with different headers\n' +
'EXPECTED: {"Content-Type":"application/json"}\nGOT: {}');
});
@@ -1299,8 +1281,8 @@ describe('ngMock', function() {
expect(function() {
hb('GET', '/match', 'different', noop, {});
}).toThrow('Expected GET /match with different data\n' +
'EXPECTED: some-data\nGOT: different');
}).toThrowError('Expected GET /match with different data\n' +
'EXPECTED: some-data\nGOT: different');
});
@@ -1325,13 +1307,13 @@ describe('ngMock', function() {
expect(function() {
hb('GET', '/match', '{"a":1,"b":3}', noop, {});
}).toThrow('Expected GET /match with different data\n' +
'EXPECTED: {"a":1,"b":2}\nGOT: {"a":1,"b":3}');
}).toThrowError('Expected GET /match with different data\n' +
'EXPECTED: {"a":1,"b":2}\nGOT: {"a":1,"b":3}');
});
it("should use when's respond() when no expect() respond is defined", function() {
callback.andCallFake(function(status, response) {
callback.and.callFake(function(status, response) {
expect(status).toBe(201);
expect(response).toBe('data');
});
@@ -1367,7 +1349,7 @@ describe('ngMock', function() {
hb.flush(2);
expect(callback).toHaveBeenCalled();
expect(callback.callCount).toBe(2);
expect(callback).toHaveBeenCalledTimes(2);
});
@@ -1375,19 +1357,19 @@ describe('ngMock', function() {
hb.when('GET').respond(200, '');
hb('GET', '/url', null, callback);
expect(function() {hb.flush(2);}).toThrow('No more pending request to flush !');
expect(function() {hb.flush(2);}).toThrowError('No more pending request to flush !');
expect(callback).toHaveBeenCalledOnce();
});
it('should throw exception when no request to flush', function() {
expect(function() {hb.flush();}).toThrow('No pending request to flush !');
expect(function() {hb.flush();}).toThrowError('No pending request to flush !');
hb.when('GET').respond(200, '');
hb('GET', '/some', null, callback);
hb.flush();
expect(function() {hb.flush();}).toThrow('No pending request to flush !');
expect(function() {hb.flush();}).toThrowError('No pending request to flush !');
});
@@ -1396,7 +1378,7 @@ describe('ngMock', function() {
hb.expect('GET', '/url2').respond();
hb('GET', '/url1', null, angular.noop);
expect(function() {hb.flush();}).toThrow('Unsatisfied requests: GET /url2');
expect(function() {hb.flush();}).toThrowError('Unsatisfied requests: GET /url2');
});
});
@@ -1404,7 +1386,7 @@ describe('ngMock', function() {
it('should abort requests when timeout promise resolves', function() {
hb.expect('GET', '/url1').respond(200);
var canceler, then = jasmine.createSpy('then').andCallFake(function(fn) {
var canceler, then = jasmine.createSpy('then').and.callFake(function(fn) {
canceler = fn;
});
@@ -1435,7 +1417,7 @@ describe('ngMock', function() {
hb.when('GET', '/test');
expect(function() {
hb('GET', '/test', null, callback);
}).toThrow('No response defined !');
}).toThrowError('No response defined !');
});
@@ -1443,7 +1425,7 @@ describe('ngMock', function() {
hb.expect('GET', '/url');
expect(function() {
hb('GET', '/url', null, callback);
}).toThrow('No response defined !');
}).toThrowError('No response defined !');
});
@@ -1471,7 +1453,7 @@ describe('ngMock', function() {
hb('POST', '/u1', 'ddd', noop, {});
expect(function() {hb.verifyNoOutstandingExpectation();}).
toThrow('Unsatisfied requests: GET /u2, POST /u3');
toThrowError('Unsatisfied requests: GET /u2, POST /u3');
});
@@ -1502,7 +1484,7 @@ describe('ngMock', function() {
expect(function() {
hb.verifyNoOutstandingRequest();
}).toThrow('Unflushed requests: 1');
}).toThrowError('Unflushed requests: 1');
});
});
@@ -1879,7 +1861,7 @@ describe('ngMock', function() {
inject(function($componentController, $rootScope) {
var $scope = {};
var ctrl = $componentController('test', { $scope: $scope, a: 'A', b: 'B' }, { x: 'X', y: 'Y' });
expect(ctrl).toEqual({ $scope: $scope, a: 'A', b: 'B', x: 'X', y: 'Y' });
expect(ctrl).toEqual(extend(new TestController($scope, 'A', 'B'), { x: 'X', y: 'Y' }));
expect($scope.$ctrl).toBe(ctrl);
});
});
@@ -1899,7 +1881,7 @@ describe('ngMock', function() {
inject(function($componentController, $rootScope) {
var $scope = {};
var ctrl = $componentController('test', { $scope: $scope, a: 'A', b: 'B' }, { x: 'X', y: 'Y' });
expect(ctrl).toEqual({ $scope: $scope, a: 'A', b: 'B', x: 'X', y: 'Y' });
expect(ctrl).toEqual(extend(new TestController($scope, 'A', 'B'), { x: 'X', y: 'Y' }));
expect($scope.$ctrl).toBe(ctrl);
});
});
@@ -1919,7 +1901,7 @@ describe('ngMock', function() {
inject(function($componentController, $rootScope) {
var $scope = {};
var ctrl = $componentController('test', { $scope: $scope, a: 'A', b: 'B' }, { x: 'X', y: 'Y' });
expect(ctrl).toEqual({ $scope: $scope, a: 'A', b: 'B', x: 'X', y: 'Y' });
expect(ctrl).toEqual(extend(new TestController($scope, 'A', 'B'), { x: 'X', y: 'Y' }));
expect($scope.$ctrl).toBe(ctrl);
});
});
@@ -1939,7 +1921,7 @@ describe('ngMock', function() {
inject(function($componentController, $rootScope) {
var $scope = {};
var ctrl = $componentController('test', { $scope: $scope, a: 'A', b: 'B' }, { x: 'X', y: 'Y' });
expect(ctrl).toEqual({ $scope: $scope, a: 'A', b: 'B', x: 'X', y: 'Y' });
expect(ctrl).toEqual(extend(new TestController($scope, 'A', 'B'), {x: 'X', y: 'Y'}));
expect($scope.testCtrl).toBe(ctrl);
});
});
@@ -1958,7 +1940,7 @@ describe('ngMock', function() {
});
inject(function($componentController, $rootScope) {
var ctrl = $componentController('test', { $scope: {} });
expect(ctrl).toEqual({ r: 6779 });
expect(ctrl).toEqual(new TestController());
});
});
@@ -1976,7 +1958,7 @@ describe('ngMock', function() {
});
inject(function($componentController, $rootScope) {
var ctrl = $componentController('test', { $scope: {} });
expect(ctrl).toEqual({ r: 22926 });
expect(ctrl).toEqual(new TestController());
});
});
@@ -1998,7 +1980,7 @@ describe('ngMock', function() {
});
inject(function($componentController, $rootScope) {
var ctrl = $componentController('test', { $scope: {} });
expect(ctrl).toEqual({ r: 18842 });
expect(ctrl).toEqual(new TestController());
});
});
@@ -2033,7 +2015,7 @@ describe('ngMock', function() {
inject(function($componentController, $rootScope) {
expect(function() {
$componentController('test', { $scope: {} });
}).toThrow('No component found');
}).toThrowError('No component found');
});
});
@@ -2059,7 +2041,7 @@ describe('ngMock', function() {
expect(function() {
var $scope = {};
$componentController('test', { $scope: $scope, a: 'A', b: 'B' }, { x: 'X', y: 'Y' });
}).toThrow('Too many components found');
}).toThrowError('Too many components found');
});
});
});
@@ -2210,7 +2192,7 @@ describe('ngMockE2E', function() {
it('should throw an error if there is nothing to animate', inject(function($animate) {
expect(function() {
$animate.flush();
}).toThrow('No pending animations ready to be closed or flushed');
}).toThrowError('No pending animations ready to be closed or flushed');
}));
it('should trigger the animation to start',
@@ -2305,11 +2287,11 @@ describe('ngMockE2E', function() {
expect(spy).not.toHaveBeenCalled();
$animate.flush();
expect(spy.callCount).toBe(1);
expect(spy).toHaveBeenCalledTimes(1);
trackedAnimations[0]();
$animate.flush();
expect(spy.callCount).toBe(2);
expect(spy).toHaveBeenCalledTimes(2);
}));
});
@@ -2382,7 +2364,7 @@ describe('ngMockE2E', function() {
expect(function() {
$animate.closeAndFlush();
}).toThrow('No pending animations ready to be closed or flushed');
}).toThrowError('No pending animations ready to be closed or flushed');
}));
});
@@ -2412,7 +2394,7 @@ describe('`afterEach` clean-up', function() {
// Spy on `angular.element.cleanData()`, so the next test can verify
// that it has been called as necessary
prevCleanDataSpy = spyOn(angular.element, 'cleanData').andCallThrough();
prevCleanDataSpy = spyOn(angular.element, 'cleanData').and.callThrough();
return $delegate;
});
@@ -2429,9 +2411,9 @@ describe('`afterEach` clean-up', function() {
it('should clean up `$rootElement` after each test', function() {
// One call is made by `testabilityPatch`'s `dealoc()`
// We want to verify the subsequent call, made by `angular-mocks`
expect(prevCleanDataSpy.callCount).toBe(2);
expect(prevCleanDataSpy).toHaveBeenCalledTimes(2);
var cleanUpNodes = prevCleanDataSpy.calls[1].args[0];
var cleanUpNodes = prevCleanDataSpy.calls.argsFor(1)[0];
expect(cleanUpNodes.length).toBe(1);
expect(cleanUpNodes[0]).toBe(prevRootElement[0]);
});
@@ -2455,7 +2437,7 @@ describe('`afterEach` clean-up', function() {
// Spy on `angular.element.cleanData()`, so the next test can verify
// that it has been called as necessary
prevCleanDataSpy = spyOn(angular.element, 'cleanData').andCallThrough();
prevCleanDataSpy = spyOn(angular.element, 'cleanData').and.callThrough();
return prevRootElement;
});
@@ -2482,9 +2464,9 @@ describe('`afterEach` clean-up', function() {
function() {
// One call is made by `testabilityPatch`'s `dealoc()`
// We want to verify the subsequent call, made by `angular-mocks`
expect(prevCleanDataSpy.callCount).toBe(2);
expect(prevCleanDataSpy).toHaveBeenCalledTimes(2);
var cleanUpNodes = prevCleanDataSpy.calls[1].args[0];
var cleanUpNodes = prevCleanDataSpy.calls.argsFor(1)[0];
expect(cleanUpNodes.length).toBe(2);
expect(cleanUpNodes[0]).toBe(prevOriginalRootElement[0]);
expect(cleanUpNodes[1]).toBe(prevRootElement[0]);
@@ -2523,7 +2505,7 @@ describe('`afterEach` clean-up', function() {
it('should set up spies for the next test to verify that `$rootScope` was cleaned up',
inject(function($rootScope) {
prevRootScope = $rootScope;
prevDestroySpy = spyOn($rootScope, '$destroy').andCallThrough();
prevDestroySpy = spyOn($rootScope, '$destroy').and.callThrough();
})
);
+38 -38
View File
@@ -441,8 +441,8 @@ describe("basic usage", function() {
$httpBackend.flush();
expect(cc).toEqualData({id: 123, name: 'misko'});
expect(callback).toHaveBeenCalledOnce();
expect(callback.mostRecentCall.args[0]).toEqual(cc);
expect(callback.mostRecentCall.args[1]()).toEqual({});
expect(callback.calls.mostRecent().args[0]).toEqual(cc);
expect(callback.calls.mostRecent().args[1]()).toEqual(Object.create(null));
});
@@ -456,8 +456,8 @@ describe("basic usage", function() {
$httpBackend.flush();
expect(cc).toEqualData({id: 123, number: '9876'});
expect(callback.mostRecentCall.args[0]).toEqual(cc);
expect(callback.mostRecentCall.args[1]()).toEqual({});
expect(callback.calls.mostRecent().args[0]).toEqual(cc);
expect(callback.calls.mostRecent().args[1]()).toEqual(Object.create(null));
});
@@ -484,8 +484,8 @@ describe("basic usage", function() {
$httpBackend.expect('GET', '/CreditCard/123').respond({id: {key: 123}, number: '9876'});
cc.$get(callback);
$httpBackend.flush();
expect(callback.mostRecentCall.args[0]).toEqual(cc);
expect(callback.mostRecentCall.args[1]()).toEqual({});
expect(callback.calls.mostRecent().args[0]).toEqual(cc);
expect(callback.calls.mostRecent().args[1]()).toEqual(Object.create(null));
expect(cc.number).toEqual('9876');
});
@@ -510,8 +510,8 @@ describe("basic usage", function() {
$httpBackend.flush();
expect(ccs).toEqualData([{id:1}, {id:2}]);
expect(callback.mostRecentCall.args[0]).toEqual(ccs);
expect(callback.mostRecentCall.args[1]()).toEqual({});
expect(callback.calls.mostRecent().args[0]).toEqual(ccs);
expect(callback.calls.mostRecent().args[1]()).toEqual(Object.create(null));
});
@@ -533,17 +533,17 @@ describe("basic usage", function() {
expect(callback).not.toHaveBeenCalled();
$httpBackend.flush();
expect(callback.mostRecentCall.args[0]).toEqualData({});
expect(callback.mostRecentCall.args[1]()).toEqual({});
expect(callback.calls.mostRecent().args[0]).toEqualData({});
expect(callback.calls.mostRecent().args[1]()).toEqual(Object.create(null));
callback.reset();
callback.calls.reset();
$httpBackend.expect('DELETE', '/CreditCard/333').respond(204, null);
CreditCard.remove({id:333}, callback);
expect(callback).not.toHaveBeenCalled();
$httpBackend.flush();
expect(callback.mostRecentCall.args[0]).toEqualData({});
expect(callback.mostRecentCall.args[1]()).toEqual({});
expect(callback.calls.mostRecent().args[0]).toEqualData({});
expect(callback.calls.mostRecent().args[1]()).toEqual(Object.create(null));
});
@@ -591,8 +591,8 @@ describe("basic usage", function() {
$httpBackend.flush();
expect(cc).toEqualData({id:123});
expect(callback.mostRecentCall.args[0]).toEqual(cc);
expect(callback.mostRecentCall.args[1]()).toEqual({header1: 'a'});
expect(callback.calls.mostRecent().args[0]).toEqual(cc);
expect(callback.calls.mostRecent().args[1]()).toEqual(extend(Object.create(null), {header1: 'a'}));
});
@@ -714,7 +714,7 @@ describe("basic usage", function() {
$httpBackend.flush();
expect(callback).toHaveBeenCalledOnce();
expect(callback.mostRecentCall.args[0]).toBe(cc);
expect(callback.calls.mostRecent().args[0]).toBe(cc);
});
@@ -725,7 +725,7 @@ describe("basic usage", function() {
cc.$promise.then(callback);
$httpBackend.flush();
callback.reset();
callback.calls.reset();
cc.$promise.then(callback);
$rootScope.$apply(); //flush async queue
@@ -766,7 +766,7 @@ describe("basic usage", function() {
cc.$promise.then(null, callback);
$httpBackend.flush();
var response = callback.mostRecentCall.args[0];
var response = callback.calls.mostRecent().args[0];
expect(response.data).toEqual('resource not found');
expect(response.status).toEqual(404);
@@ -824,7 +824,7 @@ describe("basic usage", function() {
$httpBackend.flush();
expect(callback).toHaveBeenCalledOnce();
expect(cc).toEqualData({id: 123, number: '9876'});
callback.reset();
callback.calls.reset();
$httpBackend.expect('POST', '/CreditCard').respond({id: 1, number: '9'});
@@ -907,7 +907,7 @@ describe("basic usage", function() {
$httpBackend.flush();
expect(callback).toHaveBeenCalledOnce();
expect(callback.mostRecentCall.args[0]).toBe(ccs);
expect(callback.calls.mostRecent().args[0]).toBe(ccs);
});
@@ -918,7 +918,7 @@ describe("basic usage", function() {
ccs.$promise.then(callback);
$httpBackend.flush();
callback.reset();
callback.calls.reset();
ccs.$promise.then(callback);
$rootScope.$apply(); //flush async queue
@@ -945,7 +945,7 @@ describe("basic usage", function() {
ccs.$promise.then(null, callback);
$httpBackend.flush();
var response = callback.mostRecentCall.args[0];
var response = callback.calls.mostRecent().args[0];
expect(response.data).toEqual('resource not found');
expect(response.status).toEqual(404);
@@ -1000,7 +1000,7 @@ describe("basic usage", function() {
$httpBackend.flush();
expect(callback).toHaveBeenCalledOnce();
var response = callback.mostRecentCall.args[0];
var response = callback.calls.mostRecent().args[0];
expect(response.resource).toBe(ccs);
expect(response.status).toBe(200);
expect(response.config).toBeDefined();
@@ -1029,7 +1029,7 @@ describe("basic usage", function() {
$httpBackend.flush();
expect(callback).toHaveBeenCalledOnce();
var response = callback.mostRecentCall.args[0];
var response = callback.calls.mostRecent().args[0];
expect(response.status).toBe(404);
expect(response.config).toBeDefined();
});
@@ -1042,7 +1042,7 @@ describe("basic usage", function() {
errorCB;
beforeEach(function() {
errorCB = jasmine.createSpy('error').andCallFake(function(response) {
errorCB = jasmine.createSpy('error').and.callFake(function(response) {
expect(response.data).toBe(ERROR_RESPONSE);
expect(response.status).toBe(ERROR_CODE);
});
@@ -1210,8 +1210,8 @@ describe("basic usage", function() {
$httpBackend.flush();
expect(user).toEqualData({id: 123, name: 'user1'});
expect(callback).toHaveBeenCalledOnce();
expect(callback.mostRecentCall.args[0]).toEqual(user);
expect(callback.mostRecentCall.args[1]()).toEqual({});
expect(callback.calls.mostRecent().args[0]).toEqual(user);
expect(callback.calls.mostRecent().args[1]()).toEqual(Object.create(null));
});
it('should append when an id is supplied', function() {
@@ -1222,8 +1222,8 @@ describe("basic usage", function() {
$httpBackend.flush();
expect(user).toEqualData({id: 123, name: 'newName'});
expect(callback).toHaveBeenCalledOnce();
expect(callback.mostRecentCall.args[0]).toEqual(user);
expect(callback.mostRecentCall.args[1]()).toEqual({});
expect(callback.calls.mostRecent().args[0]).toEqual(user);
expect(callback.calls.mostRecent().args[1]()).toEqual(Object.create(null));
});
it('should append when an id is supplied and the format is a parameter', function() {
@@ -1234,8 +1234,8 @@ describe("basic usage", function() {
$httpBackend.flush();
expect(user).toEqualData({id: 123, name: 'newName'});
expect(callback).toHaveBeenCalledOnce();
expect(callback.mostRecentCall.args[0]).toEqual(user);
expect(callback.mostRecentCall.args[1]()).toEqual({});
expect(callback.calls.mostRecent().args[0]).toEqual(user);
expect(callback.calls.mostRecent().args[1]()).toEqual(Object.create(null));
});
});
@@ -1345,7 +1345,7 @@ describe('errors', function() {
expect(successSpy).not.toHaveBeenCalled();
expect(failureSpy).toHaveBeenCalled();
expect(failureSpy.mostRecentCall.args[0]).toMatch(
expect(failureSpy.calls.mostRecent().args[0]).toMatch(
/^\[\$resource:badcfg\] Error in resource configuration for action `query`\. Expected response to contain an array but got an object \(Request: GET \/Customer\/123\)/
);
});
@@ -1362,7 +1362,7 @@ describe('errors', function() {
expect(successSpy).not.toHaveBeenCalled();
expect(failureSpy).toHaveBeenCalled();
expect(failureSpy.mostRecentCall.args[0]).toMatch(
expect(failureSpy.calls.mostRecent().args[0]).toMatch(
/^\[\$resource:badcfg\] Error in resource configuration for action `get`\. Expected response to contain an object but got an array \(Request: GET \/Customer\/123\)/
);
});
@@ -1376,7 +1376,7 @@ describe('cancelling requests', function() {
beforeEach(module('ngResource', function($provide) {
$provide.decorator('$http', function($delegate) {
httpSpy = jasmine.createSpy('$http').andCallFake($delegate);
httpSpy = jasmine.createSpy('$http').and.callFake($delegate);
return httpSpy;
});
}));
@@ -1401,7 +1401,7 @@ describe('cancelling requests', function() {
$httpBackend.flush();
expect(httpSpy).toHaveBeenCalledOnce();
expect(httpSpy.calls[0].args[0].timeout).toBe(10000);
expect(httpSpy.calls.argsFor(0)[0].timeout).toBe(10000);
});
it('should delete non-numeric timeouts in actions and log a $debug message',
@@ -1420,7 +1420,7 @@ describe('cancelling requests', function() {
$httpBackend.flush();
expect(httpSpy).toHaveBeenCalledOnce();
expect(httpSpy.calls[0].args[0].timeout).toBeUndefined();
expect(httpSpy.calls.argsFor(0)[0].timeout).toBeUndefined();
expect($log.debug).toHaveBeenCalledOnceWith('ngResource:\n' +
' Only numeric values are allowed as `timeout`.\n' +
' Promises are not supported in $resource, because the same value would ' +
@@ -1444,8 +1444,8 @@ describe('cancelling requests', function() {
var creditCard = CreditCard.get();
expect(creditCard.$cancelRequest).toBeDefined();
expect(httpSpy.calls[0].args[0].timeout).toEqual(jasmine.any($q));
expect(httpSpy.calls[0].args[0].timeout.then).toBeDefined();
expect(httpSpy.calls.argsFor(0)[0].timeout).toEqual(jasmine.any($q));
expect(httpSpy.calls.argsFor(0)[0].timeout.then).toBeDefined();
expect($log.debug).toHaveBeenCalledOnceWith('ngResource:\n' +
' Only numeric values are allowed as `timeout`.\n' +
+1 -1
View File
@@ -937,7 +937,7 @@ describe('ngView animations', function() {
function spyOnAnimateEnter() {
return function($animate) {
spyOn($animate, 'enter').andCallThrough();
spyOn($animate, 'enter').and.callThrough();
};
}
+1 -1
View File
@@ -72,7 +72,7 @@ describe('$routeParams', function() {
$location.path('/qux//bazValue');
$rootScope.$digest();
expect($routeParams).toEqual({baz: 'bazValue', bar: undefined});
expect($routeParams).toEqual({baz: 'bazValue'});
});
});
+36 -36
View File
@@ -155,7 +155,7 @@ describe('$route', function() {
$location.path('/NONE');
$rootScope.$digest();
expect(log).toEqual('before();after();');
expect($route.current).toEqual(null);
expect($route.current).toEqual(undefined);
});
});
@@ -213,7 +213,7 @@ describe('$route', function() {
$location.path('/NONE');
$rootScope.$digest();
expect(log).toEqual('before();after();');
expect($route.current).toEqual(null);
expect($route.current).toEqual(undefined);
});
});
@@ -266,7 +266,7 @@ describe('$route', function() {
$location.path('/BLANK');
$rootScope.$digest();
expect(log).toEqual('before();after();');
expect($route.current).toEqual(null);
expect($route.current).toEqual(undefined);
log = '';
$location.path('/Book2/Moby/one/two/Chapter/Intro').search('p=123');
@@ -278,7 +278,7 @@ describe('$route', function() {
$location.path('/BOOK2/Moby/one/two/CHAPTER/Intro').search('p=123');
$rootScope.$digest();
expect(log).toEqual('before();after();');
expect($route.current).toEqual(null);
expect($route.current).toEqual(undefined);
});
});
@@ -418,7 +418,7 @@ describe('$route', function() {
$rootScope.$on('$routeChangeStart', callback);
$location.path('/test');
$rootScope.$digest();
callback.reset();
callback.calls.reset();
$location.search({any: true});
$rootScope.$digest();
@@ -536,7 +536,7 @@ describe('$route', function() {
expect($route.current.controller).toBe(NotFoundCtrl);
expect(onChangeSpy).toHaveBeenCalled();
onChangeSpy.reset();
onChangeSpy.calls.reset();
$location.path('/foo');
$rootScope.$digest();
@@ -555,7 +555,7 @@ describe('$route', function() {
inject(function($route, $location, $rootScope) {
var currentRoute, nextRoute,
onChangeSpy = jasmine.createSpy('onChange').andCallFake(function(e, next) {
onChangeSpy = jasmine.createSpy('onChange').and.callFake(function(e, next) {
currentRoute = $route.current;
nextRoute = next;
});
@@ -575,7 +575,7 @@ describe('$route', function() {
expect(nextRoute.templateUrl).toBe('404.html');
expect($route.current.templateUrl).toBe('404.html');
expect(onChangeSpy).toHaveBeenCalled();
onChangeSpy.reset();
onChangeSpy.calls.reset();
// match regular route
$location.path('/foo');
@@ -585,7 +585,7 @@ describe('$route', function() {
expect(nextRoute.templateUrl).toBe('foo.html');
expect($route.current.templateUrl).toEqual('foo.html');
expect(onChangeSpy).toHaveBeenCalled();
onChangeSpy.reset();
onChangeSpy.calls.reset();
// match otherwise route again
$location.path('/anotherUnknownRoute');
@@ -936,14 +936,14 @@ describe('$route', function() {
$rootScope.$digest();
expect($location.path()).toBe('/foo');
expect($route.current.templateUrl).toBe('foo.html');
expect(onChangeSpy.callCount).toBe(2);
expect(onChangeSpy).toHaveBeenCalledTimes(2);
onChangeSpy.reset();
onChangeSpy.calls.reset();
$location.path('/baz');
$rootScope.$digest();
expect($location.path()).toBe('/bar');
expect($route.current.templateUrl).toBe('bar.html');
expect(onChangeSpy.callCount).toBe(2);
expect(onChangeSpy).toHaveBeenCalledTimes(2);
});
});
@@ -1066,13 +1066,13 @@ describe('$route', function() {
$routeProvider.when('/foo/:id/:extra', {redirectTo: '/bar/:id'});
});
inject(function($browser, $route, $location, $rootScope) {
var $browserUrl = spyOnlyCallsWithArgs($browser, 'url').andCallThrough();
var $browserUrl = spyOnlyCallsWithArgs($browser, 'url').and.callThrough();
$location.path('/foo/id3/eId');
$rootScope.$digest();
expect($location.path()).toEqual('/bar/id3');
expect($browserUrl.mostRecentCall.args)
expect($browserUrl.calls.mostRecent().args)
.toEqual(['http://server/#/bar/id3?extra=eId', true, null]);
});
});
@@ -1093,7 +1093,7 @@ describe('$route', function() {
$rootScope.$digest();
expect(reloaded).toHaveBeenCalled();
expect($routeParams).toEqual({});
reloaded.reset();
reloaded.calls.reset();
// trigger reload
$location.search({foo: 'bar'});
@@ -1122,9 +1122,9 @@ describe('$route', function() {
$location.path('/foo');
$rootScope.$digest();
expect(routeChange).toHaveBeenCalled();
expect(routeChange.callCount).toBe(2);
expect(routeChange).toHaveBeenCalledTimes(2);
expect(routeUpdate).not.toHaveBeenCalled();
routeChange.reset();
routeChange.calls.reset();
// don't trigger reload
$location.search({foo: 'bar'});
@@ -1151,14 +1151,14 @@ describe('$route', function() {
$location.path('/foo/aaa');
$rootScope.$digest();
expect(routeChange).toHaveBeenCalled();
expect(routeChange.callCount).toBe(2);
routeChange.reset();
expect(routeChange).toHaveBeenCalledTimes(2);
routeChange.calls.reset();
$location.path('/foo/bbb');
$rootScope.$digest();
expect(routeChange).toHaveBeenCalled();
expect(routeChange.callCount).toBe(2);
routeChange.reset();
expect(routeChange).toHaveBeenCalledTimes(2);
routeChange.calls.reset();
$location.search({foo: 'bar'});
$rootScope.$digest();
@@ -1187,18 +1187,18 @@ describe('$route', function() {
$location.path('/foo');
$rootScope.$digest();
expect(routeParamsWatcher).toHaveBeenCalledWith({});
routeParamsWatcher.reset();
routeParamsWatcher.calls.reset();
// trigger reload
$location.search({foo: 'bar'});
$rootScope.$digest();
expect(routeParamsWatcher).toHaveBeenCalledWith({foo: 'bar'});
routeParamsWatcher.reset();
routeParamsWatcher.calls.reset();
$location.path('/bar/123').search({});
$rootScope.$digest();
expect(routeParamsWatcher).toHaveBeenCalledWith({barId: '123'});
routeParamsWatcher.reset();
routeParamsWatcher.calls.reset();
// don't trigger reload
$location.search({foo: 'bar'});
@@ -1296,8 +1296,8 @@ describe('$route', function() {
expect(routeChangeSuccessSpy).toHaveBeenCalledOnce();
expect($log.debug.logs).toEqual([['initialized']]);
routeChangeStartSpy.reset();
routeChangeSuccessSpy.reset();
routeChangeStartSpy.calls.reset();
routeChangeSuccessSpy.calls.reset();
$log.reset();
$route.reload();
@@ -1318,11 +1318,11 @@ describe('$route', function() {
expect(routeChangeSuccessSpy).toHaveBeenCalledOnce();
expect($log.debug.logs).toEqual([['initialized']]);
routeChangeStartSpy.reset();
routeChangeSuccessSpy.reset();
routeChangeStartSpy.calls.reset();
routeChangeSuccessSpy.calls.reset();
$log.reset();
routeChangeStartSpy.andCallFake(function(evt) { evt.preventDefault(); });
routeChangeStartSpy.and.callFake(function(evt) { evt.preventDefault(); });
$route.reload();
$rootScope.$digest();
@@ -1339,7 +1339,7 @@ describe('$route', function() {
expect(routeChangeSuccessSpy).toHaveBeenCalledOnce();
expect($log.debug.logs).toEqual([['initialized']]);
routeChangeSuccessSpy.reset();
routeChangeSuccessSpy.calls.reset();
$log.reset();
$location.search('a=b');
@@ -1372,7 +1372,7 @@ describe('$route', function() {
$location.path('/bar/1');
$rootScope.$digest();
routeChangeSpy.reset();
routeChangeSpy.calls.reset();
$route.updateParams({barId: '2'});
$rootScope.$digest();
@@ -1395,7 +1395,7 @@ describe('$route', function() {
$location.path('/bar/1/2/3/4');
$rootScope.$digest();
routeChangeSpy.reset();
routeChangeSpy.calls.reset();
$route.updateParams({barId: '5', fooId: '6', spamId: '7', eggId: '8'});
$rootScope.$digest();
@@ -1418,7 +1418,7 @@ describe('$route', function() {
$location.path('/bar/1/2/3/4');
$rootScope.$digest();
routeChangeSpy.reset();
routeChangeSpy.calls.reset();
$route.updateParams({barId: '5', fooId: '6'});
$rootScope.$digest();
@@ -1443,7 +1443,7 @@ describe('$route', function() {
$location.path('/bar/1/2/3');
$location.search({initial: 'true'});
$rootScope.$digest();
routeChangeSpy.reset();
routeChangeSpy.calls.reset();
$route.updateParams({barId: '5', fooId: '6', eggId: '4'});
$rootScope.$digest();
@@ -1468,7 +1468,7 @@ describe('$route', function() {
$location.path('/bar/1/2/3');
$location.search({initial: 'true'});
$rootScope.$digest();
routeChangeSpy.reset();
routeChangeSpy.calls.reset();
$route.updateParams({barId: '5', fooId: '6', eggId: '4'});
$rootScope.$digest();
@@ -1481,7 +1481,7 @@ describe('$route', function() {
});
it('should complain if called without an existing route', inject(function($route) {
expect($route.updateParams).toThrowMinErr('ngRoute', 'norout');
expect(function() { $route.updateParams(); }).toThrowMinErr('ngRoute', 'norout');
}));
});
});
+1 -1
View File
@@ -113,7 +113,7 @@ describe('linky', function() {
it('should pass url as parameter to custom attribute function', function() {
var linkParameters = jasmine.createSpy('linkParameters').andReturn({"class": "blue"});
var linkParameters = jasmine.createSpy('linkParameters').and.returnValue({"class": "blue"});
linky("http://example.com", "_self", linkParameters);
expect(linkParameters).toHaveBeenCalledWith('http://example.com');
});
+19 -23
View File
@@ -393,21 +393,21 @@ describe('HTML', function() {
it('should call the uri validator', function() {
writer.start('a', {href:'someUrl'}, false);
expect(uriValidator).toHaveBeenCalledWith('someUrl', false);
uriValidator.reset();
uriValidator.calls.reset();
writer.start('img', {src:'someImgUrl'}, false);
expect(uriValidator).toHaveBeenCalledWith('someImgUrl', true);
uriValidator.reset();
uriValidator.calls.reset();
writer.start('someTag', {src:'someNonUrl'}, false);
expect(uriValidator).not.toHaveBeenCalled();
});
it('should drop non valid uri attributes', function() {
uriValidator.andReturn(false);
uriValidator.and.returnValue(false);
writer.start('a', {href:'someUrl'}, false);
expect(html).toEqual('<a>');
html = '';
uriValidator.andReturn(true);
uriValidator.and.returnValue(true);
writer.start('a', {href:'someUrl'}, false);
expect(html).toEqual('<a href="someUrl">');
});
@@ -416,22 +416,18 @@ describe('HTML', function() {
describe('uri checking', function() {
beforeEach(function() {
this.addMatchers({
jasmine.addMatchers({
toBeValidUrl: function() {
var sanitize;
inject(function($sanitize) {
sanitize = $sanitize;
});
var input = '<a href="' + this.actual + '"></a>';
return sanitize(input) === input;
},
toBeValidImageSrc: function() {
var sanitize;
inject(function($sanitize) {
sanitize = $sanitize;
});
var input = '<img src="' + this.actual + '"/>';
return sanitize(input) === input;
return {
compare: function(actual) {
var sanitize;
inject(function($sanitize) {
sanitize = $sanitize;
});
var input = '<a href="' + actual + '"></a>';
return { pass: sanitize(input) === input };
}
};
}
});
});
@@ -442,12 +438,12 @@ describe('HTML', function() {
$provide.value('$$sanitizeUri', $$sanitizeUri);
});
inject(function() {
$$sanitizeUri.andReturn('someUri');
$$sanitizeUri.and.returnValue('someUri');
expectHTML('<a href="someUri"></a>').toEqual('<a href="someUri"></a>');
expect($$sanitizeUri).toHaveBeenCalledWith('someUri', false);
$$sanitizeUri.andReturn('unsafe:someUri');
$$sanitizeUri.and.returnValue('unsafe:someUri');
expectHTML('<a href="someUri"></a>').toEqual('<a></a>');
});
});
@@ -458,12 +454,12 @@ describe('HTML', function() {
$provide.value('$$sanitizeUri', $$sanitizeUri);
});
inject(function() {
$$sanitizeUri.andReturn('someUri');
$$sanitizeUri.and.returnValue('someUri');
expectHTML('<img src="someUri"/>').toEqual('<img src="someUri">');
expect($$sanitizeUri).toHaveBeenCalledWith('someUri', true);
$$sanitizeUri.andReturn('unsafe:someUri');
$$sanitizeUri.and.returnValue('unsafe:someUri');
expectHTML('<img src="someUri"/>').toEqual('<img>');
});
});
+4 -4
View File
@@ -129,10 +129,10 @@ describe('angular.scenario.Application', function() {
}
};
jqLite(testWindow.document).data('$injector', $injector);
var resumeBootstrapSpy = spyOn(testWindow.angular, 'resumeBootstrap').andReturn($injector);
var resumeBootstrapSpy = spyOn(testWindow.angular, 'resumeBootstrap').and.returnValue($injector);
var injectorGet = $injector.get;
spyOn($injector, 'get').andCallFake(function(name) {
spyOn($injector, 'get').and.callFake(function(name) {
switch (name) {
case "$rootElement": return jqLite(testWindow.document);
default: return injectorGet(name);
@@ -162,7 +162,7 @@ describe('angular.scenario.Application', function() {
jqLite(testWindow.document).data('$injector', $injector);
var injectorGet = $injector.get;
var injectorSpy = spyOn($injector, 'get').andCallFake(function(name) {
var injectorSpy = spyOn($injector, 'get').and.callFake(function(name) {
switch (name) {
case "$rootElement": return jqLite(testWindow.document);
default: return injectorGet(name);
@@ -179,7 +179,7 @@ describe('angular.scenario.Application', function() {
expect(app.rootElement).toBeUndefined;
expect(injectorSpy).not.toHaveBeenCalled();
var resumeBootstrapSpy = spyOn(testWindow.angular, 'resumeBootstrap').andReturn($injector);
var resumeBootstrapSpy = spyOn(testWindow.angular, 'resumeBootstrap').and.returnValue($injector);
testWindow.angular.resumeDeferredBootstrap();
expect(app.rootElement).toBe(testWindow.document);
expect(resumeBootstrapSpy).toHaveBeenCalled();
+1 -1
View File
@@ -107,7 +107,7 @@ describe('angular.scenario.Describe', function() {
angular.scenario.Describe.id = 0;
var a = new angular.scenario.Describe();
var b = new angular.scenario.Describe();
expect(a.id).toNotEqual(b.id);
expect(a.id).not.toEqual(b.id);
});
it('should create uniqueIds for each spec', function() {
+10 -10
View File
@@ -42,7 +42,7 @@ describe('angular.scenario.ObjectModel', function() {
it('should value default empty value', function() {
expect(model.value).toEqual({
name: '',
children: []
children: {}
});
});
@@ -160,8 +160,8 @@ describe('angular.scenario.ObjectModel', function() {
model.on('SpecBegin', callback);
runner.emit('SpecBegin', spec);
expect(callback.mostRecentCall.args[0] instanceof Spec).toBe(true);
expect(callback.mostRecentCall.args[0].name).toEqual(spec.name);
expect(callback.calls.mostRecent().args[0] instanceof Spec).toBe(true);
expect(callback.calls.mostRecent().args[0].name).toEqual(spec.name);
});
it('should forward SpecError event', function() {
@@ -178,7 +178,7 @@ describe('angular.scenario.ObjectModel', function() {
runner.emit('SpecBegin', spec);
runner.emit('SpecError', spec, error);
var param = callback.mostRecentCall.args[0];
var param = callback.calls.mostRecent().args[0];
expect(param instanceof Spec).toBe(true);
expect(param.name).toEqual(spec.name);
expect(param.status).toEqual('error');
@@ -198,8 +198,8 @@ describe('angular.scenario.ObjectModel', function() {
runner.emit('SpecBegin', spec);
runner.emit('SpecEnd', spec);
expect(callback.mostRecentCall.args[0] instanceof Spec).toBe(true);
expect(callback.mostRecentCall.args[0].name).toEqual(spec.name);
expect(callback.calls.mostRecent().args[0] instanceof Spec).toBe(true);
expect(callback.calls.mostRecent().args[0].name).toEqual(spec.name);
});
it('should forward StepBegin event', function() {
@@ -215,7 +215,7 @@ describe('angular.scenario.ObjectModel', function() {
runner.emit('SpecBegin', spec);
runner.emit('StepBegin', spec, step);
var params = callback.mostRecentCall.args;
var params = callback.calls.mostRecent().args;
expect(params[0] instanceof Spec).toBe(true);
expect(params[0].name).toEqual(spec.name);
expect(params[1] instanceof Step).toBe(true);
@@ -237,7 +237,7 @@ describe('angular.scenario.ObjectModel', function() {
runner.emit('StepBegin', spec, step);
runner.emit('StepError', spec, step, error);
var params = callback.mostRecentCall.args;
var params = callback.calls.mostRecent().args;
expect(params[0] instanceof Spec).toBe(true);
expect(params[0].name).toEqual(spec.name);
expect(params[1] instanceof Step).toBe(true);
@@ -261,7 +261,7 @@ describe('angular.scenario.ObjectModel', function() {
runner.emit('StepBegin', spec, step);
runner.emit('StepFailure', spec, step, error);
var params = callback.mostRecentCall.args;
var params = callback.calls.mostRecent().args;
expect(params[0] instanceof Spec).toBe(true);
expect(params[0].name).toEqual(spec.name);
expect(params[1] instanceof Step).toBe(true);
@@ -284,7 +284,7 @@ describe('angular.scenario.ObjectModel', function() {
runner.emit('StepBegin', spec, step);
runner.emit('StepEnd', spec, step);
var params = callback.mostRecentCall.args;
var params = callback.calls.mostRecent().args;
expect(params[0] instanceof Spec).toBe(true);
expect(params[0].name).toEqual(spec.name);
expect(params[1] instanceof Step).toBe(true);

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