Compare commits
158 Commits
9.1.1
...
10.0.0-nex
Author | SHA1 | Date | |
---|---|---|---|
b40b5576df | |||
03f2f1ae47 | |||
a8978ebf8e | |||
7d7b59efa5 | |||
4cf89d4bd7 | |||
d47b318fee | |||
e485236502 | |||
41667de778 | |||
ef4736d052 | |||
db4a448439 | |||
7549c65502 | |||
1beccc1a6c | |||
0075017b43 | |||
4374931b0e | |||
64022f51d4 | |||
f9f6e2e1b3 | |||
aecf9de738 | |||
81195a238b | |||
96a3de6364 | |||
00027130ea | |||
4a18428de6 | |||
7f28845f58 | |||
1dd0b6cc18 | |||
e0415dbf16 | |||
95fc3d4c5c | |||
e145fa13b1 | |||
5fa7b8ba56 | |||
2c7d366c82 | |||
2dd6f25647 | |||
d2623f13d9 | |||
7d0af179e3 | |||
416c786774 | |||
93302b7fb8 | |||
eb8c6c7eff | |||
8660806ddc | |||
4c5e085c93 | |||
d707124fd9 | |||
aece3669e5 | |||
09c84169d5 | |||
d43c30688a | |||
ee70a18a75 | |||
76a8cd57ae | |||
4c7f89ff6d | |||
f9fb8338f5 | |||
6b3aa60446 | |||
2463548fa7 | |||
1140bbc25c | |||
1b4df6484e | |||
f40d51733a | |||
e893c5a330 | |||
8be8466a00 | |||
c8bef1259c | |||
568e9df1d6 | |||
a555fdba32 | |||
36535e9abd | |||
6402a9ae2a | |||
ca25c957bf | |||
93f07aee6c | |||
0af6e9fcbb | |||
008e12edda | |||
1927d0c7db | |||
9a0a90feb3 | |||
76d86d5a07 | |||
de7a9a3b93 | |||
04f61c0c3e | |||
c810ac7153 | |||
c24ad560fa | |||
3d2db5c5f0 | |||
32eafef6a7 | |||
2366480250 | |||
8e55a11283 | |||
e9de28111d | |||
75afd80ae8 | |||
a5eb0e56b6 | |||
4d458db1b5 | |||
24a92472bf | |||
43006bcc45 | |||
326240eb91 | |||
b59bc0e38c | |||
5516802142 | |||
cc4b813e75 | |||
64631063ae | |||
719224bffd | |||
fe2b6923ba | |||
38ad1d97ab | |||
372b9101e2 | |||
7e62aa0c6e | |||
36e927a8c6 | |||
9d8bb634f9 | |||
c5c57f6737 | |||
51a89c32c4 | |||
e1ac2efe4a | |||
702e17cfe2 | |||
4d1d0fa03b | |||
ec8bae1b27 | |||
08348fc2e8 | |||
63fbc71439 | |||
b44f7b5e16 | |||
c5df9ce474 | |||
2510e7dad6 | |||
80c68583d1 | |||
5b6ced5599 | |||
a383116b43 | |||
c6dd900f60 | |||
5ac308060d | |||
bc089abd32 | |||
c94a33c525 | |||
5cee709266 | |||
2d0847c307 | |||
995cd15a69 | |||
16497438d6 | |||
b54db86f43 | |||
44acf6734b | |||
b07b6edc2a | |||
83e4a76afa | |||
bfa7b1a494 | |||
8e0dec538e | |||
1c385a1c22 | |||
f9bc84cd99 | |||
2d7c95fb70 | |||
29e8a64cf0 | |||
00efacf561 | |||
d96995b4e3 | |||
fc3e5cb6d3 | |||
d37dad82f1 | |||
22710fc353 | |||
4f66250618 | |||
d783519835 | |||
b8e9a30d3b | |||
32ce8b1326 | |||
ff4eb0cb63 | |||
9ba46d9f88 | |||
b14ac96750 | |||
55dac05cf2 | |||
ae28d7c0b2 | |||
380de1e7b4 | |||
4f9717331d | |||
36fc28642a | |||
a323b9b1a3 | |||
58f4254fba | |||
4419907b08 | |||
9d415f9c3f | |||
fced8ee40e | |||
1cb7b88505 | |||
0e76b32aa5 | |||
e81ad3a1bc | |||
c3a85ceabc | |||
85e0e366df | |||
0ce8ad3493 | |||
528e25a81a | |||
d25012e864 | |||
c21c46a8e8 | |||
9cc8bd5f7d | |||
ae3eaf8b16 | |||
d714b95fb9 | |||
912692137a | |||
6fc85073d2 | |||
989dea7083 |
68
CHANGELOG.md
68
CHANGELOG.md
@ -1,3 +1,70 @@
|
||||
<a name="10.0.0-next.1"></a>
|
||||
# [10.0.0-next.1](https://github.com/angular/angular/compare/10.0.0-next.0...10.0.0-next.1) (2020-04-08)
|
||||
|
||||
This release contains various API docs improvements.
|
||||
|
||||
<a name="10.0.0-next.0"></a>
|
||||
# [10.0.0-next.0](https://github.com/angular/angular/compare/9.1.0-rc.0...10.0.0-next.0) (2020-04-08)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **common:** let `KeyValuePipe` accept type unions with `null` ([#36093](https://github.com/angular/angular/issues/36093)) ([d783519](https://github.com/angular/angular/commit/d783519)), closes [#35743](https://github.com/angular/angular/issues/35743)
|
||||
* **compiler:** avoid undefined expressions in holey array ([#36343](https://github.com/angular/angular/issues/36343)) ([5516802](https://github.com/angular/angular/commit/5516802))
|
||||
* **compiler:** record correct end of expression ([#34690](https://github.com/angular/angular/issues/34690)) ([df890d7](https://github.com/angular/angular/commit/df890d7)), closes [#33477](https://github.com/angular/angular/issues/33477)
|
||||
* **compiler:** resolve enum values in binary operations ([#36461](https://github.com/angular/angular/issues/36461)) ([64022f5](https://github.com/angular/angular/commit/64022f5)), closes [#35584](https://github.com/angular/angular/issues/35584)
|
||||
* **compiler-cli:** pass real source spans where they are empty ([#31805](https://github.com/angular/angular/issues/31805)) ([e893c5a](https://github.com/angular/angular/commit/e893c5a))
|
||||
* **core:** avoid migration error when non-existent symbol is imported ([#36367](https://github.com/angular/angular/issues/36367)) ([d43c306](https://github.com/angular/angular/commit/d43c306)), closes [#36346](https://github.com/angular/angular/issues/36346)
|
||||
* **core:** ngOnDestroy on multi providers called with incorrect context ([#35840](https://github.com/angular/angular/issues/35840)) ([95fc3d4](https://github.com/angular/angular/commit/95fc3d4)), closes [#35231](https://github.com/angular/angular/issues/35231)
|
||||
* **core:** run `APP_INITIALIZER`s before accessing `LOCALE_ID` token in Ivy TestBed ([#36237](https://github.com/angular/angular/issues/36237)) ([1649743](https://github.com/angular/angular/commit/1649743)), closes [#36230](https://github.com/angular/angular/issues/36230)
|
||||
* **core:** undecorated-classes-with-decorated-fields migration does not decorate derived classes ([#35339](https://github.com/angular/angular/issues/35339)) ([32eafef](https://github.com/angular/angular/commit/32eafef)), closes [#34376](https://github.com/angular/angular/issues/34376)
|
||||
* **core:** workaround Terser inlining bug ([#36200](https://github.com/angular/angular/issues/36200)) ([0ce8ad3](https://github.com/angular/angular/commit/0ce8ad3))
|
||||
* **elements:** correctly handle setting inputs to `undefined` ([#36140](https://github.com/angular/angular/issues/36140)) ([9ba46d9](https://github.com/angular/angular/commit/9ba46d9))
|
||||
* **elements:** correctly set `SimpleChange#firstChange` for pre-existing inputs ([#36140](https://github.com/angular/angular/issues/36140)) ([b14ac96](https://github.com/angular/angular/commit/b14ac96)), closes [#36130](https://github.com/angular/angular/issues/36130)
|
||||
* **language-service:** infer type of elements of array-like objects ([#36312](https://github.com/angular/angular/issues/36312)) ([fe2b692](https://github.com/angular/angular/commit/fe2b692)), closes [#36191](https://github.com/angular/angular/issues/36191)
|
||||
* **language-service:** use the `HtmlAst` to get the span of HTML tag ([#36371](https://github.com/angular/angular/issues/36371)) ([81195a2](https://github.com/angular/angular/commit/81195a2))
|
||||
* **localize:** allow ICU expansion case to start with any character except `}` ([#36123](https://github.com/angular/angular/issues/36123)) ([fced8ee](https://github.com/angular/angular/commit/fced8ee)), closes [#31586](https://github.com/angular/angular/issues/31586)
|
||||
* **ngcc:** add process title ([#36448](https://github.com/angular/angular/issues/36448)) ([76a8cd5](https://github.com/angular/angular/commit/76a8cd5)), closes [/github.com/angular/angular/issues/36414#issuecomment-609644282](https://github.com//github.com/angular/angular/issues/36414/issues/issuecomment-609644282)
|
||||
* **ngcc:** allow ngcc configuration to match pre-release versions of packages ([#36370](https://github.com/angular/angular/issues/36370)) ([326240e](https://github.com/angular/angular/commit/326240e))
|
||||
* **ngcc:** correctly detect imported TypeScript helpers ([#36284](https://github.com/angular/angular/issues/36284)) ([ca25c95](https://github.com/angular/angular/commit/ca25c95)), closes [#36089](https://github.com/angular/angular/issues/36089)
|
||||
* **ngcc:** correctly identify relative Windows-style import paths ([#36372](https://github.com/angular/angular/issues/36372)) ([aecf9de](https://github.com/angular/angular/commit/aecf9de))
|
||||
* **ngcc:** correctly identify the package path of secondary entry-points ([#36249](https://github.com/angular/angular/issues/36249)) ([995cd15](https://github.com/angular/angular/commit/995cd15)), closes [#35747](https://github.com/angular/angular/issues/35747)
|
||||
* **ngcc:** detect non-emitted, non-imported TypeScript helpers ([#36418](https://github.com/angular/angular/issues/36418)) ([5fa7b8b](https://github.com/angular/angular/commit/5fa7b8b))
|
||||
* **ngcc:** do not spawn more processes than intended in parallel mode ([#36280](https://github.com/angular/angular/issues/36280)) ([5cee709](https://github.com/angular/angular/commit/5cee709)), closes [#35719](https://github.com/angular/angular/issues/35719) [#36278](https://github.com/angular/angular/issues/36278) [/github.com/angular/angular/blob/b8e9a30d3b6/packages/compiler-cli/ngcc/src/main.ts#L429](https://github.com//github.com/angular/angular/blob/b8e9a30d3b6/packages/compiler-cli/ngcc/src/main.ts/issues/L429) [/github.com/angular/angular/blob/b8e9a30d3b6/packages/compiler-cli/ngcc/src/execution/cluster/master.ts#L108](https://github.com//github.com/angular/angular/blob/b8e9a30d3b6/packages/compiler-cli/ngcc/src/execution/cluster/master.ts/issues/L108) [/github.com/angular/angular/blob/b8e9a30d3b6/packages/compiler-cli/ngcc/src/execution/cluster/master.ts#L110](https://github.com//github.com/angular/angular/blob/b8e9a30d3b6/packages/compiler-cli/ngcc/src/execution/cluster/master.ts/issues/L110) [/github.com/angular/angular/blob/b8e9a30d3b6/packages/compiler-cli/ngcc/src/execution/cluster/master.ts#L199](https://github.com//github.com/angular/angular/blob/b8e9a30d3b6/packages/compiler-cli/ngcc/src/execution/cluster/master.ts/issues/L199)
|
||||
* **ngcc:** do not write entry-point manifest outside node_modules ([#36299](https://github.com/angular/angular/issues/36299)) ([c6dd900](https://github.com/angular/angular/commit/c6dd900)), closes [#36296](https://github.com/angular/angular/issues/36296)
|
||||
* **ngcc:** don't crash on cyclic source-map references ([#36452](https://github.com/angular/angular/issues/36452)) ([ee70a18](https://github.com/angular/angular/commit/ee70a18)), closes [#35727](https://github.com/angular/angular/issues/35727) [#35757](https://github.com/angular/angular/issues/35757)
|
||||
* **ngcc:** handle bad path mappings when finding entry-points ([#36331](https://github.com/angular/angular/issues/36331)) ([cc4b813](https://github.com/angular/angular/commit/cc4b813)), closes [#36313](https://github.com/angular/angular/issues/36313) [#36283](https://github.com/angular/angular/issues/36283)
|
||||
* **ngcc:** handle entry-points within container folders ([#36305](https://github.com/angular/angular/issues/36305)) ([38ad1d9](https://github.com/angular/angular/commit/38ad1d9)), closes [#35756](https://github.com/angular/angular/issues/35756) [#36216](https://github.com/angular/angular/issues/36216)
|
||||
* **ngcc:** sniff `main` property for ESM5 format ([#36396](https://github.com/angular/angular/issues/36396)) ([2463548](https://github.com/angular/angular/commit/2463548)), closes [#35788](https://github.com/angular/angular/issues/35788)
|
||||
* **ngcc:** support ignoring deep-imports via package config ([#36423](https://github.com/angular/angular/issues/36423)) ([f9fb833](https://github.com/angular/angular/commit/f9fb833)), closes [#35750](https://github.com/angular/angular/issues/35750)
|
||||
* **ngcc:** support simple `browser` property in entry-points ([#36396](https://github.com/angular/angular/issues/36396)) ([6b3aa60](https://github.com/angular/angular/commit/6b3aa60)), closes [#36062](https://github.com/angular/angular/issues/36062)
|
||||
* **ngcc:** use path-mappings from tsconfig in dependency resolution ([#36180](https://github.com/angular/angular/issues/36180)) ([380de1e](https://github.com/angular/angular/commit/380de1e)), closes [#36119](https://github.com/angular/angular/issues/36119)
|
||||
* **ngcc:** use preserve whitespaces from tsconfig if provided ([#36189](https://github.com/angular/angular/issues/36189)) ([b8e9a30](https://github.com/angular/angular/commit/b8e9a30)), closes [#35871](https://github.com/angular/angular/issues/35871)
|
||||
* **platform-server:** update `xhr2` dependency ([#36366](https://github.com/angular/angular/issues/36366)) ([b59bc0e](https://github.com/angular/angular/commit/b59bc0e)), closes [#36358](https://github.com/angular/angular/issues/36358)
|
||||
* **router:** allow UrlMatcher to return null ([#36402](https://github.com/angular/angular/issues/36402)) ([568e9df](https://github.com/angular/angular/commit/568e9df)), closes [#29824](https://github.com/angular/angular/issues/29824)
|
||||
* **router:** state data missing in routerLink ([#36462](https://github.com/angular/angular/issues/36462)) ([e0415db](https://github.com/angular/angular/commit/e0415db)), closes [#33173](https://github.com/angular/angular/issues/33173)
|
||||
* **service-worker:** by default register the SW after 30s even the app never stabilizes ([#35870](https://github.com/angular/angular/issues/35870)) ([29e8a64](https://github.com/angular/angular/commit/29e8a64)), closes [#34464](https://github.com/angular/angular/issues/34464)
|
||||
* **service-worker:** prevent SW registration strategies from affecting app stabilization ([#35870](https://github.com/angular/angular/issues/35870)) ([2d7c95f](https://github.com/angular/angular/commit/2d7c95f))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **compiler:** add dependency info and ng-content selectors to metadata ([#35695](https://github.com/angular/angular/issues/35695)) ([32ce8b1](https://github.com/angular/angular/commit/32ce8b1))
|
||||
* **compiler:** Propagate value span of ExpressionBinding to ParsedProperty ([#36133](https://github.com/angular/angular/issues/36133)) ([d714b95](https://github.com/angular/angular/commit/d714b95))
|
||||
* **core:** undecorated-classes migration should handle derived abstract classes ([#35339](https://github.com/angular/angular/issues/35339)) ([c24ad56](https://github.com/angular/angular/commit/c24ad56))
|
||||
* **service-worker:** support timeout in `registerWhenStable` SW registration strategy ([#35870](https://github.com/angular/angular/issues/35870)) ([00efacf](https://github.com/angular/angular/commit/00efacf)), closes [#34464](https://github.com/angular/angular/issues/34464)
|
||||
|
||||
|
||||
### BREAKING CHANGES
|
||||
|
||||
* **router:** UrlMatcher's type now reflects that it could always return
|
||||
null.
|
||||
|
||||
If you implemented your own Router or Recognizer class, please update it to
|
||||
handle matcher returning null.
|
||||
|
||||
|
||||
|
||||
<a name="9.1.1"></a>
|
||||
## [9.1.1](https://github.com/angular/angular/compare/9.1.0...9.1.1) (2020-04-07)
|
||||
|
||||
@ -32,7 +99,6 @@
|
||||
* **router:** state data missing in routerLink ([#36462](https://github.com/angular/angular/issues/36462)) ([0e7a89a](https://github.com/angular/angular/commit/0e7a89a)), closes [#33173](https://github.com/angular/angular/issues/33173)
|
||||
|
||||
|
||||
|
||||
<a name="9.1.0"></a>
|
||||
# [9.1.0](https://github.com/angular/angular/compare/9.0.0...9.1.0) (2020-03-25)
|
||||
|
||||
|
@ -99,7 +99,7 @@ Project-specific [TypeScript](https://www.typescriptlang.org/) configuration fil
|
||||
|
||||
| APPLICATION-SPECIFIC CONFIG FILES | PURPOSE |
|
||||
| :--------------------- | :------------------------------------------|
|
||||
| `browserslist` | Configures sharing of target browsers and Node.js versions among various front-end tools. See [Browserslist on GitHub](https://github.com/browserslist/browserslist) for more information. |
|
||||
| `.browserslistrc` | Configures sharing of target browsers and Node.js versions among various front-end tools. See [Browserslist on GitHub](https://github.com/browserslist/browserslist) for more information. |
|
||||
| `karma.conf.js` | Application-specific [Karma](https://karma-runner.github.io/2.0/config/configuration-file.html) configuration. |
|
||||
| `tsconfig.app.json` | Application-specific [TypeScript](https://www.typescriptlang.org/) configuration, including TypeScript and Angular template compiler options. See [TypeScript Configuration](guide/typescript-configuration) and [Angular Compiler Options](guide/angular-compiler-options). |
|
||||
| `tsconfig.spec.json` | [TypeScript](https://www.typescriptlang.org/) configuration for the application tests. See [TypeScript Configuration](guide/typescript-configuration). |
|
||||
|
@ -101,7 +101,8 @@ The following table provides the status for Angular versions under support.
|
||||
|
||||
Version | Status | Released | Active Ends | LTS Ends
|
||||
------- | ------ | ------------ | ------------ | ------------
|
||||
^8.0.0 | Active | May 28, 2019 | Nov 28, 2019 | Nov 28, 2020
|
||||
^9.0.0 | Active | Feb 06, 2020 | Aug 06, 2020 | Aug 06, 2021
|
||||
^8.0.0 | LTS | May 28, 2019 | Nov 28, 2019 | Nov 28, 2020
|
||||
^7.0.0 | LTS | Oct 18, 2018 | Apr 18, 2019 | Apr 18, 2020
|
||||
|
||||
Angular versions ^4.0.0, ^5.0.0 and ^6.0.0 are no longer under support.
|
||||
|
@ -53,7 +53,7 @@ The initial `tsconfig.json` for an Angular app typically looks like the followin
|
||||
]
|
||||
},
|
||||
"angularCompilerOptions": {
|
||||
"fullTemplateTypeCheck": true,
|
||||
"strictTemplates": true,
|
||||
"strictInjectionParameters": true
|
||||
}
|
||||
}
|
||||
@ -62,7 +62,6 @@ The initial `tsconfig.json` for an Angular app typically looks like the followin
|
||||
|
||||
{@a noImplicitAny}
|
||||
|
||||
|
||||
### *noImplicitAny* and *suppressImplicitAnyIndexErrors*
|
||||
|
||||
TypeScript developers disagree about whether the `noImplicitAny` flag should be `true` or `false`.
|
||||
@ -96,6 +95,7 @@ For more information about how the TypeScript configuration affects compilation,
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
{@a typings}
|
||||
|
||||
## TypeScript typings
|
||||
@ -146,7 +146,6 @@ For instance, to install typings for `jasmine` you run `npm install @types/jasmi
|
||||
|
||||
{@a target}
|
||||
|
||||
|
||||
### *target*
|
||||
|
||||
By default, the target is `es2015`, which is supported only in modern browsers. You can configure the target to `es5` to specifically support legacy browsers. [Differential loading](guide/deployment#differential-loading) is also provided by the Angular CLI to support modern, and legacy browsers with separate bundles.
|
||||
|
@ -23,7 +23,7 @@
|
||||
"build-local-with-viewengine": "yarn ~~build",
|
||||
"prebuild-local-with-viewengine-ci": "node scripts/switch-to-viewengine && yarn setup-local-ci",
|
||||
"build-local-with-viewengine-ci": "yarn ~~build --progress=false",
|
||||
"extract-cli-command-docs": "node tools/transforms/cli-docs-package/extract-cli-commands.js 526c3cc37",
|
||||
"extract-cli-command-docs": "node tools/transforms/cli-docs-package/extract-cli-commands.js 56c648827",
|
||||
"lint": "yarn check-env && yarn docs-lint && ng lint && yarn example-lint && yarn tools-lint",
|
||||
"test": "yarn check-env && ng test",
|
||||
"pree2e": "yarn check-env && yarn update-webdriver",
|
||||
|
@ -37,7 +37,6 @@
|
||||
],
|
||||
"angularCompilerOptions": {
|
||||
"disableTypeScriptVersionCheck": true,
|
||||
"fullTemplateTypeCheck": true,
|
||||
"strictInjectionParameters": true,
|
||||
"strictTemplates": true
|
||||
}
|
||||
|
@ -20,8 +20,17 @@ export type Golden = CircularDependency[];
|
||||
* the source file objects are mapped to their relative file names.
|
||||
*/
|
||||
export function convertReferenceChainToGolden(refs: ReferenceChain[], baseDir: string): Golden {
|
||||
return refs.map(
|
||||
chain => chain.map(({fileName}) => convertPathToForwardSlash(relative(baseDir, fileName))));
|
||||
return refs
|
||||
.map(
|
||||
// Normalize cycles as the paths can vary based on which node in the cycle is visited
|
||||
// first in the analyzer. The paths represent cycles. Hence we can shift nodes in a
|
||||
// deterministic way so that the goldens don't change unnecessarily and cycle comparison
|
||||
// is simpler.
|
||||
chain => normalizeCircularDependency(
|
||||
chain.map(({fileName}) => convertPathToForwardSlash(relative(baseDir, fileName)))))
|
||||
// Sort cycles so that the golden doesn't change unnecessarily when cycles are detected
|
||||
// in different order (e.g. new imports cause cycles to be detected earlier or later).
|
||||
.sort(compareCircularDependency);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -44,6 +53,53 @@ export function compareGoldens(actual: Golden, expected: Golden) {
|
||||
return {newCircularDeps, fixedCircularDeps};
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalizes the a circular dependency by ensuring that the path starts with the first
|
||||
* node in alphabetical order. Since the path array represents a cycle, we can make a
|
||||
* specific node the first element in the path that represents the cycle.
|
||||
*
|
||||
* This method is helpful because the path of circular dependencies changes based on which
|
||||
* file in the path has been visited first by the analyzer. e.g. Assume we have a circular
|
||||
* dependency represented as: `A -> B -> C`. The analyzer will detect this cycle when it
|
||||
* visits `A`. Though when a source file that is analyzed before `A` starts importing `B`,
|
||||
* the cycle path will detected as `B -> C -> A`. This represents the same cycle, but is just
|
||||
* different due to a limitation of using a data structure that can be written to a text-based
|
||||
* golden file.
|
||||
*
|
||||
* To account for this non-deterministic behavior in goldens, we shift the circular
|
||||
* dependency path to the first node based on alphabetical order. e.g. `A` will always
|
||||
* be the first node in the path that represents the cycle.
|
||||
*/
|
||||
function normalizeCircularDependency(path: CircularDependency): CircularDependency {
|
||||
if (path.length <= 1) {
|
||||
return path;
|
||||
}
|
||||
|
||||
let indexFirstNode: number = 0;
|
||||
let valueFirstNode: string = path[0];
|
||||
|
||||
// Find a node in the cycle path that precedes all other elements
|
||||
// in terms of alphabetical order.
|
||||
for (let i = 1; i < path.length; i++) {
|
||||
const value = path[i];
|
||||
if (value.localeCompare(valueFirstNode, 'en') < 0) {
|
||||
indexFirstNode = i;
|
||||
valueFirstNode = value;
|
||||
}
|
||||
}
|
||||
|
||||
// If the alphabetically first node is already at start of the path, just
|
||||
// return the actual path as no changes need to be made.
|
||||
if (indexFirstNode === 0) {
|
||||
return path;
|
||||
}
|
||||
|
||||
// Move the determined first node (as of alphabetical order) to the start of a new
|
||||
// path array. The nodes before the first node in the old path are then concatenated
|
||||
// to the end of the new path. This is possible because the path represents a cycle.
|
||||
return [...path.slice(indexFirstNode), ...path.slice(0, indexFirstNode)];
|
||||
}
|
||||
|
||||
/** Checks whether the specified circular dependencies are equal. */
|
||||
function isSameCircularDependency(actual: CircularDependency, expected: CircularDependency) {
|
||||
if (actual.length !== expected.length) {
|
||||
@ -56,3 +112,20 @@ function isSameCircularDependency(actual: CircularDependency, expected: Circular
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares two circular dependencies by respecting the alphabetic order of nodes in the
|
||||
* cycle paths. The first nodes which don't match in both paths are decisive on the order.
|
||||
*/
|
||||
function compareCircularDependency(a: CircularDependency, b: CircularDependency): number {
|
||||
// Go through nodes in both cycle paths and determine whether `a` should be ordered
|
||||
// before `b`. The first nodes which don't match decide on the order.
|
||||
for (let i = 0; i < Math.min(a.length, b.length); i++) {
|
||||
const compareValue = a[i].localeCompare(b[i], 'en');
|
||||
if (compareValue !== 0) {
|
||||
return compareValue;
|
||||
}
|
||||
}
|
||||
// If all nodes are equal in the cycles, the order is based on the length of both cycles.
|
||||
return a.length - b.length;
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
2
goldens/public-api/router/router.d.ts
vendored
2
goldens/public-api/router/router.d.ts
vendored
@ -500,7 +500,7 @@ export declare abstract class UrlHandlingStrategy {
|
||||
abstract shouldProcessUrl(url: UrlTree): boolean;
|
||||
}
|
||||
|
||||
export declare type UrlMatcher = (segments: UrlSegment[], group: UrlSegmentGroup, route: Route) => UrlMatchResult;
|
||||
export declare type UrlMatcher = (segments: UrlSegment[], group: UrlSegmentGroup, route: Route) => UrlMatchResult | null;
|
||||
|
||||
export declare type UrlMatchResult = {
|
||||
consumed: UrlSegment[];
|
||||
|
10
package.json
10
package.json
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "angular-srcs",
|
||||
"version": "9.1.1",
|
||||
"version": "10.0.0-next.1",
|
||||
"private": true,
|
||||
"description": "Angular - a web framework for modern web apps",
|
||||
"homepage": "https://github.com/angular/angular",
|
||||
@ -72,8 +72,8 @@
|
||||
"@types/fs-extra": "4.0.2",
|
||||
"@types/hammerjs": "2.0.35",
|
||||
"@types/inquirer": "^0.0.44",
|
||||
"@types/jasmine": "^2.8.8",
|
||||
"@types/jasminewd2": "^2.0.6",
|
||||
"@types/jasmine": "3.5.10",
|
||||
"@types/jasminewd2": "^2.0.8",
|
||||
"@types/minimist": "^1.2.0",
|
||||
"@types/node": "^12.11.1",
|
||||
"@types/selenium-webdriver": "3.0.7",
|
||||
@ -105,8 +105,8 @@
|
||||
"hammerjs": "2.0.8",
|
||||
"http-server": "^0.11.1",
|
||||
"incremental-dom": "0.4.1",
|
||||
"jasmine": "^3.1.0",
|
||||
"jasmine-core": "^3.1.0",
|
||||
"jasmine": "^3.5.0",
|
||||
"jasmine-core": "^3.5.0",
|
||||
"jquery": "3.0.0",
|
||||
"karma": "~4.1.0",
|
||||
"karma-chrome-launcher": "^2.2.0",
|
||||
|
@ -29,8 +29,8 @@ const XSSI_PREFIX = ')]}\'\n';
|
||||
|
||||
{
|
||||
describe('XhrBackend', () => {
|
||||
let factory: MockXhrFactory = null !;
|
||||
let backend: HttpXhrBackend = null !;
|
||||
let factory: MockXhrFactory = null!;
|
||||
let backend: HttpXhrBackend = null!;
|
||||
beforeEach(() => {
|
||||
factory = new MockXhrFactory();
|
||||
backend = new HttpXhrBackend(factory);
|
||||
@ -92,7 +92,7 @@ const XSSI_PREFIX = ')]}\'\n';
|
||||
factory.mock.mockFlush(200, 'OK', JSON.stringify({data: 'some data'}));
|
||||
expect(events.length).toBe(2);
|
||||
const res = events[1] as HttpResponse<{data: string}>;
|
||||
expect(res.body !.data).toBe('some data');
|
||||
expect(res.body!.data).toBe('some data');
|
||||
});
|
||||
it('handles a blank json response', () => {
|
||||
const events = trackEvents(backend.handle(TEST_POST.clone({responseType: 'json'})));
|
||||
@ -106,14 +106,14 @@ const XSSI_PREFIX = ')]}\'\n';
|
||||
factory.mock.mockFlush(500, 'Error', JSON.stringify({data: 'some data'}));
|
||||
expect(events.length).toBe(2);
|
||||
const res = events[1] as any as HttpErrorResponse;
|
||||
expect(res.error !.data).toBe('some data');
|
||||
expect(res.error!.data).toBe('some data');
|
||||
});
|
||||
it('handles a json error response with XSSI prefix', () => {
|
||||
const events = trackEvents(backend.handle(TEST_POST.clone({responseType: 'json'})));
|
||||
factory.mock.mockFlush(500, 'Error', XSSI_PREFIX + JSON.stringify({data: 'some data'}));
|
||||
expect(events.length).toBe(2);
|
||||
const res = events[1] as any as HttpErrorResponse;
|
||||
expect(res.error !.data).toBe('some data');
|
||||
expect(res.error!.data).toBe('some data');
|
||||
});
|
||||
it('handles a json string response', () => {
|
||||
const events = trackEvents(backend.handle(TEST_POST.clone({responseType: 'json'})));
|
||||
@ -128,7 +128,7 @@ const XSSI_PREFIX = ')]}\'\n';
|
||||
factory.mock.mockFlush(200, 'OK', XSSI_PREFIX + JSON.stringify({data: 'some data'}));
|
||||
expect(events.length).toBe(2);
|
||||
const res = events[1] as HttpResponse<{data: string}>;
|
||||
expect(res.body !.data).toBe('some data');
|
||||
expect(res.body!.data).toBe('some data');
|
||||
});
|
||||
it('emits unsuccessful responses via the error path', done => {
|
||||
backend.handle(TEST_POST).subscribe(undefined, (err: HttpErrorResponse) => {
|
||||
@ -141,7 +141,7 @@ const XSSI_PREFIX = ')]}\'\n';
|
||||
it('emits real errors via the error path', done => {
|
||||
backend.handle(TEST_POST).subscribe(undefined, (err: HttpErrorResponse) => {
|
||||
expect(err instanceof HttpErrorResponse).toBe(true);
|
||||
expect(err.error instanceof Error);
|
||||
expect(err.error instanceof Error).toBeTrue();
|
||||
expect(err.url).toBe('/test');
|
||||
done();
|
||||
});
|
||||
|
@ -95,7 +95,7 @@ runInEachFileSystem(() => {
|
||||
});
|
||||
// The "test" compilation result is just the name of the decorator being compiled
|
||||
// (suffixed with `(compiled)`)
|
||||
handler.compile.and.callFake((decl: ts.Declaration, analysis: any) => {
|
||||
(handler.compile as any).and.callFake((decl: ts.Declaration, analysis: any) => {
|
||||
logs.push(`compile: ${(decl as any).name.text}@${analysis.decoratorName} (resolved: ${
|
||||
analysis.resolved})`);
|
||||
return `@${analysis.decoratorName} (compiled)`;
|
||||
@ -183,7 +183,7 @@ runInEachFileSystem(() => {
|
||||
it('should call detect on the decorator handlers with each class from the parsed file',
|
||||
() => {
|
||||
expect(testHandler.detect).toHaveBeenCalledTimes(5);
|
||||
expect(testHandler.detect.calls.allArgs().map(args => args[1])).toEqual([
|
||||
expect(testHandler.detect.calls.allArgs().map((args: any[]) => args[1])).toEqual([
|
||||
null,
|
||||
jasmine.arrayContaining([jasmine.objectContaining({name: 'Component'})]),
|
||||
jasmine.arrayContaining([jasmine.objectContaining({name: 'Directive'})]),
|
||||
|
@ -34,9 +34,9 @@ describe('ClusterExecutor', () => {
|
||||
|
||||
beforeEach(() => {
|
||||
masterRunSpy = spyOn(ClusterMaster.prototype, 'run')
|
||||
.and.returnValue(Promise.resolve('CusterMaster#run()'));
|
||||
.and.returnValue(Promise.resolve('CusterMaster#run()' as any));
|
||||
workerRunSpy = spyOn(ClusterWorker.prototype, 'run')
|
||||
.and.returnValue(Promise.resolve('CusterWorker#run()'));
|
||||
.and.returnValue(Promise.resolve('CusterWorker#run()' as any));
|
||||
createTaskCompletedCallback = jasmine.createSpy('createTaskCompletedCallback');
|
||||
|
||||
mockLogger = new MockLogger();
|
||||
|
@ -38,7 +38,7 @@ runInEachFileSystem(() => {
|
||||
initMockFileSystem(fs, testFiles);
|
||||
|
||||
// Force single-process execution in unit tests by mocking available CPUs to 1.
|
||||
spyOn(os, 'cpus').and.returnValue([{model: 'Mock CPU'}]);
|
||||
spyOn(os, 'cpus').and.returnValue([{model: 'Mock CPU'} as any]);
|
||||
});
|
||||
|
||||
it('should run ngcc without errors for esm2015', () => {
|
||||
@ -962,7 +962,8 @@ runInEachFileSystem(() => {
|
||||
.toMatch(ANGULAR_CORE_IMPORT_REGEX);
|
||||
|
||||
// Copies over files (unchanged) that did not need compiling
|
||||
expect(fs.exists(_(`/node_modules/@angular/common/__ivy_ngcc__/esm5/src/version.js`)));
|
||||
expect(fs.exists(_(`/node_modules/@angular/common/__ivy_ngcc__/esm5/src/version.js`)))
|
||||
.toBeTrue();
|
||||
expect(fs.readFile(_(`/node_modules/@angular/common/__ivy_ngcc__/esm5/src/version.js`)))
|
||||
.toEqual(fs.readFile(_(`/node_modules/@angular/common/esm5/src/version.js`)));
|
||||
|
||||
|
@ -66,6 +66,9 @@ runInEachFileSystem(() => {
|
||||
});
|
||||
spyOn(lockFile, 'read').and.callFake(() => {
|
||||
log.push('read() => ' + lockFileContents);
|
||||
if (lockFileContents === null) {
|
||||
throw {code: 'ENOENT'};
|
||||
}
|
||||
return lockFileContents;
|
||||
});
|
||||
|
||||
@ -99,6 +102,9 @@ runInEachFileSystem(() => {
|
||||
});
|
||||
spyOn(lockFile, 'read').and.callFake(() => {
|
||||
log.push('read() => ' + lockFileContents);
|
||||
if (lockFileContents === null) {
|
||||
throw {code: 'ENOENT'};
|
||||
}
|
||||
return lockFileContents;
|
||||
});
|
||||
|
||||
@ -148,6 +154,9 @@ runInEachFileSystem(() => {
|
||||
});
|
||||
spyOn(lockFile, 'read').and.callFake(() => {
|
||||
log.push('read() => ' + lockFileContents);
|
||||
if (lockFileContents === null) {
|
||||
throw {code: 'ENOENT'};
|
||||
}
|
||||
return lockFileContents;
|
||||
});
|
||||
|
||||
@ -167,4 +176,4 @@ runInEachFileSystem(() => {
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -12,6 +12,8 @@ describe('unlocker', () => {
|
||||
it('should attach a handler to the `disconnect` event', () => {
|
||||
spyOn(process, 'on');
|
||||
require('../../../src/locking/lock_file_with_child_process/unlocker');
|
||||
expect(process.on).toHaveBeenCalledWith('disconnect', jasmine.any(Function));
|
||||
// TODO: @JiaLiPassion, need to wait for @types/jasmine to handle the override case
|
||||
// https://github.com/DefinitelyTyped/DefinitelyTyped/issues/42455
|
||||
expect(process.on).toHaveBeenCalledWith('disconnect' as any, jasmine.any(Function));
|
||||
});
|
||||
});
|
||||
|
@ -70,7 +70,7 @@ runInEachFileSystem(() => {
|
||||
const config = new NgccConfiguration(fs, _('/project'));
|
||||
spyOn(config, 'getConfig').and.returnValue({
|
||||
entryPoints: {[_('/project/node_modules/some_package/valid_entry_point')]: {ignore: true}}
|
||||
});
|
||||
} as any);
|
||||
const entryPoint = getEntryPointInfo(
|
||||
fs, config, new MockLogger(), SOME_PACKAGE,
|
||||
_('/project/node_modules/some_package/valid_entry_point'));
|
||||
@ -95,7 +95,8 @@ runInEachFileSystem(() => {
|
||||
esm2015: './some_other.js',
|
||||
};
|
||||
spyOn(config, 'getConfig').and.returnValue({
|
||||
entryPoints: {[_('/project/node_modules/some_package/valid_entry_point')]: {override}}
|
||||
entryPoints: {[_('/project/node_modules/some_package/valid_entry_point')]: {override}},
|
||||
versionRange: '*'
|
||||
});
|
||||
const entryPoint = getEntryPointInfo(
|
||||
fs, config, new MockLogger(), SOME_PACKAGE,
|
||||
@ -145,7 +146,9 @@ runInEachFileSystem(() => {
|
||||
const override =
|
||||
JSON.parse(createPackageJson('missing_package_json', {excludes: ['name']}));
|
||||
spyOn(config, 'getConfig').and.returnValue({
|
||||
entryPoints: {[_('/project/node_modules/some_package/missing_package_json')]: {override}}
|
||||
entryPoints:
|
||||
{[_('/project/node_modules/some_package/missing_package_json')]: {override}},
|
||||
versionRange: '*'
|
||||
});
|
||||
const entryPoint = getEntryPointInfo(
|
||||
fs, config, new MockLogger(), SOME_PACKAGE,
|
||||
@ -279,7 +282,8 @@ runInEachFileSystem(() => {
|
||||
]);
|
||||
const config = new NgccConfiguration(fs, _('/project'));
|
||||
spyOn(config, 'getConfig').and.returnValue({
|
||||
entryPoints: {[_('/project/node_modules/some_package/missing_metadata')]: {}}
|
||||
entryPoints: {[_('/project/node_modules/some_package/missing_metadata')]: {}},
|
||||
versionRange: '*'
|
||||
});
|
||||
const entryPoint = getEntryPointInfo(
|
||||
fs, config, new MockLogger(), SOME_PACKAGE,
|
||||
@ -398,7 +402,7 @@ runInEachFileSystem(() => {
|
||||
if (result === NO_ENTRY_POINT || result === INCOMPATIBLE_ENTRY_POINT) {
|
||||
return fail(`Expected an entry point but got ${result}`);
|
||||
}
|
||||
entryPoint = result;
|
||||
entryPoint = result as any;
|
||||
});
|
||||
|
||||
it('should return `esm2015` format for `fesm2015` property', () => {
|
||||
|
@ -47,7 +47,7 @@ describe('CachedFileSystem', () => {
|
||||
let lstatSpy: jasmine.Spy;
|
||||
beforeEach(() => {
|
||||
// For most of the tests the files are not symbolic links.
|
||||
lstatSpy = spyOn(delegate, 'lstat').and.returnValue({isSymbolicLink: () => false});
|
||||
lstatSpy = spyOn(delegate, 'lstat').and.returnValue({isSymbolicLink: () => false} as any);
|
||||
});
|
||||
|
||||
it('should call delegate if not in cache', () => {
|
||||
@ -93,7 +93,7 @@ describe('CachedFileSystem', () => {
|
||||
describe('invalidateCaches()', () => {
|
||||
it('should call the delegate `readFile()` if the path for the cached file has been invalidated',
|
||||
() => {
|
||||
spyOn(delegate, 'lstat').and.returnValue({isSymbolicLink: () => false});
|
||||
spyOn(delegate, 'lstat').and.returnValue({isSymbolicLink: () => false} as any);
|
||||
const spy = spyOn(delegate, 'readFile').and.returnValue('Some contents');
|
||||
fs.readFile(abcPath); // Call once to fill the cache
|
||||
spy.calls.reset();
|
||||
@ -230,7 +230,7 @@ describe('CachedFileSystem', () => {
|
||||
describe('moveFile()', () => {
|
||||
beforeEach(() => {
|
||||
// `moveFile()` relies upon `readFile` which calls through to `lstat()`, so stub it out.
|
||||
spyOn(delegate, 'lstat').and.returnValue({isSymbolicLink: () => false});
|
||||
spyOn(delegate, 'lstat').and.returnValue({isSymbolicLink: () => false} as any);
|
||||
});
|
||||
|
||||
it('should call delegate', () => {
|
||||
|
@ -65,10 +65,12 @@ describe('NodeJSFileSystem', () => {
|
||||
|
||||
describe('readdir()', () => {
|
||||
it('should delegate to fs.readdirSync()', () => {
|
||||
const spy = spyOn(realFs, 'readdirSync').and.returnValue(['x', 'y/z']);
|
||||
const spy = spyOn(realFs, 'readdirSync').and.returnValue(['x', 'y/z'] as any);
|
||||
const result = fs.readdir(abcPath);
|
||||
expect(result).toEqual([relativeFrom('x'), relativeFrom('y/z')]);
|
||||
expect(spy).toHaveBeenCalledWith(abcPath);
|
||||
// TODO: @JiaLiPassion need to wait for @types/jasmine update to handle optional parameters.
|
||||
// https://github.com/DefinitelyTyped/DefinitelyTyped/issues/43486
|
||||
expect(spy as any).toHaveBeenCalledWith(abcPath);
|
||||
});
|
||||
});
|
||||
|
||||
@ -88,7 +90,9 @@ describe('NodeJSFileSystem', () => {
|
||||
const spy = spyOn(realFs, 'statSync').and.returnValue(stats);
|
||||
const result = fs.stat(abcPath);
|
||||
expect(result).toBe(stats);
|
||||
expect(spy).toHaveBeenCalledWith(abcPath);
|
||||
// TODO: @JiaLiPassion need to wait for @types/jasmine update to handle optional parameters.
|
||||
// https://github.com/DefinitelyTyped/DefinitelyTyped/issues/43486
|
||||
expect(spy as any).toHaveBeenCalledWith(abcPath);
|
||||
});
|
||||
});
|
||||
|
||||
@ -125,7 +129,7 @@ describe('NodeJSFileSystem', () => {
|
||||
const xyPath = absoluteFrom('/x/y');
|
||||
const mkdirCalls: string[] = [];
|
||||
const existsCalls: string[] = [];
|
||||
spyOn(realFs, 'mkdirSync').and.callFake((path: string) => mkdirCalls.push(path));
|
||||
spyOn(realFs, 'mkdirSync').and.callFake(((path: string) => mkdirCalls.push(path)) as any);
|
||||
spyOn(fs, 'exists').and.callFake((path: AbsoluteFsPath) => {
|
||||
existsCalls.push(path);
|
||||
switch (path) {
|
||||
@ -171,12 +175,14 @@ describe('NodeJSFileSystem', () => {
|
||||
}
|
||||
return false;
|
||||
});
|
||||
spyOn(fs, 'stat').and.returnValue({isDirectory: () => true});
|
||||
const mkdirSyncSpy = spyOn(realFs, 'mkdirSync').and.callFake((path: string) => {
|
||||
if (path === abcPath) {
|
||||
throw new Error('It exists already. Supposedly.');
|
||||
}
|
||||
});
|
||||
spyOn(fs, 'stat').and.returnValue({isDirectory: () => true} as any);
|
||||
const mkdirSyncSpy =
|
||||
spyOn(realFs, 'mkdirSync').and.callFake(((path: string) => {
|
||||
if (path === abcPath) {
|
||||
throw new Error(
|
||||
'It exists already. Supposedly.');
|
||||
}
|
||||
}) as any);
|
||||
|
||||
fs.ensureDir(abcPath);
|
||||
expect(mkdirSyncSpy).toHaveBeenCalledTimes(3);
|
||||
@ -186,11 +192,12 @@ describe('NodeJSFileSystem', () => {
|
||||
|
||||
it('should fail if creating the directory throws and the directory does not exist', () => {
|
||||
spyOn(fs, 'exists').and.returnValue(false);
|
||||
spyOn(realFs, 'mkdirSync').and.callFake((path: string) => {
|
||||
if (path === abcPath) {
|
||||
throw new Error('Unable to create directory (for whatever reason).');
|
||||
}
|
||||
});
|
||||
spyOn(realFs, 'mkdirSync')
|
||||
.and.callFake(((path: string) => {
|
||||
if (path === abcPath) {
|
||||
throw new Error('Unable to create directory (for whatever reason).');
|
||||
}
|
||||
}) as any);
|
||||
|
||||
expect(() => fs.ensureDir(abcPath))
|
||||
.toThrowError('Unable to create directory (for whatever reason).');
|
||||
@ -210,12 +217,12 @@ describe('NodeJSFileSystem', () => {
|
||||
}
|
||||
return false;
|
||||
});
|
||||
spyOn(fs, 'stat').and.returnValue({isDirectory: isDirectorySpy});
|
||||
spyOn(realFs, 'mkdirSync').and.callFake((path: string) => {
|
||||
if (path === abcPath) {
|
||||
throw new Error('It exists already. Supposedly.');
|
||||
}
|
||||
});
|
||||
spyOn(fs, 'stat').and.returnValue({isDirectory: isDirectorySpy} as any);
|
||||
spyOn(realFs, 'mkdirSync').and.callFake(((path: string) => {
|
||||
if (path === abcPath) {
|
||||
throw new Error('It exists already. Supposedly.');
|
||||
}
|
||||
}) as any);
|
||||
|
||||
expect(() => fs.ensureDir(abcPath)).toThrowError('It exists already. Supposedly.');
|
||||
expect(isDirectorySpy).toHaveBeenCalledTimes(1);
|
||||
|
@ -775,18 +775,22 @@ runInEachFileSystem(() => {
|
||||
|
||||
describe('(visited file tracking)', () => {
|
||||
it('should track each time a source file is visited', () => {
|
||||
const addDependency = jasmine.createSpy('DependencyTracker');
|
||||
const addDependency =
|
||||
jasmine.createSpy<DependencyTracker['addDependency']>('DependencyTracker');
|
||||
const {expression, checker} = makeExpression(
|
||||
`class A { static foo = 42; } function bar() { return A.foo; }`, 'bar()');
|
||||
const evaluator = makeEvaluator(checker, {...fakeDepTracker, addDependency});
|
||||
evaluator.evaluate(expression);
|
||||
expect(addDependency).toHaveBeenCalledTimes(2); // two declaration visited
|
||||
expect(addDependency.calls.allArgs().map(args => [args[0].fileName, args[1].fileName]))
|
||||
expect(
|
||||
addDependency.calls.allArgs().map(
|
||||
(args: Parameters<typeof addDependency>) => [args[0].fileName, args[1].fileName]))
|
||||
.toEqual([[_('/entry.ts'), _('/entry.ts')], [_('/entry.ts'), _('/entry.ts')]]);
|
||||
});
|
||||
|
||||
it('should track imported source files', () => {
|
||||
const addDependency = jasmine.createSpy('DependencyTracker');
|
||||
const addDependency =
|
||||
jasmine.createSpy<DependencyTracker['addDependency']>('DependencyTracker');
|
||||
const {expression, checker} =
|
||||
makeExpression(`import {Y} from './other'; const A = Y;`, 'A', [
|
||||
{name: _('/other.ts'), contents: `export const Y = 'test';`},
|
||||
@ -795,7 +799,9 @@ runInEachFileSystem(() => {
|
||||
const evaluator = makeEvaluator(checker, {...fakeDepTracker, addDependency});
|
||||
evaluator.evaluate(expression);
|
||||
expect(addDependency).toHaveBeenCalledTimes(2);
|
||||
expect(addDependency.calls.allArgs().map(args => [args[0].fileName, args[1].fileName]))
|
||||
expect(
|
||||
addDependency.calls.allArgs().map(
|
||||
(args: Parameters<typeof addDependency>) => [args[0].fileName, args[1].fileName]))
|
||||
.toEqual([
|
||||
[_('/entry.ts'), _('/entry.ts')],
|
||||
[_('/entry.ts'), _('/other.ts')],
|
||||
@ -803,7 +809,8 @@ runInEachFileSystem(() => {
|
||||
});
|
||||
|
||||
it('should track files passed through during re-exports', () => {
|
||||
const addDependency = jasmine.createSpy('DependencyTracker');
|
||||
const addDependency =
|
||||
jasmine.createSpy<DependencyTracker['addDependency']>('DependencyTracker');
|
||||
const {expression, checker} =
|
||||
makeExpression(`import * as mod from './direct-reexport';`, 'mod.value.property', [
|
||||
{name: _('/const.ts'), contents: 'export const value = {property: "test"};'},
|
||||
@ -823,7 +830,9 @@ runInEachFileSystem(() => {
|
||||
const evaluator = makeEvaluator(checker, {...fakeDepTracker, addDependency});
|
||||
evaluator.evaluate(expression);
|
||||
expect(addDependency).toHaveBeenCalledTimes(2);
|
||||
expect(addDependency.calls.allArgs().map(args => [args[0].fileName, args[1].fileName]))
|
||||
expect(
|
||||
addDependency.calls.allArgs().map(
|
||||
(args: Parameters<typeof addDependency>) => [args[0].fileName, args[1].fileName]))
|
||||
.toEqual([
|
||||
[_('/entry.ts'), _('/direct-reexport.ts')],
|
||||
// Not '/indirect-reexport.ts' or '/def.ts'.
|
||||
|
@ -41,7 +41,9 @@ describe('ngc transformer command-line', () => {
|
||||
basePath = support.basePath;
|
||||
outDir = path.join(basePath, 'built');
|
||||
process.chdir(basePath);
|
||||
write = (fileName: string, content: string) => { support.write(fileName, content); };
|
||||
write = (fileName: string, content: string) => {
|
||||
support.write(fileName, content);
|
||||
};
|
||||
|
||||
write('tsconfig-base.json', `{
|
||||
"compilerOptions": {
|
||||
@ -96,8 +98,9 @@ describe('ngc transformer command-line', () => {
|
||||
});
|
||||
|
||||
describe('errors', () => {
|
||||
|
||||
beforeEach(() => { errorSpy.and.stub(); });
|
||||
beforeEach(() => {
|
||||
errorSpy.and.stub();
|
||||
});
|
||||
|
||||
it('should not print the stack trace if user input file does not exist', () => {
|
||||
writeConfig(`{
|
||||
@ -231,7 +234,6 @@ describe('ngc transformer command-line', () => {
|
||||
});
|
||||
|
||||
describe('compile ngfactory files', () => {
|
||||
|
||||
it('should compile ngfactory files that are not referenced by root files', () => {
|
||||
writeConfig(`{
|
||||
"extends": "./tsconfig-base.json",
|
||||
@ -1122,7 +1124,6 @@ describe('ngc transformer command-line', () => {
|
||||
});
|
||||
|
||||
describe('with external symbol re-exports enabled', () => {
|
||||
|
||||
it('should be able to compile multiple libraries with summaries', () => {
|
||||
// Note: we need to emit the generated code for the libraries
|
||||
// into the node_modules, as that is the only way that we
|
||||
@ -1559,11 +1560,15 @@ describe('ngc transformer command-line', () => {
|
||||
originalTimeout = jasmine.DEFAULT_TIMEOUT_INTERVAL;
|
||||
jasmine.DEFAULT_TIMEOUT_INTERVAL = 10000;
|
||||
const timerToken = 100;
|
||||
spyOn(ts.sys, 'setTimeout').and.callFake((callback: () => void) => {
|
||||
// TODO: @JiaLiPassion, need to wait @types/jasmine to handle optional method case
|
||||
// https://github.com/DefinitelyTyped/DefinitelyTyped/issues/43486
|
||||
spyOn(ts.sys as any, 'setTimeout').and.callFake((callback: () => void) => {
|
||||
timer = callback;
|
||||
return timerToken;
|
||||
});
|
||||
spyOn(ts.sys, 'clearTimeout').and.callFake((token: number) => {
|
||||
// TODO: @JiaLiPassion, need to wait @types/jasmine to handle optional method case
|
||||
// https://github.com/DefinitelyTyped/DefinitelyTyped/issues/43486
|
||||
spyOn(ts.sys as any, 'clearTimeout').and.callFake((token: number) => {
|
||||
if (token == timerToken) {
|
||||
timer = undefined;
|
||||
}
|
||||
@ -1615,7 +1620,9 @@ describe('ngc transformer command-line', () => {
|
||||
`);
|
||||
});
|
||||
|
||||
afterEach(() => { jasmine.DEFAULT_TIMEOUT_INTERVAL = originalTimeout; });
|
||||
afterEach(() => {
|
||||
jasmine.DEFAULT_TIMEOUT_INTERVAL = originalTimeout;
|
||||
});
|
||||
|
||||
function writeAppConfig(location: string) {
|
||||
writeConfig(`{
|
||||
@ -1670,11 +1677,13 @@ describe('ngc transformer command-line', () => {
|
||||
`);
|
||||
}));
|
||||
|
||||
it('should recompile when the html file changes',
|
||||
expectRecompile(() => { write('greet.html', '<p> Hello {{name}} again!</p>'); }));
|
||||
it('should recompile when the html file changes', expectRecompile(() => {
|
||||
write('greet.html', '<p> Hello {{name}} again!</p>');
|
||||
}));
|
||||
|
||||
it('should recompile when the css file changes',
|
||||
expectRecompile(() => { write('greet.css', `p.greeting { color: blue }`); }));
|
||||
it('should recompile when the css file changes', expectRecompile(() => {
|
||||
write('greet.css', `p.greeting { color: blue }`);
|
||||
}));
|
||||
});
|
||||
|
||||
describe('regressions', () => {
|
||||
@ -2039,8 +2048,8 @@ describe('ngc transformer command-line', () => {
|
||||
expect(exitCode).toBe(1, 'Compile was expected to fail');
|
||||
const srcPathWithSep = `lib/`;
|
||||
expect(messages[0])
|
||||
.toEqual(
|
||||
`${srcPathWithSep}test.component.ts(6,21): Error during template compile of 'TestComponent'
|
||||
.toEqual(`${
|
||||
srcPathWithSep}test.component.ts(6,21): Error during template compile of 'TestComponent'
|
||||
Tagged template expressions are not supported in metadata in 't1'
|
||||
't1' references 't2' at ${srcPathWithSep}indirect1.ts(3,27)
|
||||
't2' contains the error at ${srcPathWithSep}indirect2.ts(4,27).
|
||||
@ -2049,7 +2058,6 @@ describe('ngc transformer command-line', () => {
|
||||
});
|
||||
|
||||
describe('tree shakeable services', () => {
|
||||
|
||||
function compileService(source: string): string {
|
||||
write('service.ts', source);
|
||||
|
||||
@ -2323,17 +2331,17 @@ describe('ngc transformer command-line', () => {
|
||||
}));
|
||||
write('lib1/index.ts', `
|
||||
import {Directive} from '@angular/core';
|
||||
|
||||
|
||||
@Directive()
|
||||
export class BaseClass {}
|
||||
`);
|
||||
write('index.ts', `
|
||||
import {NgModule, Directive} from '@angular/core';
|
||||
import {BaseClass} from 'lib1_built';
|
||||
|
||||
|
||||
@Directive({selector: 'my-dir'})
|
||||
export class MyDirective extends BaseClass {}
|
||||
|
||||
|
||||
@NgModule({declarations: [MyDirective]})
|
||||
export class AppModule {}
|
||||
`);
|
||||
|
@ -13,8 +13,8 @@ describe('convertValueToOutputAst', () => {
|
||||
it('should convert all array elements, including undefined', () => {
|
||||
const ctx = null;
|
||||
const value = new Array(3).concat('foo');
|
||||
const expr = convertValueToOutputAst(ctx !, value) as o.LiteralArrayExpr;
|
||||
expect(expr instanceof o.LiteralArrayExpr);
|
||||
const expr = convertValueToOutputAst(ctx!, value) as o.LiteralArrayExpr;
|
||||
expect(expr instanceof o.LiteralArrayExpr).toBe(true);
|
||||
expect(expr.entries.length).toBe(4);
|
||||
for (let i = 0; i < 4; ++i) {
|
||||
expect(expr.entries[i] instanceof o.Expression).toBe(true);
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -25,30 +25,45 @@ describe('global utils', () => {
|
||||
describe('publishDefaultGlobalUtils', () => {
|
||||
beforeEach(() => publishDefaultGlobalUtils());
|
||||
|
||||
it('should publish getComponent', () => { assertPublished('getComponent', getComponent); });
|
||||
it('should publish getComponent', () => {
|
||||
assertPublished('getComponent', getComponent);
|
||||
});
|
||||
|
||||
it('should publish getContext', () => { assertPublished('getContext', getContext); });
|
||||
it('should publish getContext', () => {
|
||||
assertPublished('getContext', getContext);
|
||||
});
|
||||
|
||||
it('should publish getListeners', () => { assertPublished('getListeners', getListeners); });
|
||||
it('should publish getListeners', () => {
|
||||
assertPublished('getListeners', getListeners);
|
||||
});
|
||||
|
||||
it('should publish getOwningComponent',
|
||||
() => { assertPublished('getOwningComponent', getOwningComponent); });
|
||||
it('should publish getOwningComponent', () => {
|
||||
assertPublished('getOwningComponent', getOwningComponent);
|
||||
});
|
||||
|
||||
it('should publish getRootComponents',
|
||||
() => { assertPublished('getRootComponents', getRootComponents); });
|
||||
it('should publish getRootComponents', () => {
|
||||
assertPublished('getRootComponents', getRootComponents);
|
||||
});
|
||||
|
||||
it('should publish getDirectives', () => { assertPublished('getDirectives', getDirectives); });
|
||||
it('should publish getDirectives', () => {
|
||||
assertPublished('getDirectives', getDirectives);
|
||||
});
|
||||
|
||||
it('should publish getHostComponent',
|
||||
() => { assertPublished('getHostElement', getHostElement); });
|
||||
it('should publish getHostComponent', () => {
|
||||
assertPublished('getHostElement', getHostElement);
|
||||
});
|
||||
|
||||
it('should publish getInjector', () => { assertPublished('getInjector', getInjector); });
|
||||
it('should publish getInjector', () => {
|
||||
assertPublished('getInjector', getInjector);
|
||||
});
|
||||
|
||||
it('should publish applyChanges', () => { assertPublished('applyChanges', applyChanges); });
|
||||
it('should publish applyChanges', () => {
|
||||
assertPublished('applyChanges', applyChanges);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
function assertPublished(name: string, value: {}) {
|
||||
function assertPublished(name: string, value: Function) {
|
||||
const w = global as any as GlobalDevModeContainer;
|
||||
expect(w[GLOBAL_PUBLISH_EXPANDO_KEY][name]).toBe(value);
|
||||
}
|
||||
|
@ -12,24 +12,21 @@ import {AttributeMarker, TAttributes, TNode, TNodeType} from '../../src/render3/
|
||||
import {CssSelector, CssSelectorList, SelectorFlags} from '../../src/render3/interfaces/projection';
|
||||
import {extractAttrsAndClassesFromSelector, getProjectAsAttrValue, isNodeMatchingSelector, isNodeMatchingSelectorList, stringifyCSSSelectorList} from '../../src/render3/node_selector_matcher';
|
||||
|
||||
function testLStaticData(tagName: string, attrs: TAttributes | null): TNode {
|
||||
return createTNode(null !, null, TNodeType.Element, 0, tagName, attrs);
|
||||
function testLStaticData(tagName: string, attrs: TAttributes|null): TNode {
|
||||
return createTNode(null!, null, TNodeType.Element, 0, tagName, attrs);
|
||||
}
|
||||
|
||||
describe('css selector matching', () => {
|
||||
function isMatching(
|
||||
tagName: string, attrsOrTNode: TAttributes | TNode | null, selector: CssSelector): boolean {
|
||||
tagName: string, attrsOrTNode: TAttributes|TNode|null, selector: CssSelector): boolean {
|
||||
const tNode = (!attrsOrTNode || Array.isArray(attrsOrTNode)) ?
|
||||
createTNode(null !, null, TNodeType.Element, 0, tagName, attrsOrTNode as TAttributes) :
|
||||
createTNode(null!, null, TNodeType.Element, 0, tagName, attrsOrTNode as TAttributes) :
|
||||
(attrsOrTNode as TNode);
|
||||
return isNodeMatchingSelector(tNode, selector, true);
|
||||
}
|
||||
|
||||
describe('isNodeMatchingSimpleSelector', () => {
|
||||
|
||||
|
||||
describe('element matching', () => {
|
||||
|
||||
it('should match element name only if names are the same', () => {
|
||||
expect(isMatching('span', null, ['span']))
|
||||
.toBeTruthy(`Selector 'span' should match <span>`);
|
||||
@ -55,11 +52,9 @@ describe('css selector matching', () => {
|
||||
});
|
||||
|
||||
describe('attributes matching', () => {
|
||||
|
||||
// TODO: do we need to differentiate no value and empty value? that is: title vs. title="" ?
|
||||
|
||||
it('should match single attribute without value', () => {
|
||||
|
||||
expect(isMatching('span', ['title', ''], [
|
||||
'', 'title', ''
|
||||
])).toBeTruthy(`Selector '[title]' should match <span title>`);
|
||||
@ -81,10 +76,13 @@ describe('css selector matching', () => {
|
||||
])).toBeFalsy(`Selector '[other]' should NOT match <span title="">'`);
|
||||
});
|
||||
|
||||
it('should match namespaced attributes', () => {
|
||||
// TODO: this case will not work, need more discussion
|
||||
// https://github.com/angular/angular/pull/34625#discussion_r401791275
|
||||
xit('should match namespaced attributes', () => {
|
||||
expect(isMatching(
|
||||
'span', [AttributeMarker.NamespaceURI, 'http://some/uri', 'title', 'name'],
|
||||
['', 'title', '']));
|
||||
'span', [AttributeMarker.NamespaceURI, 'http://some/uri', 'title', 'name'],
|
||||
['', 'title', '']))
|
||||
.toBeTruthy();
|
||||
});
|
||||
|
||||
it('should match selector with one attribute without value when element has several attributes',
|
||||
@ -226,7 +224,6 @@ describe('css selector matching', () => {
|
||||
});
|
||||
|
||||
describe('class matching', () => {
|
||||
|
||||
it('should match with a class selector when an element has multiple classes', () => {
|
||||
expect(isMatching('span', ['class', 'foo bar'], [
|
||||
'', SelectorFlags.CLASS, 'foo'
|
||||
@ -326,7 +323,6 @@ describe('css selector matching', () => {
|
||||
});
|
||||
|
||||
describe('negations', () => {
|
||||
|
||||
it('should match when negation part is null', () => {
|
||||
expect(isMatching('span', null, ['span'])).toBeTruthy(`Selector 'span' should match <span>`);
|
||||
});
|
||||
@ -434,13 +430,11 @@ describe('css selector matching', () => {
|
||||
expect(isMatching('div', ['name', 'name', 'title', '', 'class', 'foo bar'], selector))
|
||||
.toBeFalsy();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('isNodeMatchingSelectorList', () => {
|
||||
|
||||
function isAnyMatching(
|
||||
tagName: string, attrs: string[] | null, selector: CssSelectorList): boolean {
|
||||
tagName: string, attrs: string[]|null, selector: CssSelectorList): boolean {
|
||||
return isNodeMatchingSelectorList(testLStaticData(tagName, attrs), selector, false);
|
||||
}
|
||||
|
||||
@ -466,16 +460,18 @@ describe('css selector matching', () => {
|
||||
});
|
||||
|
||||
describe('reading the ngProjectAs attribute value', function() {
|
||||
|
||||
function testTNode(attrs: TAttributes | null) { return testLStaticData('tag', attrs); }
|
||||
function testTNode(attrs: TAttributes|null) {
|
||||
return testLStaticData('tag', attrs);
|
||||
}
|
||||
|
||||
it('should get ngProjectAs value if present', function() {
|
||||
expect(getProjectAsAttrValue(testTNode([AttributeMarker.ProjectAs, ['tag', 'foo', 'bar']])))
|
||||
.toEqual(['tag', 'foo', 'bar']);
|
||||
});
|
||||
|
||||
it('should return null if there are no attributes',
|
||||
function() { expect(getProjectAsAttrValue(testTNode(null))).toBe(null); });
|
||||
it('should return null if there are no attributes', function() {
|
||||
expect(getProjectAsAttrValue(testTNode(null))).toBe(null);
|
||||
});
|
||||
|
||||
it('should return if ngProjectAs is not present', function() {
|
||||
expect(getProjectAsAttrValue(testTNode(['foo', 'bar']))).toBe(null);
|
||||
@ -484,15 +480,13 @@ describe('css selector matching', () => {
|
||||
it('should not accidentally identify ngProjectAs in attribute values', function() {
|
||||
expect(getProjectAsAttrValue(testTNode(['foo', AttributeMarker.ProjectAs]))).toBe(null);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('stringifyCSSSelectorList', () => {
|
||||
|
||||
it('should stringify selector with a tag name only',
|
||||
() => { expect(stringifyCSSSelectorList([['button']])).toBe('button'); });
|
||||
it('should stringify selector with a tag name only', () => {
|
||||
expect(stringifyCSSSelectorList([['button']])).toBe('button');
|
||||
});
|
||||
|
||||
it('should stringify selector with attributes', () => {
|
||||
expect(stringifyCSSSelectorList([['', 'id', '']])).toBe('[id]');
|
||||
@ -616,4 +610,4 @@ describe('extractAttrsAndClassesFromSelector', () => {
|
||||
expect(extracted.classes).toEqual(classes as string[]);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -15,7 +15,10 @@ describe('utils', () => {
|
||||
let clearTimeoutSpy: jasmine.Spy;
|
||||
|
||||
beforeEach(() => {
|
||||
setTimeoutSpy = spyOn(window, 'setTimeout').and.returnValue(42);
|
||||
// TODO: @JiaLiPassion, need to wait @types/jasmine to fix the wrong return
|
||||
// type infer issue.
|
||||
// https://github.com/DefinitelyTyped/DefinitelyTyped/issues/43486
|
||||
setTimeoutSpy = spyOn(window, 'setTimeout').and.returnValue(42 as any);
|
||||
clearTimeoutSpy = spyOn(window, 'clearTimeout');
|
||||
});
|
||||
|
||||
@ -81,8 +84,9 @@ describe('utils', () => {
|
||||
expect(camelToDashCase('foo1Bar2Baz3Qux4')).toBe('foo1-bar2-baz3-qux4');
|
||||
});
|
||||
|
||||
it('should keep existing dashes',
|
||||
() => { expect(camelToDashCase('fooBar-baz-Qux')).toBe('foo-bar-baz--qux'); });
|
||||
it('should keep existing dashes', () => {
|
||||
expect(camelToDashCase('fooBar-baz-Qux')).toBe('foo-bar-baz--qux');
|
||||
});
|
||||
});
|
||||
|
||||
describe('createCustomEvent()', () => {
|
||||
@ -97,7 +101,6 @@ describe('utils', () => {
|
||||
expect(event.cancelable).toBe(false);
|
||||
expect(event.detail).toEqual(value);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('isElement()', () => {
|
||||
@ -129,7 +132,7 @@ describe('utils', () => {
|
||||
it('should return true for functions', () => {
|
||||
const obj = {foo: function() {}, bar: () => null, baz() {}};
|
||||
const fns = [
|
||||
function(){},
|
||||
function() {},
|
||||
() => null,
|
||||
obj.foo,
|
||||
obj.bar,
|
||||
@ -180,7 +183,7 @@ describe('utils', () => {
|
||||
</ul>
|
||||
</div>
|
||||
`;
|
||||
li = div.querySelector('li') !;
|
||||
li = div.querySelector('li')!;
|
||||
});
|
||||
|
||||
it('should return whether the element matches the selector', () => {
|
||||
@ -216,7 +219,9 @@ describe('utils', () => {
|
||||
];
|
||||
|
||||
values.forEach((v1, i) => {
|
||||
values.forEach((v2, j) => { expect(strictEquals(v1, v2)).toBe(i === j); });
|
||||
values.forEach((v2, j) => {
|
||||
expect(strictEquals(v1, v2)).toBe(i === j);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -6,7 +6,7 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {Inject, ReflectiveInjector, forwardRef, resolveForwardRef} from '@angular/core';
|
||||
import {forwardRef, Inject, ReflectiveInjector, resolveForwardRef} from '@angular/core';
|
||||
|
||||
{
|
||||
describe('forwardRef examples', () => {
|
||||
@ -26,7 +26,9 @@ import {Inject, ReflectiveInjector, forwardRef, resolveForwardRef} from '@angula
|
||||
|
||||
// Door attempts to inject Lock, despite it not being defined yet.
|
||||
// forwardRef makes this possible.
|
||||
constructor(@Inject(forwardRef(() => Lock)) lock: Lock) { this.lock = lock; }
|
||||
constructor(@Inject(forwardRef(() => Lock)) lock: Lock) {
|
||||
this.lock = lock;
|
||||
}
|
||||
}
|
||||
|
||||
// Only at this point Lock is defined.
|
||||
@ -42,7 +44,7 @@ import {Inject, ReflectiveInjector, forwardRef, resolveForwardRef} from '@angula
|
||||
it('can be unwrapped', () => {
|
||||
// #docregion resolve_forward_ref
|
||||
const ref = forwardRef(() => 'refValue');
|
||||
expect(resolveForwardRef(ref)).toEqual('refValue');
|
||||
expect(resolveForwardRef(ref as any)).toEqual('refValue');
|
||||
expect(resolveForwardRef('regularValue')).toEqual('regularValue');
|
||||
// #enddocregion
|
||||
});
|
||||
|
@ -168,9 +168,10 @@ describe('completions', () => {
|
||||
});
|
||||
|
||||
it('should be able to get completions in an empty interpolation', () => {
|
||||
const marker = mockHost.getLocationMarkerFor(PARSING_CASES, 'empty-interpolation');
|
||||
const completions = ngLS.getCompletionsAtPosition(PARSING_CASES, marker.start);
|
||||
expectContain(completions, CompletionKind.PROPERTY, ['title', 'subTitle']);
|
||||
mockHost.override(TEST_TEMPLATE, `{{ ~{cursor} }}`);
|
||||
const marker = mockHost.getLocationMarkerFor(TEST_TEMPLATE, 'cursor');
|
||||
const completions = ngLS.getCompletionsAtPosition(TEST_TEMPLATE, marker.start);
|
||||
expectContain(completions, CompletionKind.PROPERTY, ['title', 'hero']);
|
||||
});
|
||||
|
||||
it('should suggest $any() type cast function in an interpolation', () => {
|
||||
@ -282,9 +283,14 @@ describe('completions', () => {
|
||||
|
||||
describe('with a *ngIf', () => {
|
||||
it('should be able to get completions for exported *ngIf variable', () => {
|
||||
const marker = mockHost.getLocationMarkerFor(PARSING_CASES, 'promised-person-name');
|
||||
const completions = ngLS.getCompletionsAtPosition(PARSING_CASES, marker.start);
|
||||
expectContain(completions, CompletionKind.PROPERTY, ['name', 'age', 'street']);
|
||||
mockHost.override(TEST_TEMPLATE, `
|
||||
<div *ngIf="heroP | async as h">
|
||||
{{ h.~{cursor} }}
|
||||
</div>
|
||||
`);
|
||||
const marker = mockHost.getLocationMarkerFor(TEST_TEMPLATE, 'cursor');
|
||||
const completions = ngLS.getCompletionsAtPosition(TEST_TEMPLATE, marker.start);
|
||||
expectContain(completions, CompletionKind.PROPERTY, ['id', 'name']);
|
||||
});
|
||||
});
|
||||
|
||||
@ -368,9 +374,14 @@ describe('completions', () => {
|
||||
});
|
||||
|
||||
it('should be able to infer the type of a ngForOf with an async pipe', () => {
|
||||
const marker = mockHost.getLocationMarkerFor(PARSING_CASES, 'async-person-name');
|
||||
const completions = ngLS.getCompletionsAtPosition(PARSING_CASES, marker.start);
|
||||
expectContain(completions, CompletionKind.PROPERTY, ['name', 'age', 'street']);
|
||||
mockHost.override(TEST_TEMPLATE, `
|
||||
<div *ngFor="let h of heroesP | async">
|
||||
{{ h.~{cursor} }}
|
||||
</div>
|
||||
`);
|
||||
const marker = mockHost.getLocationMarkerFor(TEST_TEMPLATE, 'cursor');
|
||||
const completions = ngLS.getCompletionsAtPosition(TEST_TEMPLATE, marker.start);
|
||||
expectContain(completions, CompletionKind.PROPERTY, ['id', 'name']);
|
||||
});
|
||||
|
||||
it('should be able to resolve variable in nested loop', () => {
|
||||
@ -498,14 +509,27 @@ describe('completions', () => {
|
||||
|
||||
describe('with references', () => {
|
||||
it('should list references', () => {
|
||||
const marker = mockHost.getLocationMarkerFor(PARSING_CASES, 'test-comp-content');
|
||||
const completions = ngLS.getCompletionsAtPosition(PARSING_CASES, marker.start);
|
||||
expectContain(completions, CompletionKind.REFERENCE, ['div', 'test1', 'test2']);
|
||||
mockHost.override(TEST_TEMPLATE, `
|
||||
<div #myDiv>
|
||||
<test-comp #test1>
|
||||
{{ ~{cursor} }}
|
||||
</test-comp>
|
||||
</div>
|
||||
<test-comp #test2></test-comp>
|
||||
`);
|
||||
const marker = mockHost.getLocationMarkerFor(TEST_TEMPLATE, 'cursor');
|
||||
const completions = ngLS.getCompletionsAtPosition(TEST_TEMPLATE, marker.start);
|
||||
expectContain(completions, CompletionKind.REFERENCE, ['myDiv', 'test1', 'test2']);
|
||||
});
|
||||
|
||||
it('should reference the component', () => {
|
||||
const marker = mockHost.getLocationMarkerFor(PARSING_CASES, 'test-comp-after-test');
|
||||
const completions = ngLS.getCompletionsAtPosition(PARSING_CASES, marker.start);
|
||||
mockHost.override(TEST_TEMPLATE, `
|
||||
<test-comp #test1>
|
||||
{{ test1.~{cursor} }}
|
||||
</test-comp>
|
||||
`);
|
||||
const marker = mockHost.getLocationMarkerFor(TEST_TEMPLATE, 'cursor');
|
||||
const completions = ngLS.getCompletionsAtPosition(TEST_TEMPLATE, marker.start);
|
||||
expectContain(completions, CompletionKind.PROPERTY, ['name', 'testEvent']);
|
||||
});
|
||||
|
||||
|
@ -342,7 +342,7 @@ describe('diagnostics', () => {
|
||||
expect(category).toBe(ts.DiagnosticCategory.Error);
|
||||
expect(messageText)
|
||||
.toBe(
|
||||
`Identifier 'missingField' is not defined. '{ implicitPerson: Person; }' does not contain such a member`,
|
||||
`Identifier 'missingField' is not defined. '{ implicitPerson: Hero; }' does not contain such a member`,
|
||||
);
|
||||
const span = mockHost.getLocationMarkerFor(TEST_TEMPLATE, 'emb');
|
||||
expect(start).toBe(span.start);
|
||||
@ -361,7 +361,7 @@ describe('diagnostics', () => {
|
||||
expect(category).toBe(ts.DiagnosticCategory.Error);
|
||||
expect(messageText)
|
||||
.toBe(
|
||||
`Identifier 'missingField' is not defined. 'Person' does not contain such a member`,
|
||||
`Identifier 'missingField' is not defined. 'Hero' does not contain such a member`,
|
||||
);
|
||||
const span = mockHost.getLocationMarkerFor(TEST_TEMPLATE, 'emb');
|
||||
expect(start).toBe(span.start);
|
||||
|
@ -28,7 +28,7 @@ describe('html_info', () => {
|
||||
const elements = SchemaInformation.instance.allKnownElements();
|
||||
for (const element of elements) {
|
||||
for (const prop of SchemaInformation.instance.propertiesOf(element)) {
|
||||
expect(domRegistry.hasProperty(element, prop, []));
|
||||
expect(domRegistry.hasProperty(element, prop, [])).toBeTrue();
|
||||
}
|
||||
}
|
||||
});
|
||||
@ -48,4 +48,4 @@ function uniqueElements<T>(a: T[], b: T[]): T[] {
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
@ -16,16 +16,13 @@ import * as ParsingCases from './parsing-cases';
|
||||
imports: [CommonModule, FormsModule],
|
||||
declarations: [
|
||||
AppComponent,
|
||||
ParsingCases.AsyncForUsingComponent,
|
||||
ParsingCases.CaseIncompleteOpen,
|
||||
ParsingCases.CaseMissingClosing,
|
||||
ParsingCases.CaseUnknown,
|
||||
ParsingCases.CounterDirective,
|
||||
ParsingCases.EmptyInterpolation,
|
||||
ParsingCases.HintModel,
|
||||
ParsingCases.NoValueAttribute,
|
||||
ParsingCases.NumberModel,
|
||||
ParsingCases.References,
|
||||
ParsingCases.StringModel,
|
||||
ParsingCases.TemplateReference,
|
||||
ParsingCases.TestComponent,
|
||||
|
@ -63,45 +63,6 @@ export class HintModel {
|
||||
hintChange: EventEmitter<string> = new EventEmitter();
|
||||
}
|
||||
|
||||
interface Person {
|
||||
name: string;
|
||||
age: number;
|
||||
street: string;
|
||||
}
|
||||
|
||||
@Component({
|
||||
template: `
|
||||
<div *ngFor="let person of people | async">
|
||||
{{person.~{async-person-name}name}}
|
||||
</div>
|
||||
<div *ngIf="promisedPerson | async as person">
|
||||
{{person.~{promised-person-name}name}}
|
||||
</div>
|
||||
`,
|
||||
})
|
||||
export class AsyncForUsingComponent {
|
||||
people: Promise<Person[]> = Promise.resolve([]);
|
||||
promisedPerson: Promise<Person> = Promise.resolve({
|
||||
name: 'John Doe',
|
||||
age: 42,
|
||||
street: '123 Angular Ln',
|
||||
});
|
||||
}
|
||||
|
||||
@Component({
|
||||
template: `
|
||||
<div #div>
|
||||
<test-comp #test1>
|
||||
{{~{test-comp-content}}}
|
||||
{{test1.~{test-comp-after-test}name}}
|
||||
{{div.~{test-comp-after-div}.innerText}}
|
||||
</test-comp>
|
||||
</div>
|
||||
<test-comp #test2></test-comp>`,
|
||||
})
|
||||
export class References {
|
||||
}
|
||||
|
||||
class CounterDirectiveContext<T> {
|
||||
constructor(public $implicit: T) {}
|
||||
}
|
||||
@ -121,8 +82,8 @@ export class CounterDirective implements OnChanges {
|
||||
}
|
||||
|
||||
interface WithContextDirectiveContext {
|
||||
$implicit: {implicitPerson: Person;};
|
||||
nonImplicitPerson: Person;
|
||||
$implicit: {implicitPerson: Hero;};
|
||||
nonImplicitPerson: Hero;
|
||||
}
|
||||
|
||||
@Directive({selector: '[withContext]'})
|
||||
@ -156,7 +117,9 @@ export class TemplateReference {
|
||||
*/
|
||||
title = 'Some title';
|
||||
hero: Hero = {id: 1, name: 'Windstorm'};
|
||||
heroP = Promise.resolve(this.hero);
|
||||
heroes: Hero[] = [this.hero];
|
||||
heroesP = Promise.resolve(this.heroes);
|
||||
tupleArray: [string, Hero] = ['test', this.hero];
|
||||
league: Hero[][] = [this.heroes];
|
||||
heroesByName: {[name: string]: Hero} = {};
|
||||
@ -171,11 +134,3 @@ export class TemplateReference {
|
||||
constNames = [{name: 'name'}] as const;
|
||||
private myField = 'My Field';
|
||||
}
|
||||
|
||||
@Component({
|
||||
template: '{{~{empty-interpolation}}}',
|
||||
})
|
||||
export class EmptyInterpolation {
|
||||
title = 'Some title';
|
||||
subTitle = 'Some sub title';
|
||||
}
|
||||
|
@ -94,7 +94,7 @@ describe('TypeScriptServiceHost', () => {
|
||||
const tsLS = ts.createLanguageService(tsLSHost);
|
||||
const ngLSHost = new TypeScriptServiceHost(tsLSHost, tsLS);
|
||||
const templates = ngLSHost.getTemplates('/app/parsing-cases.ts');
|
||||
expect(templates.length).toBe(8);
|
||||
expect(templates.length).toBe(5);
|
||||
});
|
||||
|
||||
it('should be able to find external template', () => {
|
||||
|
@ -17,7 +17,9 @@ import {HammerGestureConfig, HammerGesturesPlugin,} from '@angular/platform-brow
|
||||
let fakeConsole: any;
|
||||
if (isNode) return;
|
||||
|
||||
beforeEach(() => { fakeConsole = {warn: jasmine.createSpy('console.warn')}; });
|
||||
beforeEach(() => {
|
||||
fakeConsole = {warn: jasmine.createSpy('console.warn')};
|
||||
});
|
||||
|
||||
describe('with no custom loader', () => {
|
||||
beforeEach(() => {
|
||||
@ -25,7 +27,7 @@ import {HammerGestureConfig, HammerGesturesPlugin,} from '@angular/platform-brow
|
||||
});
|
||||
|
||||
it('should implement addGlobalEventListener', () => {
|
||||
spyOn(plugin, 'addEventListener').and.callFake(() => {});
|
||||
spyOn(plugin, 'addEventListener').and.callFake(() => () => {});
|
||||
|
||||
expect(() => {
|
||||
plugin.addGlobalEventListener('document', 'swipe', () => {});
|
||||
@ -61,7 +63,9 @@ import {HammerGestureConfig, HammerGesturesPlugin,} from '@angular/platform-brow
|
||||
// Inject the NgZone so that we can make it available to the plugin through a fake
|
||||
// EventManager.
|
||||
let ngZone: NgZone;
|
||||
beforeEach(inject([NgZone], (z: NgZone) => { ngZone = z; }));
|
||||
beforeEach(inject([NgZone], (z: NgZone) => {
|
||||
ngZone = z;
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
originalHammerGlobal = (window as any).Hammer;
|
||||
@ -84,13 +88,15 @@ import {HammerGestureConfig, HammerGesturesPlugin,} from '@angular/platform-brow
|
||||
plugin = new HammerGesturesPlugin(document, hammerConfig, fakeConsole, loader);
|
||||
|
||||
// Use a fake EventManager that has access to the NgZone.
|
||||
plugin.manager = { getZone: () => ngZone } as EventManager;
|
||||
plugin.manager = {getZone: () => ngZone} as EventManager;
|
||||
|
||||
someElement = document.createElement('div');
|
||||
someListener = () => {};
|
||||
});
|
||||
|
||||
afterEach(() => { (window as any).Hammer = originalHammerGlobal; });
|
||||
afterEach(() => {
|
||||
(window as any).Hammer = originalHammerGlobal;
|
||||
});
|
||||
|
||||
it('should not log a warning when HammerJS is not loaded', () => {
|
||||
plugin.addEventListener(someElement, 'swipe', () => {});
|
||||
|
@ -51,7 +51,6 @@ import {KeyEventsPlugin} from '@angular/platform-browser/src/dom/events/key_even
|
||||
.toEqual({'domEventName': 'keydown', 'fullKey': 'control.shift'});
|
||||
expect(KeyEventsPlugin.parseEventName('keyup.control.shift'))
|
||||
.toEqual({'domEventName': 'keyup', 'fullKey': 'control.shift'});
|
||||
|
||||
});
|
||||
|
||||
it('should alias esc to escape', () => {
|
||||
@ -62,11 +61,10 @@ import {KeyEventsPlugin} from '@angular/platform-browser/src/dom/events/key_even
|
||||
it('should implement addGlobalEventListener', () => {
|
||||
const plugin = new KeyEventsPlugin(document);
|
||||
|
||||
spyOn(plugin, 'addEventListener').and.callFake(() => {});
|
||||
spyOn(plugin, 'addEventListener').and.callFake(() => () => {});
|
||||
|
||||
expect(() => plugin.addGlobalEventListener('window', 'keyup.control.esc', () => {}))
|
||||
.not.toThrowError();
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
|
@ -36,7 +36,8 @@ export type Routes = Route[];
|
||||
* @publicApi
|
||||
*/
|
||||
export type UrlMatchResult = {
|
||||
consumed: UrlSegment[]; posParams?: {[name: string]: UrlSegment};
|
||||
consumed: UrlSegment[];
|
||||
posParams?: {[name: string]: UrlSegment};
|
||||
};
|
||||
|
||||
/**
|
||||
@ -64,7 +65,7 @@ export type UrlMatchResult = {
|
||||
* @publicApi
|
||||
*/
|
||||
export type UrlMatcher = (segments: UrlSegment[], group: UrlSegmentGroup, route: Route) =>
|
||||
UrlMatchResult;
|
||||
UrlMatchResult|null;
|
||||
|
||||
/**
|
||||
*
|
||||
@ -109,7 +110,7 @@ export type ResolveData = {
|
||||
* @see `Route#loadChildren`.
|
||||
* @publicApi
|
||||
*/
|
||||
export type LoadChildrenCallback = () => Type<any>| NgModuleFactory<any>| Observable<Type<any>>|
|
||||
export type LoadChildrenCallback = () => Type<any>|NgModuleFactory<any>|Observable<Type<any>>|
|
||||
Promise<NgModuleFactory<any>|Type<any>|any>;
|
||||
|
||||
/**
|
||||
@ -123,7 +124,7 @@ export type LoadChildrenCallback = () => Type<any>| NgModuleFactory<any>| Observ
|
||||
* @see `Route#loadChildren`.
|
||||
* @publicApi
|
||||
*/
|
||||
export type LoadChildren = LoadChildrenCallback | DeprecatedLoadChildren;
|
||||
export type LoadChildren = LoadChildrenCallback|DeprecatedLoadChildren;
|
||||
|
||||
/**
|
||||
* A string of the form `path/to/file#exportName` that acts as a URL for a set of routes to load.
|
||||
@ -147,7 +148,7 @@ export type DeprecatedLoadChildren = string;
|
||||
* @see `RouterLink`
|
||||
* @publicApi
|
||||
*/
|
||||
export type QueryParamsHandling = 'merge' | 'preserve' | '';
|
||||
export type QueryParamsHandling = 'merge'|'preserve'|'';
|
||||
|
||||
/**
|
||||
*
|
||||
@ -156,9 +157,9 @@ export type QueryParamsHandling = 'merge' | 'preserve' | '';
|
||||
* @see `Route#runGuardsAndResolvers`
|
||||
* @publicApi
|
||||
*/
|
||||
export type RunGuardsAndResolvers = 'pathParamsChange' | 'pathParamsOrQueryParamsChange' |
|
||||
'paramsChange' | 'paramsOrQueryParamsChange' | 'always' |
|
||||
((from: ActivatedRouteSnapshot, to: ActivatedRouteSnapshot) => boolean);
|
||||
export type RunGuardsAndResolvers =
|
||||
'pathParamsChange'|'pathParamsOrQueryParamsChange'|'paramsChange'|'paramsOrQueryParamsChange'|
|
||||
'always'|((from: ActivatedRouteSnapshot, to: ActivatedRouteSnapshot) => boolean);
|
||||
|
||||
/**
|
||||
* A configuration object that defines a single route.
|
||||
@ -519,36 +520,36 @@ function validateNode(route: Route, fullPath: string): void {
|
||||
}
|
||||
if (!route.component && !route.children && !route.loadChildren &&
|
||||
(route.outlet && route.outlet !== PRIMARY_OUTLET)) {
|
||||
throw new Error(
|
||||
`Invalid configuration of route '${fullPath}': a componentless route without children or loadChildren cannot have a named outlet set`);
|
||||
throw new Error(`Invalid configuration of route '${
|
||||
fullPath}': a componentless route without children or loadChildren cannot have a named outlet set`);
|
||||
}
|
||||
if (route.redirectTo && route.children) {
|
||||
throw new Error(
|
||||
`Invalid configuration of route '${fullPath}': redirectTo and children cannot be used together`);
|
||||
throw new Error(`Invalid configuration of route '${
|
||||
fullPath}': redirectTo and children cannot be used together`);
|
||||
}
|
||||
if (route.redirectTo && route.loadChildren) {
|
||||
throw new Error(
|
||||
`Invalid configuration of route '${fullPath}': redirectTo and loadChildren cannot be used together`);
|
||||
throw new Error(`Invalid configuration of route '${
|
||||
fullPath}': redirectTo and loadChildren cannot be used together`);
|
||||
}
|
||||
if (route.children && route.loadChildren) {
|
||||
throw new Error(
|
||||
`Invalid configuration of route '${fullPath}': children and loadChildren cannot be used together`);
|
||||
throw new Error(`Invalid configuration of route '${
|
||||
fullPath}': children and loadChildren cannot be used together`);
|
||||
}
|
||||
if (route.redirectTo && route.component) {
|
||||
throw new Error(
|
||||
`Invalid configuration of route '${fullPath}': redirectTo and component cannot be used together`);
|
||||
throw new Error(`Invalid configuration of route '${
|
||||
fullPath}': redirectTo and component cannot be used together`);
|
||||
}
|
||||
if (route.path && route.matcher) {
|
||||
throw new Error(
|
||||
`Invalid configuration of route '${fullPath}': path and matcher cannot be used together`);
|
||||
}
|
||||
if (route.redirectTo === void 0 && !route.component && !route.children && !route.loadChildren) {
|
||||
throw new Error(
|
||||
`Invalid configuration of route '${fullPath}'. One of the following must be provided: component, redirectTo, children or loadChildren`);
|
||||
throw new Error(`Invalid configuration of route '${
|
||||
fullPath}'. One of the following must be provided: component, redirectTo, children or loadChildren`);
|
||||
}
|
||||
if (route.path === void 0 && route.matcher === void 0) {
|
||||
throw new Error(
|
||||
`Invalid configuration of route '${fullPath}': routes must have either a path or a matcher specified`);
|
||||
throw new Error(`Invalid configuration of route '${
|
||||
fullPath}': routes must have either a path or a matcher specified`);
|
||||
}
|
||||
if (typeof route.path === 'string' && route.path.charAt(0) === '/') {
|
||||
throw new Error(`Invalid configuration of route '${fullPath}': path cannot start with a slash`);
|
||||
@ -556,12 +557,12 @@ function validateNode(route: Route, fullPath: string): void {
|
||||
if (route.path === '' && route.redirectTo !== void 0 && route.pathMatch === void 0) {
|
||||
const exp =
|
||||
`The default value of 'pathMatch' is 'prefix', but often the intent is to use 'full'.`;
|
||||
throw new Error(
|
||||
`Invalid configuration of route '{path: "${fullPath}", redirectTo: "${route.redirectTo}"}': please provide 'pathMatch'. ${exp}`);
|
||||
throw new Error(`Invalid configuration of route '{path: "${fullPath}", redirectTo: "${
|
||||
route.redirectTo}"}': please provide 'pathMatch'. ${exp}`);
|
||||
}
|
||||
if (route.pathMatch !== void 0 && route.pathMatch !== 'full' && route.pathMatch !== 'prefix') {
|
||||
throw new Error(
|
||||
`Invalid configuration of route '${fullPath}': pathMatch can only be set to 'prefix' or 'full'`);
|
||||
throw new Error(`Invalid configuration of route '${
|
||||
fullPath}': pathMatch can only be set to 'prefix' or 'full'`);
|
||||
}
|
||||
if (route.children) {
|
||||
validateConfig(route.children, fullPath);
|
||||
|
@ -3708,7 +3708,7 @@ describe('Integration', () => {
|
||||
router.navigate(['/user/:fedor']);
|
||||
advance(fixture);
|
||||
|
||||
expect(navigateSpy.calls.mostRecent().args[1].queryParams);
|
||||
expect(navigateSpy.calls.mostRecent().args[1]!.queryParams);
|
||||
})));
|
||||
});
|
||||
|
||||
|
@ -7,8 +7,8 @@
|
||||
*/
|
||||
|
||||
import {isPlatformBrowser} from '@angular/common';
|
||||
import {APP_INITIALIZER, ApplicationRef, InjectionToken, Injector, ModuleWithProviders, NgModule, PLATFORM_ID} from '@angular/core';
|
||||
import {Observable, of } from 'rxjs';
|
||||
import {APP_INITIALIZER, ApplicationRef, InjectionToken, Injector, ModuleWithProviders, NgModule, NgZone, PLATFORM_ID} from '@angular/core';
|
||||
import {Observable, merge, of } from 'rxjs';
|
||||
import {delay, filter, take} from 'rxjs/operators';
|
||||
|
||||
import {NgswCommChannel} from './low_level';
|
||||
@ -55,8 +55,12 @@ export abstract class SwRegistrationOptions {
|
||||
* registered (e.g. there might be a long-running timeout or polling interval, preventing the app
|
||||
* to stabilize). The available option are:
|
||||
*
|
||||
* - `registerWhenStable`: Register as soon as the application stabilizes (no pending
|
||||
* micro-/macro-tasks).
|
||||
* - `registerWhenStable:<timeout>`: Register as soon as the application stabilizes (no pending
|
||||
* micro-/macro-tasks) but no later than `<timeout>` milliseconds. If the app hasn't
|
||||
* stabilized after `<timeout>` milliseconds (for example, due to a recurrent asynchronous
|
||||
* task), the ServiceWorker will be registered anyway.
|
||||
* If `<timeout>` is omitted, the ServiceWorker will only be registered once the app
|
||||
* stabilizes.
|
||||
* - `registerImmediately`: Register immediately.
|
||||
* - `registerWithDelay:<timeout>`: Register with a delay of `<timeout>` milliseconds. For
|
||||
* example, use `registerWithDelay:5000` to register the ServiceWorker after 5 seconds. If
|
||||
@ -96,17 +100,19 @@ export function ngswAppInitializer(
|
||||
if (typeof options.registrationStrategy === 'function') {
|
||||
readyToRegister$ = options.registrationStrategy();
|
||||
} else {
|
||||
const [strategy, ...args] = (options.registrationStrategy || 'registerWhenStable').split(':');
|
||||
const [strategy, ...args] =
|
||||
(options.registrationStrategy || 'registerWhenStable:30000').split(':');
|
||||
|
||||
switch (strategy) {
|
||||
case 'registerImmediately':
|
||||
readyToRegister$ = of (null);
|
||||
break;
|
||||
case 'registerWithDelay':
|
||||
readyToRegister$ = of (null).pipe(delay(+args[0] || 0));
|
||||
readyToRegister$ = delayWithTimeout(+args[0] || 0);
|
||||
break;
|
||||
case 'registerWhenStable':
|
||||
const appRef = injector.get<ApplicationRef>(ApplicationRef);
|
||||
readyToRegister$ = appRef.isStable.pipe(filter(stable => stable));
|
||||
readyToRegister$ = !args[0] ? whenStable(injector) :
|
||||
merge(whenStable(injector), delayWithTimeout(+args[0]));
|
||||
break;
|
||||
default:
|
||||
// Unknown strategy.
|
||||
@ -116,14 +122,28 @@ export function ngswAppInitializer(
|
||||
}
|
||||
|
||||
// Don't return anything to avoid blocking the application until the SW is registered.
|
||||
// Also, run outside the Angular zone to avoid preventing the app from stabilizing (especially
|
||||
// given that some registration strategies wait for the app to stabilize).
|
||||
// Catch and log the error if SW registration fails to avoid uncaught rejection warning.
|
||||
readyToRegister$.pipe(take(1)).subscribe(
|
||||
() => navigator.serviceWorker.register(script, {scope: options.scope})
|
||||
.catch(err => console.error('Service worker registration failed with:', err)));
|
||||
const ngZone = injector.get(NgZone);
|
||||
ngZone.runOutsideAngular(
|
||||
() => readyToRegister$.pipe(take(1)).subscribe(
|
||||
() =>
|
||||
navigator.serviceWorker.register(script, {scope: options.scope})
|
||||
.catch(err => console.error('Service worker registration failed with:', err))));
|
||||
};
|
||||
return initializer;
|
||||
}
|
||||
|
||||
function delayWithTimeout(timeout: number): Observable<unknown> {
|
||||
return of (null).pipe(delay(timeout));
|
||||
}
|
||||
|
||||
function whenStable(injector: Injector): Observable<unknown> {
|
||||
const appRef = injector.get(ApplicationRef);
|
||||
return appRef.isStable.pipe(filter(stable => stable));
|
||||
}
|
||||
|
||||
export function ngswCommChannelFactory(
|
||||
opts: SwRegistrationOptions, platformId: string): NgswCommChannel {
|
||||
return new NgswCommChannel(
|
||||
|
@ -9,7 +9,7 @@
|
||||
import {PLATFORM_ID} from '@angular/core';
|
||||
import {TestBed} from '@angular/core/testing';
|
||||
import {NgswCommChannel} from '@angular/service-worker/src/low_level';
|
||||
import {SwRegistrationOptions, ngswCommChannelFactory} from '@angular/service-worker/src/module';
|
||||
import {ngswCommChannelFactory, SwRegistrationOptions} from '@angular/service-worker/src/module';
|
||||
import {SwPush} from '@angular/service-worker/src/push';
|
||||
import {SwUpdate} from '@angular/service-worker/src/update';
|
||||
import {MockPushManager, MockPushSubscription, MockServiceWorkerContainer, MockServiceWorkerRegistration, patchDecodeBase64} from '@angular/service-worker/testing/mock';
|
||||
@ -32,14 +32,18 @@ import {MockPushManager, MockPushSubscription, MockServiceWorkerContainer, MockS
|
||||
|
||||
mock.setupSw();
|
||||
|
||||
(comm as any).registration.subscribe((reg: any) => { done(); });
|
||||
(comm as any).registration.subscribe((reg: any) => {
|
||||
done();
|
||||
});
|
||||
});
|
||||
it('can access the registration when it comes after subscription', done => {
|
||||
const mock = new MockServiceWorkerContainer();
|
||||
const comm = new NgswCommChannel(mock as any);
|
||||
const regPromise = mock.getRegistration() as any as MockServiceWorkerRegistration;
|
||||
|
||||
(comm as any).registration.subscribe((reg: any) => { done(); });
|
||||
(comm as any).registration.subscribe((reg: any) => {
|
||||
done();
|
||||
});
|
||||
|
||||
mock.setupSw();
|
||||
});
|
||||
@ -158,7 +162,7 @@ import {MockPushManager, MockPushSubscription, MockServiceWorkerContainer, MockS
|
||||
});
|
||||
|
||||
describe('requestSubscription()', () => {
|
||||
it('returns a promise that resolves to the subscription', async() => {
|
||||
it('returns a promise that resolves to the subscription', async () => {
|
||||
const promise = push.requestSubscription({serverPublicKey: 'test'});
|
||||
expect(promise).toEqual(jasmine.any(Promise));
|
||||
|
||||
@ -166,7 +170,7 @@ import {MockPushManager, MockPushSubscription, MockServiceWorkerContainer, MockS
|
||||
expect(sub).toEqual(jasmine.any(MockPushSubscription));
|
||||
});
|
||||
|
||||
it('calls `PushManager.subscribe()` (with appropriate options)', async() => {
|
||||
it('calls `PushManager.subscribe()` (with appropriate options)', async () => {
|
||||
const decode = (charCodeArr: Uint8Array) =>
|
||||
Array.from(charCodeArr).map(c => String.fromCharCode(c)).join('');
|
||||
|
||||
@ -179,16 +183,16 @@ import {MockPushManager, MockPushSubscription, MockServiceWorkerContainer, MockS
|
||||
|
||||
expect(pmSubscribeSpy).toHaveBeenCalledTimes(1);
|
||||
expect(pmSubscribeSpy).toHaveBeenCalledWith({
|
||||
applicationServerKey: jasmine.any(Uint8Array),
|
||||
applicationServerKey: jasmine.any(Uint8Array) as any,
|
||||
userVisibleOnly: true,
|
||||
});
|
||||
|
||||
const actualAppServerKey = pmSubscribeSpy.calls.first().args[0].applicationServerKey;
|
||||
const actualAppServerKeyStr = decode(actualAppServerKey);
|
||||
const actualAppServerKey = pmSubscribeSpy.calls.first().args[0]!.applicationServerKey;
|
||||
const actualAppServerKeyStr = decode(actualAppServerKey as Uint8Array);
|
||||
expect(actualAppServerKeyStr).toBe(appServerKeyStr);
|
||||
});
|
||||
|
||||
it('emits the new `PushSubscription` on `SwPush.subscription`', async() => {
|
||||
it('emits the new `PushSubscription` on `SwPush.subscription`', async () => {
|
||||
const subscriptionSpy = jasmine.createSpy('subscriptionSpy');
|
||||
push.subscription.subscribe(subscriptionSpy);
|
||||
const sub = await push.requestSubscription({serverPublicKey: 'test'});
|
||||
@ -204,7 +208,7 @@ import {MockPushManager, MockPushSubscription, MockServiceWorkerContainer, MockS
|
||||
psUnsubscribeSpy = spyOn(MockPushSubscription.prototype, 'unsubscribe').and.callThrough();
|
||||
});
|
||||
|
||||
it('rejects if currently not subscribed to push notifications', async() => {
|
||||
it('rejects if currently not subscribed to push notifications', async () => {
|
||||
try {
|
||||
await push.unsubscribe();
|
||||
throw new Error('`unsubscribe()` should fail');
|
||||
@ -213,15 +217,17 @@ import {MockPushManager, MockPushSubscription, MockServiceWorkerContainer, MockS
|
||||
}
|
||||
});
|
||||
|
||||
it('calls `PushSubscription.unsubscribe()`', async() => {
|
||||
it('calls `PushSubscription.unsubscribe()`', async () => {
|
||||
await push.requestSubscription({serverPublicKey: 'test'});
|
||||
await push.unsubscribe();
|
||||
|
||||
expect(psUnsubscribeSpy).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('rejects if `PushSubscription.unsubscribe()` fails', async() => {
|
||||
psUnsubscribeSpy.and.callFake(() => { throw new Error('foo'); });
|
||||
it('rejects if `PushSubscription.unsubscribe()` fails', async () => {
|
||||
psUnsubscribeSpy.and.callFake(() => {
|
||||
throw new Error('foo');
|
||||
});
|
||||
|
||||
try {
|
||||
await push.requestSubscription({serverPublicKey: 'test'});
|
||||
@ -232,7 +238,7 @@ import {MockPushManager, MockPushSubscription, MockServiceWorkerContainer, MockS
|
||||
}
|
||||
});
|
||||
|
||||
it('rejects if `PushSubscription.unsubscribe()` returns false', async() => {
|
||||
it('rejects if `PushSubscription.unsubscribe()` returns false', async () => {
|
||||
psUnsubscribeSpy.and.returnValue(Promise.resolve(false));
|
||||
|
||||
try {
|
||||
@ -244,7 +250,7 @@ import {MockPushManager, MockPushSubscription, MockServiceWorkerContainer, MockS
|
||||
}
|
||||
});
|
||||
|
||||
it('emits `null` on `SwPush.subscription`', async() => {
|
||||
it('emits `null` on `SwPush.subscription`', async () => {
|
||||
const subscriptionSpy = jasmine.createSpy('subscriptionSpy');
|
||||
push.subscription.subscribe(subscriptionSpy);
|
||||
|
||||
@ -254,7 +260,7 @@ import {MockPushManager, MockPushSubscription, MockServiceWorkerContainer, MockS
|
||||
expect(subscriptionSpy).toHaveBeenCalledWith(null);
|
||||
});
|
||||
|
||||
it('does not emit on `SwPush.subscription` on failure', async() => {
|
||||
it('does not emit on `SwPush.subscription` on failure', async () => {
|
||||
const subscriptionSpy = jasmine.createSpy('subscriptionSpy');
|
||||
const initialSubEmit = new Promise(resolve => subscriptionSpy.and.callFake(resolve));
|
||||
|
||||
@ -271,7 +277,9 @@ import {MockPushManager, MockPushSubscription, MockServiceWorkerContainer, MockS
|
||||
subscriptionSpy.calls.reset();
|
||||
|
||||
// Error due to `PushSubscription.unsubscribe()` error.
|
||||
psUnsubscribeSpy.and.callFake(() => { throw new Error('foo'); });
|
||||
psUnsubscribeSpy.and.callFake(() => {
|
||||
throw new Error('foo');
|
||||
});
|
||||
await push.unsubscribe().catch(() => undefined);
|
||||
expect(subscriptionSpy).not.toHaveBeenCalled();
|
||||
|
||||
@ -338,7 +346,7 @@ import {MockPushManager, MockPushSubscription, MockServiceWorkerContainer, MockS
|
||||
push.subscription.subscribe(subscriptionSpy);
|
||||
});
|
||||
|
||||
it('emits on worker-driven changes (i.e. when the controller changes)', async() => {
|
||||
it('emits on worker-driven changes (i.e. when the controller changes)', async () => {
|
||||
// Initial emit for the current `ServiceWorkerController`.
|
||||
await nextSubEmitPromise;
|
||||
expect(subscriptionSpy).toHaveBeenCalledTimes(1);
|
||||
@ -353,7 +361,7 @@ import {MockPushManager, MockPushSubscription, MockServiceWorkerContainer, MockS
|
||||
expect(subscriptionSpy).toHaveBeenCalledWith(null);
|
||||
});
|
||||
|
||||
it('emits on subscription changes (i.e. when subscribing/unsubscribing)', async() => {
|
||||
it('emits on subscription changes (i.e. when subscribing/unsubscribing)', async () => {
|
||||
await nextSubEmitPromise;
|
||||
subscriptionSpy.calls.reset();
|
||||
|
||||
@ -391,11 +399,16 @@ import {MockPushManager, MockPushSubscription, MockServiceWorkerContainer, MockS
|
||||
});
|
||||
|
||||
it('gives an error when registering', done => {
|
||||
push.requestSubscription({serverPublicKey: 'test'}).catch(err => { done(); });
|
||||
push.requestSubscription({serverPublicKey: 'test'}).catch(err => {
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('gives an error when unsubscribing',
|
||||
done => { push.unsubscribe().catch(err => { done(); }); });
|
||||
it('gives an error when unsubscribing', done => {
|
||||
push.unsubscribe().catch(err => {
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@ -461,7 +474,9 @@ import {MockPushManager, MockPushSubscription, MockServiceWorkerContainer, MockS
|
||||
});
|
||||
});
|
||||
return update.activateUpdate()
|
||||
.catch(err => { expect(err.message).toEqual('Failed to activate'); })
|
||||
.catch(err => {
|
||||
expect(err.message).toEqual('Failed to activate');
|
||||
})
|
||||
.then(() => done())
|
||||
.catch(err => done.fail(err));
|
||||
});
|
||||
@ -475,8 +490,12 @@ import {MockPushManager, MockPushSubscription, MockServiceWorkerContainer, MockS
|
||||
expect(() => TestBed.inject(SwUpdate)).not.toThrow();
|
||||
});
|
||||
describe('with no SW', () => {
|
||||
beforeEach(() => { comm = new NgswCommChannel(undefined); });
|
||||
it('can be instantiated', () => { update = new SwUpdate(comm); });
|
||||
beforeEach(() => {
|
||||
comm = new NgswCommChannel(undefined);
|
||||
});
|
||||
it('can be instantiated', () => {
|
||||
update = new SwUpdate(comm);
|
||||
});
|
||||
it('does not crash on subscription to observables', () => {
|
||||
update = new SwUpdate(comm);
|
||||
update.available.toPromise().catch(err => fail(err));
|
||||
@ -484,11 +503,15 @@ import {MockPushManager, MockPushSubscription, MockServiceWorkerContainer, MockS
|
||||
});
|
||||
it('gives an error when checking for updates', done => {
|
||||
update = new SwUpdate(comm);
|
||||
update.checkForUpdate().catch(err => { done(); });
|
||||
update.checkForUpdate().catch(err => {
|
||||
done();
|
||||
});
|
||||
});
|
||||
it('gives an error when activating updates', done => {
|
||||
update = new SwUpdate(comm);
|
||||
update.activateUpdate().catch(err => { done(); });
|
||||
update.activateUpdate().catch(err => {
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -7,7 +7,7 @@
|
||||
*/
|
||||
|
||||
import {ApplicationRef, PLATFORM_ID} from '@angular/core';
|
||||
import {TestBed, fakeAsync, flushMicrotasks, tick} from '@angular/core/testing';
|
||||
import {fakeAsync, flushMicrotasks, TestBed, tick} from '@angular/core/testing';
|
||||
import {Subject} from 'rxjs';
|
||||
import {filter, take} from 'rxjs/operators';
|
||||
|
||||
@ -30,10 +30,10 @@ describe('ServiceWorkerModule', () => {
|
||||
|
||||
beforeEach(
|
||||
() => swRegisterSpy =
|
||||
spyOn(navigator.serviceWorker, 'register').and.returnValue(Promise.resolve()));
|
||||
spyOn(navigator.serviceWorker, 'register').and.returnValue(Promise.resolve(null as any)));
|
||||
|
||||
describe('register()', () => {
|
||||
const configTestBed = async(opts: SwRegistrationOptions) => {
|
||||
const configTestBed = async (opts: SwRegistrationOptions) => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [ServiceWorkerModule.register('sw.js', opts)],
|
||||
providers: [{provide: PLATFORM_ID, useValue: 'browser'}],
|
||||
@ -42,35 +42,35 @@ describe('ServiceWorkerModule', () => {
|
||||
await untilStable();
|
||||
};
|
||||
|
||||
it('sets the registration options', async() => {
|
||||
it('sets the registration options', async () => {
|
||||
await configTestBed({enabled: true, scope: 'foo'});
|
||||
|
||||
expect(TestBed.inject(SwRegistrationOptions)).toEqual({enabled: true, scope: 'foo'});
|
||||
expect(swRegisterSpy).toHaveBeenCalledWith('sw.js', {scope: 'foo'});
|
||||
});
|
||||
|
||||
it('can disable the SW', async() => {
|
||||
it('can disable the SW', async () => {
|
||||
await configTestBed({enabled: false});
|
||||
|
||||
expect(TestBed.inject(SwUpdate).isEnabled).toBe(false);
|
||||
expect(swRegisterSpy).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('can enable the SW', async() => {
|
||||
it('can enable the SW', async () => {
|
||||
await configTestBed({enabled: true});
|
||||
|
||||
expect(TestBed.inject(SwUpdate).isEnabled).toBe(true);
|
||||
expect(swRegisterSpy).toHaveBeenCalledWith('sw.js', {scope: undefined});
|
||||
});
|
||||
|
||||
it('defaults to enabling the SW', async() => {
|
||||
it('defaults to enabling the SW', async () => {
|
||||
await configTestBed({});
|
||||
|
||||
expect(TestBed.inject(SwUpdate).isEnabled).toBe(true);
|
||||
expect(swRegisterSpy).toHaveBeenCalledWith('sw.js', {scope: undefined});
|
||||
});
|
||||
|
||||
it('catches and a logs registration errors', async() => {
|
||||
it('catches and a logs registration errors', async () => {
|
||||
const consoleErrorSpy = spyOn(console, 'error');
|
||||
swRegisterSpy.and.returnValue(Promise.reject('no reason'));
|
||||
|
||||
@ -92,7 +92,7 @@ describe('ServiceWorkerModule', () => {
|
||||
});
|
||||
};
|
||||
|
||||
it('sets the registration options (and overwrites those set via `.register()`', async() => {
|
||||
it('sets the registration options (and overwrites those set via `.register()`', async () => {
|
||||
configTestBed({enabled: true, scope: 'provider'});
|
||||
await untilStable();
|
||||
|
||||
@ -100,7 +100,7 @@ describe('ServiceWorkerModule', () => {
|
||||
expect(swRegisterSpy).toHaveBeenCalledWith('sw.js', {scope: 'provider'});
|
||||
});
|
||||
|
||||
it('can disable the SW', async() => {
|
||||
it('can disable the SW', async () => {
|
||||
configTestBed({enabled: false}, {enabled: true});
|
||||
await untilStable();
|
||||
|
||||
@ -108,7 +108,7 @@ describe('ServiceWorkerModule', () => {
|
||||
expect(swRegisterSpy).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('can enable the SW', async() => {
|
||||
it('can enable the SW', async () => {
|
||||
configTestBed({enabled: true}, {enabled: false});
|
||||
await untilStable();
|
||||
|
||||
@ -116,7 +116,7 @@ describe('ServiceWorkerModule', () => {
|
||||
expect(swRegisterSpy).toHaveBeenCalledWith('sw.js', {scope: undefined});
|
||||
});
|
||||
|
||||
it('defaults to enabling the SW', async() => {
|
||||
it('defaults to enabling the SW', async () => {
|
||||
configTestBed({}, {enabled: false});
|
||||
await untilStable();
|
||||
|
||||
@ -141,13 +141,13 @@ describe('ServiceWorkerModule', () => {
|
||||
],
|
||||
});
|
||||
|
||||
// Dummy `get()` call to initialize the test "app".
|
||||
// Dummy `inject()` call to initialize the test "app".
|
||||
TestBed.inject(ApplicationRef);
|
||||
|
||||
return isStableSub;
|
||||
};
|
||||
|
||||
it('defaults to registering the SW when the app stabilizes', fakeAsync(() => {
|
||||
it('defaults to registering the SW when the app stabilizes (under 30s)', fakeAsync(() => {
|
||||
const isStableSub = configTestBedWithMockedStability();
|
||||
|
||||
isStableSub.next(false);
|
||||
@ -156,13 +156,91 @@ describe('ServiceWorkerModule', () => {
|
||||
tick();
|
||||
expect(swRegisterSpy).not.toHaveBeenCalled();
|
||||
|
||||
tick(20000);
|
||||
expect(swRegisterSpy).not.toHaveBeenCalled();
|
||||
|
||||
isStableSub.next(true);
|
||||
|
||||
tick();
|
||||
expect(swRegisterSpy).toHaveBeenCalledWith('sw.js', {scope: undefined});
|
||||
}));
|
||||
|
||||
it('registers the SW when the app stabilizes with `registerWhenStable`', fakeAsync(() => {
|
||||
it('defaults to registering the SW after 30s if the app does not stabilize sooner',
|
||||
fakeAsync(() => {
|
||||
const isStableSub = configTestBedWithMockedStability();
|
||||
|
||||
tick(29999);
|
||||
expect(swRegisterSpy).not.toHaveBeenCalled();
|
||||
|
||||
tick(1);
|
||||
expect(swRegisterSpy).toHaveBeenCalledWith('sw.js', {scope: undefined});
|
||||
}));
|
||||
|
||||
it('registers the SW when the app stabilizes with `registerWhenStable:<timeout>`',
|
||||
fakeAsync(() => {
|
||||
const isStableSub = configTestBedWithMockedStability('registerWhenStable:1000');
|
||||
|
||||
isStableSub.next(false);
|
||||
isStableSub.next(false);
|
||||
|
||||
tick();
|
||||
expect(swRegisterSpy).not.toHaveBeenCalled();
|
||||
|
||||
tick(500);
|
||||
expect(swRegisterSpy).not.toHaveBeenCalled();
|
||||
|
||||
isStableSub.next(true);
|
||||
|
||||
tick();
|
||||
expect(swRegisterSpy).toHaveBeenCalledWith('sw.js', {scope: undefined});
|
||||
}));
|
||||
|
||||
it('registers the SW after `timeout` if the app does not stabilize with `registerWhenStable:<timeout>`',
|
||||
fakeAsync(() => {
|
||||
configTestBedWithMockedStability('registerWhenStable:1000');
|
||||
|
||||
tick(999);
|
||||
expect(swRegisterSpy).not.toHaveBeenCalled();
|
||||
|
||||
tick(1);
|
||||
expect(swRegisterSpy).toHaveBeenCalledWith('sw.js', {scope: undefined});
|
||||
}));
|
||||
|
||||
it('registers the SW asap (asynchronously) before the app stabilizes with `registerWhenStable:0`',
|
||||
fakeAsync(() => {
|
||||
const isStableSub = configTestBedWithMockedStability('registerWhenStable:0');
|
||||
|
||||
// Create a microtask.
|
||||
Promise.resolve();
|
||||
|
||||
flushMicrotasks();
|
||||
expect(swRegisterSpy).not.toHaveBeenCalled();
|
||||
|
||||
tick(0);
|
||||
expect(swRegisterSpy).toHaveBeenCalledWith('sw.js', {scope: undefined});
|
||||
}));
|
||||
|
||||
it('registers the SW only when the app stabilizes with `registerWhenStable:`',
|
||||
fakeAsync(() => {
|
||||
const isStableSub = configTestBedWithMockedStability('registerWhenStable:');
|
||||
|
||||
isStableSub.next(false);
|
||||
isStableSub.next(false);
|
||||
|
||||
tick();
|
||||
expect(swRegisterSpy).not.toHaveBeenCalled();
|
||||
|
||||
tick(60000);
|
||||
expect(swRegisterSpy).not.toHaveBeenCalled();
|
||||
|
||||
isStableSub.next(true);
|
||||
|
||||
tick();
|
||||
expect(swRegisterSpy).toHaveBeenCalledWith('sw.js', {scope: undefined});
|
||||
}));
|
||||
|
||||
it('registers the SW only when the app stabilizes with `registerWhenStable`',
|
||||
fakeAsync(() => {
|
||||
const isStableSub = configTestBedWithMockedStability('registerWhenStable');
|
||||
|
||||
isStableSub.next(false);
|
||||
@ -171,6 +249,9 @@ describe('ServiceWorkerModule', () => {
|
||||
tick();
|
||||
expect(swRegisterSpy).not.toHaveBeenCalled();
|
||||
|
||||
tick(60000);
|
||||
expect(swRegisterSpy).not.toHaveBeenCalled();
|
||||
|
||||
isStableSub.next(true);
|
||||
|
||||
tick();
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -6,7 +6,7 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {ChangeDetectorRef, Component, EventEmitter, Input, NO_ERRORS_SCHEMA, NgModule, NgModuleFactory, NgZone, OnChanges, OnDestroy, Output, SimpleChange, SimpleChanges, Testability, destroyPlatform, forwardRef} from '@angular/core';
|
||||
import {ChangeDetectorRef, Component, destroyPlatform, EventEmitter, forwardRef, Input, NgModule, NgModuleFactory, NgZone, NO_ERRORS_SCHEMA, OnChanges, OnDestroy, Output, SimpleChange, SimpleChanges, Testability} from '@angular/core';
|
||||
import {async, fakeAsync, flushMicrotasks, tick} from '@angular/core/testing';
|
||||
import {BrowserModule} from '@angular/platform-browser';
|
||||
import {platformBrowserDynamic} from '@angular/platform-browser-dynamic';
|
||||
@ -14,7 +14,6 @@ import {platformBrowserDynamic} from '@angular/platform-browser-dynamic';
|
||||
import * as angular from '../../common/src/angular1';
|
||||
import {$EXCEPTION_HANDLER, $ROOT_SCOPE} from '../../common/src/constants';
|
||||
import {html, multiTrim, withEachNg1Version} from '../../common/test/helpers/common_test_helpers';
|
||||
|
||||
import {UpgradeAdapter, UpgradeAdapterRef} from '../src/upgrade_adapter';
|
||||
|
||||
|
||||
@ -24,7 +23,6 @@ declare global {
|
||||
|
||||
withEachNg1Version(() => {
|
||||
describe('adapter: ng1 to ng2', () => {
|
||||
|
||||
beforeEach(() => destroyPlatform());
|
||||
afterEach(() => destroyPlatform());
|
||||
|
||||
@ -113,11 +111,12 @@ withEachNg1Version(() => {
|
||||
ng1Module.directive('ng2', adapter.downgradeNg2Component(Ng2));
|
||||
adapter.bootstrap(element, ['ng1']).ready((ref) => {
|
||||
expect(platformRef.bootstrapModule).toHaveBeenCalledWith(jasmine.any(Function), [
|
||||
{providers: []}, jasmine.any(Object)
|
||||
{providers: []}, jasmine.any(Object) as any
|
||||
]);
|
||||
expect(platformRef.bootstrapModuleFactory)
|
||||
.toHaveBeenCalledWith(
|
||||
jasmine.any(NgModuleFactory), {providers: [], ngZone: jasmine.any(NgZone)});
|
||||
jasmine.any(NgModuleFactory),
|
||||
jasmine.objectContaining({ngZone: jasmine.any(NgZone), providers: []}));
|
||||
ref.dispose();
|
||||
});
|
||||
}));
|
||||
@ -232,7 +231,9 @@ withEachNg1Version(() => {
|
||||
})
|
||||
class Ng2 {
|
||||
l: any;
|
||||
constructor() { this.l = l; }
|
||||
constructor() {
|
||||
this.l = l;
|
||||
}
|
||||
}
|
||||
|
||||
@NgModule({
|
||||
@ -262,7 +263,9 @@ withEachNg1Version(() => {
|
||||
@Component({selector: 'my-app', template: '<my-child [value]="value"></my-child>'})
|
||||
class AppComponent {
|
||||
value?: number;
|
||||
constructor() { appComponent = this; }
|
||||
constructor() {
|
||||
appComponent = this;
|
||||
}
|
||||
}
|
||||
|
||||
@Component({
|
||||
@ -272,7 +275,9 @@ withEachNg1Version(() => {
|
||||
class ChildComponent {
|
||||
valueFromPromise?: number;
|
||||
@Input()
|
||||
set value(v: number) { expect(NgZone.isInAngularZone()).toBe(true); }
|
||||
set value(v: number) {
|
||||
expect(NgZone.isInAngularZone()).toBe(true);
|
||||
}
|
||||
|
||||
constructor(private zone: NgZone) {}
|
||||
|
||||
@ -352,14 +357,15 @@ withEachNg1Version(() => {
|
||||
|
||||
const element = html('<ng2></ng2>');
|
||||
adapter.bootstrap(element, ['ng1']).ready((ref) => {
|
||||
expect(multiTrim(document.body.textContent !)).toBe('It works');
|
||||
expect(multiTrim(document.body.textContent!)).toBe('It works');
|
||||
});
|
||||
}));
|
||||
|
||||
it('should bind properties, events', async(() => {
|
||||
const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module));
|
||||
const ng1Module =
|
||||
angular.module_('ng1', []).value($EXCEPTION_HANDLER, (err: any) => { throw err; });
|
||||
const ng1Module = angular.module_('ng1', []).value($EXCEPTION_HANDLER, (err: any) => {
|
||||
throw err;
|
||||
});
|
||||
|
||||
ng1Module.run(($rootScope: any) => {
|
||||
$rootScope.name = 'world';
|
||||
@ -409,8 +415,8 @@ withEachNg1Version(() => {
|
||||
}
|
||||
const actValue = changes[prop].currentValue;
|
||||
if (actValue != value) {
|
||||
throw new Error(
|
||||
`Expected changes record for'${prop}' to be '${value}' but was '${actValue}'`);
|
||||
throw new Error(`Expected changes record for'${prop}' to be '${
|
||||
value}' but was '${actValue}'`);
|
||||
}
|
||||
};
|
||||
|
||||
@ -458,7 +464,7 @@ withEachNg1Version(() => {
|
||||
| modelA: {{modelA}}; modelB: {{modelB}}; eventA: {{eventA}}; eventB: {{eventB}};
|
||||
</div>`);
|
||||
adapter.bootstrap(element, ['ng1']).ready((ref) => {
|
||||
expect(multiTrim(document.body.textContent !))
|
||||
expect(multiTrim(document.body.textContent!))
|
||||
.toEqual(
|
||||
'ignore: -; ' +
|
||||
'literal: Text; interpolate: Hello world; ' +
|
||||
@ -466,7 +472,7 @@ withEachNg1Version(() => {
|
||||
'modelA: newA; modelB: newB; eventA: aFired; eventB: bFired;');
|
||||
|
||||
ref.ng1RootScope.$apply('name = "everyone"');
|
||||
expect(multiTrim(document.body.textContent !))
|
||||
expect(multiTrim(document.body.textContent!))
|
||||
.toEqual(
|
||||
'ignore: -; ' +
|
||||
'literal: Text; interpolate: Hello everyone; ' +
|
||||
@ -475,7 +481,6 @@ withEachNg1Version(() => {
|
||||
|
||||
ref.dispose();
|
||||
});
|
||||
|
||||
}));
|
||||
|
||||
it('should support two-way binding and event listener', async(() => {
|
||||
@ -541,9 +546,9 @@ withEachNg1Version(() => {
|
||||
ngOnChangesCount = 0;
|
||||
firstChangesCount = 0;
|
||||
// TODO(issue/24571): remove '!'.
|
||||
initialValue !: string;
|
||||
initialValue!: string;
|
||||
// TODO(issue/24571): remove '!'.
|
||||
@Input() foo !: string;
|
||||
@Input() foo!: string;
|
||||
|
||||
ngOnChanges(changes: SimpleChanges) {
|
||||
this.ngOnChangesCount++;
|
||||
@ -590,7 +595,9 @@ withEachNg1Version(() => {
|
||||
const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module));
|
||||
const ng1Module = angular.module_('ng1', []);
|
||||
|
||||
ng1Module.run(($rootScope: any /** TODO #9100 */) => { $rootScope.modelA = 'A'; });
|
||||
ng1Module.run(($rootScope: any /** TODO #9100 */) => {
|
||||
$rootScope.modelA = 'A';
|
||||
});
|
||||
|
||||
let ng2Instance: Ng2;
|
||||
@Component({selector: 'ng2', template: '{{_value}}'})
|
||||
@ -598,11 +605,21 @@ withEachNg1Version(() => {
|
||||
private _value: any = '';
|
||||
private _onChangeCallback: (_: any) => void = () => {};
|
||||
private _onTouchedCallback: () => void = () => {};
|
||||
constructor() { ng2Instance = this; }
|
||||
writeValue(value: any) { this._value = value; }
|
||||
registerOnChange(fn: any) { this._onChangeCallback = fn; }
|
||||
registerOnTouched(fn: any) { this._onTouchedCallback = fn; }
|
||||
doTouch() { this._onTouchedCallback(); }
|
||||
constructor() {
|
||||
ng2Instance = this;
|
||||
}
|
||||
writeValue(value: any) {
|
||||
this._value = value;
|
||||
}
|
||||
registerOnChange(fn: any) {
|
||||
this._onChangeCallback = fn;
|
||||
}
|
||||
registerOnTouched(fn: any) {
|
||||
this._onTouchedCallback = fn;
|
||||
}
|
||||
doTouch() {
|
||||
this._onTouchedCallback();
|
||||
}
|
||||
doChange(newValue: string) {
|
||||
this._value = newValue;
|
||||
this._onChangeCallback(newValue);
|
||||
@ -653,14 +670,18 @@ withEachNg1Version(() => {
|
||||
return {
|
||||
template: '<div ng-if="!destroyIt"><ng2></ng2></div>',
|
||||
controller: function($rootScope: any, $timeout: Function) {
|
||||
$timeout(() => { $rootScope.destroyIt = true; });
|
||||
$timeout(() => {
|
||||
$rootScope.destroyIt = true;
|
||||
});
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
@Component({selector: 'ng2', template: 'test'})
|
||||
class Ng2 {
|
||||
ngOnDestroy() { onDestroyed.emit('destroyed'); }
|
||||
ngOnDestroy() {
|
||||
onDestroyed.emit('destroyed');
|
||||
}
|
||||
}
|
||||
|
||||
@NgModule({
|
||||
@ -673,7 +694,9 @@ withEachNg1Version(() => {
|
||||
ng1Module.directive('ng2', adapter.downgradeNg2Component(Ng2));
|
||||
const element = html('<ng1></ng1>');
|
||||
adapter.bootstrap(element, ['ng1']).ready((ref) => {
|
||||
onDestroyed.subscribe(() => { ref.dispose(); });
|
||||
onDestroyed.subscribe(() => {
|
||||
ref.dispose();
|
||||
});
|
||||
});
|
||||
}));
|
||||
|
||||
@ -689,7 +712,9 @@ withEachNg1Version(() => {
|
||||
|
||||
@Component({selector: 'ng2-inner', template: 'test'})
|
||||
class Ng2InnerComponent implements OnDestroy {
|
||||
ngOnDestroy() { destroyed = true; }
|
||||
ngOnDestroy() {
|
||||
destroyed = true;
|
||||
}
|
||||
}
|
||||
|
||||
@NgModule({
|
||||
@ -789,7 +814,7 @@ withEachNg1Version(() => {
|
||||
@Component({selector: 'ng2', template: 'ng2-{{ itemId }}(<ng-content></ng-content>)'})
|
||||
class Ng2Component {
|
||||
// TODO(issue/24571): remove '!'.
|
||||
@Input() itemId !: string;
|
||||
@Input() itemId!: string;
|
||||
}
|
||||
|
||||
@NgModule({imports: [BrowserModule], declarations: [Ng2Component]})
|
||||
@ -838,7 +863,7 @@ withEachNg1Version(() => {
|
||||
ng1Module.directive('rootComponent', adapter.downgradeNg2Component(RootComponent));
|
||||
|
||||
document.body.innerHTML = '<root-component></root-component>';
|
||||
adapter.bootstrap(document.body.firstElementChild !, ['myExample']).ready((ref) => {
|
||||
adapter.bootstrap(document.body.firstElementChild!, ['myExample']).ready((ref) => {
|
||||
expect(multiTrim(document.body.textContent)).toEqual('It works!');
|
||||
ref.dispose();
|
||||
});
|
||||
@ -868,7 +893,9 @@ withEachNg1Version(() => {
|
||||
dataA = 'foo';
|
||||
dataB = 'bar';
|
||||
|
||||
constructor() { ng2ComponentInstance = this; }
|
||||
constructor() {
|
||||
ng2ComponentInstance = this;
|
||||
}
|
||||
}
|
||||
|
||||
// Define `ng1Module`
|
||||
@ -888,8 +915,8 @@ withEachNg1Version(() => {
|
||||
const element = html(`<ng2></ng2>`);
|
||||
|
||||
adapter.bootstrap(element, ['ng1Module']).ready(ref => {
|
||||
const ng1 = element.querySelector('ng1') !;
|
||||
const ng1Controller = angular.element(ng1).controller !('ng1');
|
||||
const ng1 = element.querySelector('ng1')!;
|
||||
const ng1Controller = angular.element(ng1).controller!('ng1');
|
||||
|
||||
expect(multiTrim(element.textContent)).toBe('Inside: foo, bar | Outside: foo, bar');
|
||||
|
||||
@ -932,7 +959,9 @@ withEachNg1Version(() => {
|
||||
dataA = {value: 'foo'};
|
||||
dataB = {value: 'bar'};
|
||||
|
||||
constructor() { ng2ComponentInstance = this; }
|
||||
constructor() {
|
||||
ng2ComponentInstance = this;
|
||||
}
|
||||
}
|
||||
|
||||
// Define `ng1Module`
|
||||
@ -952,8 +981,8 @@ withEachNg1Version(() => {
|
||||
const element = html(`<ng2></ng2>`);
|
||||
|
||||
adapter.bootstrap(element, ['ng1Module']).ready(ref => {
|
||||
const ng1 = element.querySelector('ng1') !;
|
||||
const ng1Controller = angular.element(ng1).controller !('ng1');
|
||||
const ng1 = element.querySelector('ng1')!;
|
||||
const ng1Controller = angular.element(ng1).controller!('ng1');
|
||||
|
||||
expect(multiTrim(element.textContent)).toBe('Inside: foo, bar | Outside: foo, bar');
|
||||
|
||||
@ -996,7 +1025,9 @@ withEachNg1Version(() => {
|
||||
dataA = {value: 'foo'};
|
||||
dataB = {value: 'bar'};
|
||||
|
||||
constructor() { ng2ComponentInstance = this; }
|
||||
constructor() {
|
||||
ng2ComponentInstance = this;
|
||||
}
|
||||
}
|
||||
|
||||
// Define `ng1Module`
|
||||
@ -1016,8 +1047,8 @@ withEachNg1Version(() => {
|
||||
const element = html(`<ng2></ng2>`);
|
||||
|
||||
adapter.bootstrap(element, ['ng1Module']).ready(ref => {
|
||||
const ng1 = element.querySelector('ng1') !;
|
||||
const ng1Controller = angular.element(ng1).controller !('ng1');
|
||||
const ng1 = element.querySelector('ng1')!;
|
||||
const ng1Controller = angular.element(ng1).controller!('ng1');
|
||||
|
||||
expect(multiTrim(element.textContent)).toBe('Inside: foo, bar | Outside: foo, bar');
|
||||
|
||||
@ -1077,8 +1108,8 @@ withEachNg1Version(() => {
|
||||
const element = html(`<ng2></ng2>`);
|
||||
|
||||
adapter.bootstrap(element, ['ng1Module']).ready(ref => {
|
||||
const ng1 = element.querySelector('ng1') !;
|
||||
const ng1Controller = angular.element(ng1).controller !('ng1');
|
||||
const ng1 = element.querySelector('ng1')!;
|
||||
const ng1Controller = angular.element(ng1).controller!('ng1');
|
||||
|
||||
expect(multiTrim(element.textContent)).toBe('Inside: - | Outside: foo, bar');
|
||||
|
||||
@ -1204,7 +1235,9 @@ withEachNg1Version(() => {
|
||||
restrict: 'E',
|
||||
template: '{{someText}} - Length: {{data.length}}',
|
||||
scope: {data: '='},
|
||||
controller: function($scope: any) { $scope.someText = 'ng1 - Data: ' + $scope.data; }
|
||||
controller: function($scope: any) {
|
||||
$scope.someText = 'ng1 - Data: ' + $scope.data;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
@ -1248,7 +1281,9 @@ withEachNg1Version(() => {
|
||||
restrict: 'E',
|
||||
template: '{{someText}} - Length: {{data.length}}',
|
||||
scope: {data: '='},
|
||||
link: function($scope: any) { $scope.someText = 'ng1 - Data: ' + $scope.data; }
|
||||
link: function($scope: any) {
|
||||
$scope.someText = 'ng1 - Data: ' + $scope.data;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
@ -1291,7 +1326,9 @@ withEachNg1Version(() => {
|
||||
cbFn(200, `${method}:${url}`);
|
||||
});
|
||||
|
||||
const ng1 = () => { return {templateUrl: 'url.html'}; };
|
||||
const ng1 = () => {
|
||||
return {templateUrl: 'url.html'};
|
||||
};
|
||||
ng1Module.directive('ng1', ng1);
|
||||
@Component({selector: 'ng2', template: '<ng1></ng1>'})
|
||||
class Ng2 {
|
||||
@ -1320,7 +1357,13 @@ withEachNg1Version(() => {
|
||||
cbFn(200, `${method}:${url}`);
|
||||
});
|
||||
|
||||
const ng1 = () => { return {templateUrl() { return 'url.html'; }}; };
|
||||
const ng1 = () => {
|
||||
return {
|
||||
templateUrl() {
|
||||
return 'url.html';
|
||||
}
|
||||
};
|
||||
};
|
||||
ng1Module.directive('ng1', ng1);
|
||||
@Component({selector: 'ng2', template: '<ng1></ng1>'})
|
||||
class Ng2 {
|
||||
@ -1345,7 +1388,9 @@ withEachNg1Version(() => {
|
||||
const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module));
|
||||
const ng1Module = angular.module_('ng1', []);
|
||||
|
||||
const ng1 = () => { return {template: ''}; };
|
||||
const ng1 = () => {
|
||||
return {template: ''};
|
||||
};
|
||||
ng1Module.directive('ng1', ng1);
|
||||
|
||||
@Component({selector: 'ng2', template: '<ng1></ng1>'})
|
||||
@ -1371,7 +1416,13 @@ withEachNg1Version(() => {
|
||||
const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module));
|
||||
const ng1Module = angular.module_('ng1', []);
|
||||
|
||||
const ng1 = () => { return {template() { return ''; }}; };
|
||||
const ng1 = () => {
|
||||
return {
|
||||
template() {
|
||||
return '';
|
||||
}
|
||||
};
|
||||
};
|
||||
ng1Module.directive('ng1', ng1);
|
||||
|
||||
@Component({selector: 'ng2', template: '<ng1></ng1>'})
|
||||
@ -1398,7 +1449,9 @@ withEachNg1Version(() => {
|
||||
const ng1Module = angular.module_('ng1', []);
|
||||
ng1Module.run(($templateCache: any) => $templateCache.put('url.html', 'WORKS'));
|
||||
|
||||
const ng1 = () => { return {templateUrl: 'url.html'}; };
|
||||
const ng1 = () => {
|
||||
return {templateUrl: 'url.html'};
|
||||
};
|
||||
ng1Module.directive('ng1', ng1);
|
||||
|
||||
@Component({selector: 'ng2', template: '<ng1></ng1>'})
|
||||
@ -1431,13 +1484,20 @@ withEachNg1Version(() => {
|
||||
'{{ctl.scope}}; {{ctl.isClass}}; {{ctl.hasElement}}; {{ctl.isPublished()}}',
|
||||
controllerAs: 'ctl',
|
||||
controller: class {
|
||||
scope: any; hasElement: string; $element: any; isClass: any;
|
||||
scope: any;
|
||||
hasElement: string;
|
||||
$element: any;
|
||||
isClass: any;
|
||||
constructor($scope: any, $element: any) {
|
||||
this.verifyIAmAClass();
|
||||
this.scope = $scope.$parent.$parent == $scope.$root ? 'scope' : 'wrong-scope';
|
||||
this.hasElement = $element[0].nodeName;
|
||||
this.$element = $element;
|
||||
} verifyIAmAClass() { this.isClass = 'isClass'; } isPublished() {
|
||||
}
|
||||
verifyIAmAClass() {
|
||||
this.isClass = 'isClass';
|
||||
}
|
||||
isPublished() {
|
||||
return this.$element.controller('ng1') == this ? 'published' : 'not-published';
|
||||
}
|
||||
}
|
||||
@ -1543,7 +1603,9 @@ withEachNg1Version(() => {
|
||||
template: '{{ctl.status}}',
|
||||
require: 'ng1',
|
||||
controllerAs: 'ctrl',
|
||||
controller: class {status = 'WORKS';},
|
||||
controller: class {
|
||||
status = 'WORKS';
|
||||
},
|
||||
link: function(scope: any, element: any, attrs: any, linkController: any) {
|
||||
expect(scope.$root).toEqual($rootScope);
|
||||
expect(element[0].nodeName).toEqual('NG1');
|
||||
@ -1577,7 +1639,13 @@ withEachNg1Version(() => {
|
||||
const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module));
|
||||
const ng1Module = angular.module_('ng1', []);
|
||||
|
||||
const parent = () => { return {controller: class {parent = 'PARENT';}}; };
|
||||
const parent = () => {
|
||||
return {
|
||||
controller: class {
|
||||
parent = 'PARENT';
|
||||
}
|
||||
};
|
||||
};
|
||||
const ng1 = () => {
|
||||
return {
|
||||
scope: {title: '@'},
|
||||
@ -1585,7 +1653,9 @@ withEachNg1Version(() => {
|
||||
template: '{{parent.parent}}:{{ng1.status}}',
|
||||
require: ['ng1', '^parent', '?^^notFound'],
|
||||
controllerAs: 'ctrl',
|
||||
controller: class {status = 'WORKS';},
|
||||
controller: class {
|
||||
status = 'WORKS';
|
||||
},
|
||||
link: function(scope: any, element: any, attrs: any, linkControllers: any) {
|
||||
expect(linkControllers[0].status).toEqual('WORKS');
|
||||
expect(linkControllers[1].parent).toEqual('PARENT');
|
||||
@ -1633,16 +1703,21 @@ withEachNg1Version(() => {
|
||||
scope: {},
|
||||
bindToController: true,
|
||||
controllerAs: '$ctrl',
|
||||
controller: class {$onInit() { $onInitSpyA(); }}
|
||||
controller: class {
|
||||
$onInit() {
|
||||
$onInitSpyA();
|
||||
}
|
||||
}
|
||||
}))
|
||||
.directive('ng1B', () => ({
|
||||
template: '',
|
||||
scope: {},
|
||||
bindToController: false,
|
||||
controllerAs: '$ctrl',
|
||||
controller: function(this: any) {
|
||||
this.$onInit = $onInitSpyB;
|
||||
}
|
||||
}))
|
||||
.directive(
|
||||
'ng1B', () => ({
|
||||
template: '',
|
||||
scope: {},
|
||||
bindToController: false,
|
||||
controllerAs: '$ctrl',
|
||||
controller: function(this: any) { this.$onInit = $onInitSpyB; }
|
||||
}))
|
||||
.directive('ng2', adapter.downgradeNg2Component(Ng2Component));
|
||||
|
||||
@NgModule({
|
||||
@ -1718,7 +1793,9 @@ withEachNg1Version(() => {
|
||||
|
||||
@Component({selector: 'ng2', template: '<ng1-a></ng1-a> | <ng1-b></ng1-b>'})
|
||||
class Ng2Component {
|
||||
constructor(cd: ChangeDetectorRef) { changeDetector = cd; }
|
||||
constructor(cd: ChangeDetectorRef) {
|
||||
changeDetector = cd;
|
||||
}
|
||||
}
|
||||
|
||||
angular.module_('ng1', [])
|
||||
@ -1727,16 +1804,21 @@ withEachNg1Version(() => {
|
||||
scope: {},
|
||||
bindToController: true,
|
||||
controllerAs: '$ctrl',
|
||||
controller: class {$doCheck() { $doCheckSpyA(); }}
|
||||
controller: class {
|
||||
$doCheck() {
|
||||
$doCheckSpyA();
|
||||
}
|
||||
}
|
||||
}))
|
||||
.directive('ng1B', () => ({
|
||||
template: '',
|
||||
scope: {},
|
||||
bindToController: false,
|
||||
controllerAs: '$ctrl',
|
||||
controller: function(this: any) {
|
||||
this.$doCheck = $doCheckSpyB;
|
||||
}
|
||||
}))
|
||||
.directive(
|
||||
'ng1B', () => ({
|
||||
template: '',
|
||||
scope: {},
|
||||
bindToController: false,
|
||||
controllerAs: '$ctrl',
|
||||
controller: function(this: any) { this.$doCheck = $doCheckSpyB; }
|
||||
}))
|
||||
.directive('ng2', adapter.downgradeNg2Component(Ng2Component));
|
||||
|
||||
@NgModule({
|
||||
@ -1773,7 +1855,9 @@ withEachNg1Version(() => {
|
||||
|
||||
@Component({selector: 'ng2', template: '<ng1-a></ng1-a> | <ng1-b></ng1-b>'})
|
||||
class Ng2Component {
|
||||
constructor(cd: ChangeDetectorRef) { changeDetector = cd; }
|
||||
constructor(cd: ChangeDetectorRef) {
|
||||
changeDetector = cd;
|
||||
}
|
||||
}
|
||||
|
||||
angular.module_('ng1', [])
|
||||
@ -1835,16 +1919,21 @@ withEachNg1Version(() => {
|
||||
scope: {},
|
||||
bindToController: true,
|
||||
controllerAs: '$ctrl',
|
||||
controller: class {$postLink() { $postLinkSpyA(); }}
|
||||
controller: class {
|
||||
$postLink() {
|
||||
$postLinkSpyA();
|
||||
}
|
||||
}
|
||||
}))
|
||||
.directive('ng1B', () => ({
|
||||
template: '',
|
||||
scope: {},
|
||||
bindToController: false,
|
||||
controllerAs: '$ctrl',
|
||||
controller: function(this: any) {
|
||||
this.$postLink = $postLinkSpyB;
|
||||
}
|
||||
}))
|
||||
.directive(
|
||||
'ng1B', () => ({
|
||||
template: '',
|
||||
scope: {},
|
||||
bindToController: false,
|
||||
controllerAs: '$ctrl',
|
||||
controller: function(this: any) { this.$postLink = $postLinkSpyB; }
|
||||
}))
|
||||
.directive('ng2', adapter.downgradeNg2Component(Ng2Component));
|
||||
|
||||
@NgModule({
|
||||
@ -1924,7 +2013,9 @@ withEachNg1Version(() => {
|
||||
template: '<ng1-a [valA]="val"></ng1-a> | <ng1-b [valB]="val"></ng1-b>'
|
||||
})
|
||||
class Ng2Component {
|
||||
constructor() { ng2Instance = this; }
|
||||
constructor() {
|
||||
ng2Instance = this;
|
||||
}
|
||||
}
|
||||
|
||||
angular.module_('ng1', [])
|
||||
@ -1937,17 +2028,17 @@ withEachNg1Version(() => {
|
||||
this.$onChanges = $onChangesControllerSpyA;
|
||||
}
|
||||
}))
|
||||
.directive(
|
||||
'ng1B',
|
||||
() => ({
|
||||
template: '',
|
||||
scope: {valB: '<'},
|
||||
bindToController: false,
|
||||
controllerAs: '$ctrl',
|
||||
controller: class {
|
||||
$onChanges(changes: SimpleChanges) { $onChangesControllerSpyB(changes); }
|
||||
}
|
||||
}))
|
||||
.directive('ng1B', () => ({
|
||||
template: '',
|
||||
scope: {valB: '<'},
|
||||
bindToController: false,
|
||||
controllerAs: '$ctrl',
|
||||
controller: class {
|
||||
$onChanges(changes: SimpleChanges) {
|
||||
$onChangesControllerSpyB(changes);
|
||||
}
|
||||
}
|
||||
}))
|
||||
.directive('ng2', adapter.downgradeNg2Component(Ng2Component))
|
||||
.run(($rootScope: angular.IRootScopeService) => {
|
||||
Object.getPrototypeOf($rootScope).$onChanges = $onChangesScopeSpy;
|
||||
@ -2022,7 +2113,9 @@ withEachNg1Version(() => {
|
||||
})
|
||||
class Ng2Component {
|
||||
ng2Destroy: boolean = false;
|
||||
constructor() { ng2ComponentInstance = this; }
|
||||
constructor() {
|
||||
ng2ComponentInstance = this;
|
||||
}
|
||||
}
|
||||
|
||||
// On browsers that don't support `requestAnimationFrame` (IE 9, Android <= 4.3),
|
||||
@ -2036,16 +2129,21 @@ withEachNg1Version(() => {
|
||||
scope: {},
|
||||
bindToController: true,
|
||||
controllerAs: '$ctrl',
|
||||
controller: class {$onDestroy() { $onDestroySpyA(); }}
|
||||
controller: class {
|
||||
$onDestroy() {
|
||||
$onDestroySpyA();
|
||||
}
|
||||
}
|
||||
}))
|
||||
.directive('ng1B', () => ({
|
||||
template: '',
|
||||
scope: {},
|
||||
bindToController: false,
|
||||
controllerAs: '$ctrl',
|
||||
controller: function(this: any) {
|
||||
this.$onDestroy = $onDestroySpyB;
|
||||
}
|
||||
}))
|
||||
.directive(
|
||||
'ng1B', () => ({
|
||||
template: '',
|
||||
scope: {},
|
||||
bindToController: false,
|
||||
controllerAs: '$ctrl',
|
||||
controller: function(this: any) { this.$onDestroy = $onDestroySpyB; }
|
||||
}))
|
||||
.directive('ng2', adapter.downgradeNg2Component(Ng2Component));
|
||||
|
||||
@NgModule({
|
||||
@ -2112,7 +2210,9 @@ withEachNg1Version(() => {
|
||||
})
|
||||
class Ng2Component {
|
||||
ng2Destroy: boolean = false;
|
||||
constructor() { ng2ComponentInstance = this; }
|
||||
constructor() {
|
||||
ng2ComponentInstance = this;
|
||||
}
|
||||
}
|
||||
|
||||
// On browsers that don't support `requestAnimationFrame` (IE 9, Android <= 4.3),
|
||||
@ -2187,7 +2287,9 @@ withEachNg1Version(() => {
|
||||
@Component({selector: 'ng2', template: '<div *ngIf="!ng2Destroy"><ng1></ng1></div>'})
|
||||
class Ng2Component {
|
||||
ng2Destroy: boolean = false;
|
||||
constructor() { ng2ComponentInstance = this; }
|
||||
constructor() {
|
||||
ng2ComponentInstance = this;
|
||||
}
|
||||
}
|
||||
|
||||
// On browsers that don't support `requestAnimationFrame` (IE 9, Android <= 4.3),
|
||||
@ -2233,7 +2335,9 @@ withEachNg1Version(() => {
|
||||
@Component({selector: 'ng2', template: '<div *ngIf="!ng2Destroy"><ng1></ng1></div>'})
|
||||
class Ng2Component {
|
||||
ng2Destroy: boolean = false;
|
||||
constructor() { ng2ComponentInstance = this; }
|
||||
constructor() {
|
||||
ng2ComponentInstance = this;
|
||||
}
|
||||
}
|
||||
|
||||
// On browsers that don't support `requestAnimationFrame` (IE 9, Android <= 4.3),
|
||||
@ -2245,8 +2349,8 @@ withEachNg1Version(() => {
|
||||
.component('ng1', {
|
||||
controller: class {
|
||||
constructor(private $element: angular.IAugmentedJQuery) {} $onInit() {
|
||||
this.$element.on !('$destroy', elementDestroyListener);
|
||||
this.$element.contents !().on !('$destroy', descendantDestroyListener);
|
||||
this.$element.on!('$destroy', elementDestroyListener);
|
||||
this.$element.contents!().on!('$destroy', descendantDestroyListener);
|
||||
}
|
||||
},
|
||||
template: '<div></div>'
|
||||
@ -2287,8 +2391,8 @@ withEachNg1Version(() => {
|
||||
const ng1Component: angular.IComponent = {
|
||||
controller: class {
|
||||
constructor(private $element: angular.IAugmentedJQuery) {} $onInit() {
|
||||
this.$element.data !('test', 1);
|
||||
this.$element.contents !().data !('test', 2);
|
||||
this.$element.data!('test', 1);
|
||||
this.$element.contents!().data!('test', 2);
|
||||
|
||||
ng1ComponentElement = this.$element;
|
||||
}
|
||||
@ -2301,7 +2405,9 @@ withEachNg1Version(() => {
|
||||
class Ng2ComponentA {
|
||||
destroyIt = false;
|
||||
|
||||
constructor() { ng2ComponentAInstance = this; }
|
||||
constructor() {
|
||||
ng2ComponentAInstance = this;
|
||||
}
|
||||
}
|
||||
|
||||
@Component({selector: 'ng2B', template: '<ng1></ng1>'})
|
||||
@ -2330,15 +2436,15 @@ withEachNg1Version(() => {
|
||||
const $rootScope = ref.ng1RootScope as any;
|
||||
tick();
|
||||
$rootScope.$digest();
|
||||
expect(ng1ComponentElement.data !('test')).toBe(1);
|
||||
expect(ng1ComponentElement.contents !().data !('test')).toBe(2);
|
||||
expect(ng1ComponentElement.data!('test')).toBe(1);
|
||||
expect(ng1ComponentElement.contents!().data!('test')).toBe(2);
|
||||
|
||||
ng2ComponentAInstance.destroyIt = true;
|
||||
tick();
|
||||
$rootScope.$digest();
|
||||
|
||||
expect(ng1ComponentElement.data !('test')).toBeUndefined();
|
||||
expect(ng1ComponentElement.contents !().data !('test')).toBeUndefined();
|
||||
expect(ng1ComponentElement.data!('test')).toBeUndefined();
|
||||
expect(ng1ComponentElement.contents!().data!('test')).toBeUndefined();
|
||||
});
|
||||
}));
|
||||
|
||||
@ -2353,10 +2459,10 @@ withEachNg1Version(() => {
|
||||
const ng1Component: angular.IComponent = {
|
||||
controller: class {
|
||||
constructor(private $element: angular.IAugmentedJQuery) {} $onInit() {
|
||||
ng1DescendantElement = this.$element.contents !();
|
||||
ng1DescendantElement = this.$element.contents!();
|
||||
|
||||
this.$element.on !('click', elementClickListener);
|
||||
ng1DescendantElement.on !('click', descendantClickListener);
|
||||
this.$element.on!('click', elementClickListener);
|
||||
ng1DescendantElement.on!('click', descendantClickListener);
|
||||
}
|
||||
},
|
||||
template: '<div></div>'
|
||||
@ -2367,7 +2473,9 @@ withEachNg1Version(() => {
|
||||
class Ng2ComponentA {
|
||||
destroyIt = false;
|
||||
|
||||
constructor() { ng2ComponentAInstance = this; }
|
||||
constructor() {
|
||||
ng2ComponentAInstance = this;
|
||||
}
|
||||
}
|
||||
|
||||
@Component({selector: 'ng2B', template: '<ng1></ng1>'})
|
||||
@ -2420,7 +2528,11 @@ withEachNg1Version(() => {
|
||||
const ng1Directive: angular.IDirective = {
|
||||
template: '',
|
||||
link: {pre: () => log.push('ng1-pre')},
|
||||
controller: class {constructor() { log.push('ng1-ctrl'); }}
|
||||
controller: class {
|
||||
constructor() {
|
||||
log.push('ng1-ctrl');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Define `Ng2Component`
|
||||
@ -2577,7 +2689,11 @@ withEachNg1Version(() => {
|
||||
const ng1Directive: angular.IDirective = {
|
||||
template: '',
|
||||
link: () => log.push('ng1-post'),
|
||||
controller: class {$postLink() { log.push('ng1-$post'); }}
|
||||
controller: class {
|
||||
$postLink() {
|
||||
log.push('ng1-$post');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Define `Ng2Component`
|
||||
@ -2627,13 +2743,17 @@ withEachNg1Version(() => {
|
||||
class Ng2ComponentA {
|
||||
value = 'foo';
|
||||
showB = false;
|
||||
constructor() { ng2ComponentAInstance = this; }
|
||||
constructor() {
|
||||
ng2ComponentAInstance = this;
|
||||
}
|
||||
}
|
||||
|
||||
@Component({selector: 'ng2B', template: 'ng2B({{ value }})'})
|
||||
class Ng2ComponentB {
|
||||
value = 'bar';
|
||||
constructor() { ng2ComponentBInstance = this; }
|
||||
constructor() {
|
||||
ng2ComponentBInstance = this;
|
||||
}
|
||||
}
|
||||
|
||||
// Define `ng1Module`
|
||||
@ -2678,7 +2798,10 @@ withEachNg1Version(() => {
|
||||
template: 'ng1(<div ng-transclude>{{ $ctrl.value }}</div>)',
|
||||
transclude: true,
|
||||
controller: class {
|
||||
value = 'from-ng1'; constructor() { ng1ControllerInstances.push(this); }
|
||||
value = 'from-ng1';
|
||||
constructor() {
|
||||
ng1ControllerInstances.push(this);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@ -2697,7 +2820,9 @@ withEachNg1Version(() => {
|
||||
})
|
||||
class Ng2Component {
|
||||
value = 'from-ng2';
|
||||
constructor() { ng2ComponentInstance = this; }
|
||||
constructor() {
|
||||
ng2ComponentInstance = this;
|
||||
}
|
||||
}
|
||||
|
||||
// Define `ng1Module`
|
||||
@ -2756,7 +2881,9 @@ withEachNg1Version(() => {
|
||||
class Ng2Component {
|
||||
x = 'foo';
|
||||
y = 'bar';
|
||||
constructor() { ng2ComponentInstance = this; }
|
||||
constructor() {
|
||||
ng2ComponentInstance = this;
|
||||
}
|
||||
}
|
||||
|
||||
// Define `ng1Module`
|
||||
@ -2798,8 +2925,12 @@ withEachNg1Version(() => {
|
||||
const ng1Component: angular.IComponent = {
|
||||
template: 'ng1(default(<div ng-transclude="">fallback-{{ $ctrl.value }}</div>))',
|
||||
transclude: {slotX: 'contentX', slotY: 'contentY'},
|
||||
controller:
|
||||
class {value = 'ng1'; constructor() { ng1ControllerInstances.push(this); }}
|
||||
controller: class {
|
||||
value = 'ng1';
|
||||
constructor() {
|
||||
ng1ControllerInstances.push(this);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Define `Ng2Component`
|
||||
@ -2830,7 +2961,9 @@ withEachNg1Version(() => {
|
||||
class Ng2Component {
|
||||
x = 'foo';
|
||||
y = 'bar';
|
||||
constructor() { ng2ComponentInstance = this; }
|
||||
constructor() {
|
||||
ng2ComponentInstance = this;
|
||||
}
|
||||
}
|
||||
|
||||
// Define `ng1Module`
|
||||
@ -2880,7 +3013,11 @@ withEachNg1Version(() => {
|
||||
)`,
|
||||
transclude: {slotX: '?contentX', slotY: '?contentY'},
|
||||
controller: class {
|
||||
x = 'ng1X'; y = 'ng1Y'; constructor() { ng1ControllerInstances.push(this); }
|
||||
x = 'ng1X';
|
||||
y = 'ng1Y';
|
||||
constructor() {
|
||||
ng1ControllerInstances.push(this);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@ -2896,7 +3033,9 @@ withEachNg1Version(() => {
|
||||
class Ng2Component {
|
||||
x = 'ng2X';
|
||||
y = 'ng2Y';
|
||||
constructor() { ng2ComponentInstance = this; }
|
||||
constructor() {
|
||||
ng2ComponentInstance = this;
|
||||
}
|
||||
}
|
||||
|
||||
// Define `ng1Module`
|
||||
@ -3000,7 +3139,9 @@ withEachNg1Version(() => {
|
||||
x = 'foo';
|
||||
y = 'bar';
|
||||
show = true;
|
||||
constructor() { ng2ComponentInstance = this; }
|
||||
constructor() {
|
||||
ng2ComponentInstance = this;
|
||||
}
|
||||
}
|
||||
|
||||
// Define `ng1Module`
|
||||
@ -3202,13 +3343,18 @@ withEachNg1Version(() => {
|
||||
const ng1Module = angular.module_('ng1', []);
|
||||
let a1Injector: angular.IInjectorService|undefined;
|
||||
ng1Module.run([
|
||||
'$injector', function($injector: angular.IInjectorService) { a1Injector = $injector; }
|
||||
'$injector',
|
||||
function($injector: angular.IInjectorService) {
|
||||
a1Injector = $injector;
|
||||
}
|
||||
]);
|
||||
|
||||
const element = html('<div></div>');
|
||||
window.name = 'NG_DEFER_BOOTSTRAP!' + window.name;
|
||||
|
||||
adapter.bootstrap(element, [ng1Module.name]).ready((ref) => { ref.dispose(); });
|
||||
adapter.bootstrap(element, [ng1Module.name]).ready((ref) => {
|
||||
ref.dispose();
|
||||
});
|
||||
|
||||
tick(100);
|
||||
|
||||
@ -3275,7 +3421,7 @@ withEachNg1Version(() => {
|
||||
|
||||
document.body.innerHTML = '<ng2 name="World">project</ng2>';
|
||||
|
||||
adapter.bootstrap(document.body.firstElementChild !, ['myExample']).ready((ref) => {
|
||||
adapter.bootstrap(document.body.firstElementChild!, ['myExample']).ready((ref) => {
|
||||
expect(multiTrim(document.body.textContent))
|
||||
.toEqual('ng2[ng1[Hello World!](transclude)](project)');
|
||||
ref.dispose();
|
||||
|
@ -43,10 +43,9 @@ Zone.__load_patch('jest', (context: any, Zone: ZoneType) => {
|
||||
|
||||
function wrapTestFactoryInZone(originalJestFn: Function) {
|
||||
return function(this: unknown, ...tableArgs: any[]) {
|
||||
const testFn = originalJestFn.apply(this, tableArgs);
|
||||
return function(this: unknown, ...args: any[]) {
|
||||
args[1] = wrapTestInZone(args[1]);
|
||||
return testFn.apply(this, args);
|
||||
return originalJestFn.apply(this, tableArgs).apply(this, args);
|
||||
};
|
||||
};
|
||||
}
|
||||
@ -64,16 +63,21 @@ Zone.__load_patch('jest', (context: any, Zone: ZoneType) => {
|
||||
/**
|
||||
* Gets a function wrapping the body of a jest `it/beforeEach/afterEach` block to
|
||||
* execute in a ProxyZone zone.
|
||||
* This will run in the `testProxyZone`.
|
||||
* This will run in the `proxyZone`.
|
||||
*/
|
||||
function wrapTestInZone(testBody: Function): Function {
|
||||
if (typeof testBody !== 'function') {
|
||||
return testBody;
|
||||
}
|
||||
// The `done` callback is only passed through if the function expects at least one argument.
|
||||
// Note we have to make a function with correct number of arguments, otherwise jest will
|
||||
// think that all functions are sync or async.
|
||||
return function(this: unknown, ...args: any[]) { return proxyZone.run(testBody, this, args); };
|
||||
const wrappedFunc = function() {
|
||||
return proxyZone.run(testBody, null, arguments as any);
|
||||
};
|
||||
// Update the length of wrappedFunc to be the same as the length of the testBody
|
||||
// So jest core can handle whether the test function has `done()` or not correctly
|
||||
Object.defineProperty(
|
||||
wrappedFunc, 'length', {configurable: true, writable: true, enumerable: false});
|
||||
wrappedFunc.length = testBody.length;
|
||||
return wrappedFunc;
|
||||
}
|
||||
|
||||
['describe', 'xdescribe', 'fdescribe'].forEach(methodName => {
|
||||
|
@ -512,7 +512,7 @@ interface ZoneGlobalConfigurations {
|
||||
* Users can achieve this goal by defining `__zone_symbol__UNPATCHED_EVENTS = ['scroll',
|
||||
* 'mousemove'];` before importing `zone.js`.
|
||||
*/
|
||||
__zone_symbol__UNPATCHED_EVENTS?: boolean;
|
||||
__zone_symbol__UNPATCHED_EVENTS?: string[];
|
||||
|
||||
/**
|
||||
* Define the event names of the passive listeners.
|
||||
@ -528,7 +528,7 @@ interface ZoneGlobalConfigurations {
|
||||
*
|
||||
* The preceding code makes all scroll event listeners passive.
|
||||
*/
|
||||
__zone_symbol__PASSIVE_EVENTS?: boolean;
|
||||
__zone_symbol__PASSIVE_EVENTS?: string[];
|
||||
|
||||
/**
|
||||
* Disable wrapping uncaught promise rejection.
|
||||
|
@ -6,10 +6,18 @@ function assertInsideSyncDescribeZone() {
|
||||
}
|
||||
describe('describe', () => {
|
||||
assertInsideSyncDescribeZone();
|
||||
beforeEach(() => { assertInsideProxyZone(); });
|
||||
beforeAll(() => { assertInsideProxyZone(); });
|
||||
afterEach(() => { assertInsideProxyZone(); });
|
||||
afterAll(() => { assertInsideProxyZone(); });
|
||||
beforeEach(() => {
|
||||
assertInsideProxyZone();
|
||||
});
|
||||
beforeAll(() => {
|
||||
assertInsideProxyZone();
|
||||
});
|
||||
afterEach(() => {
|
||||
assertInsideProxyZone();
|
||||
});
|
||||
afterAll(() => {
|
||||
assertInsideProxyZone();
|
||||
});
|
||||
});
|
||||
describe.each([[1, 2]])('describe.each', (arg1, arg2) => {
|
||||
assertInsideSyncDescribeZone();
|
||||
@ -17,23 +25,78 @@ describe.each([[1, 2]])('describe.each', (arg1, arg2) => {
|
||||
expect(arg2).toBe(2);
|
||||
});
|
||||
describe('test', () => {
|
||||
it('it', () => { assertInsideProxyZone(); });
|
||||
it('it', () => {
|
||||
assertInsideProxyZone();
|
||||
});
|
||||
it.each([[1, 2]])('it.each', (arg1, arg2) => {
|
||||
assertInsideProxyZone();
|
||||
expect(arg1).toBe(1);
|
||||
expect(arg2).toBe(2);
|
||||
});
|
||||
test('test', () => { assertInsideProxyZone(); });
|
||||
test.each([[]])('test.each', () => { assertInsideProxyZone(); });
|
||||
test('test', () => {
|
||||
assertInsideProxyZone();
|
||||
});
|
||||
test.each([[]])('test.each', () => {
|
||||
assertInsideProxyZone();
|
||||
});
|
||||
});
|
||||
|
||||
it('it', () => { assertInsideProxyZone(); });
|
||||
it.each([[1, 2]])('it.each', (arg1, arg2) => {
|
||||
it('it', () => {
|
||||
assertInsideProxyZone();
|
||||
});
|
||||
it('it with done', done => {
|
||||
assertInsideProxyZone();
|
||||
done();
|
||||
});
|
||||
|
||||
it.each([[1, 2]])('it.each', (arg1, arg2, done) => {
|
||||
assertInsideProxyZone();
|
||||
expect(arg1).toBe(1);
|
||||
expect(arg2).toBe(2);
|
||||
done();
|
||||
});
|
||||
|
||||
it.each([2])('it.each with 1D array', arg1 => {
|
||||
assertInsideProxyZone();
|
||||
expect(arg1).toBe(2);
|
||||
});
|
||||
|
||||
it.each([2])('it.each with 1D array and done', (arg1, done) => {
|
||||
assertInsideProxyZone();
|
||||
expect(arg1).toBe(2);
|
||||
done();
|
||||
});
|
||||
|
||||
it.each`
|
||||
foo | bar
|
||||
${1} | ${2}
|
||||
`('it.each should work with table as a tagged template literal', ({foo, bar}) => {
|
||||
expect(foo).toBe(1);
|
||||
expect(bar).toBe(2);
|
||||
});
|
||||
|
||||
it.each`
|
||||
foo | bar
|
||||
${1} | ${2}
|
||||
`('it.each should work with table as a tagged template literal with done', ({foo, bar}, done) => {
|
||||
expect(foo).toBe(1);
|
||||
expect(bar).toBe(2);
|
||||
done();
|
||||
});
|
||||
|
||||
it.each`
|
||||
foo | bar
|
||||
${1} | ${2}
|
||||
`('(async) it.each should work with table as a tagged template literal', async ({foo, bar}) => {
|
||||
expect(foo).toBe(1);
|
||||
expect(bar).toBe(2);
|
||||
});
|
||||
|
||||
test('test', () => {
|
||||
assertInsideProxyZone();
|
||||
});
|
||||
test.each([[]])('test.each', () => {
|
||||
assertInsideProxyZone();
|
||||
});
|
||||
test('test', () => { assertInsideProxyZone(); });
|
||||
test.each([[]])('test.each', () => { assertInsideProxyZone(); });
|
||||
|
||||
test.todo('todo');
|
||||
|
@ -21,7 +21,9 @@ describe('crypto test', () => {
|
||||
const zoneASpec = {
|
||||
name: 'A',
|
||||
onScheduleTask: (delegate: ZoneDelegate, currentZone: Zone, targetZone: Zone, task: Task):
|
||||
Task => { return delegate.scheduleTask(targetZone, task); }
|
||||
Task => {
|
||||
return delegate.scheduleTask(targetZone, task);
|
||||
}
|
||||
};
|
||||
const zoneA = Zone.current.fork(zoneASpec);
|
||||
spyOn(zoneASpec, 'onScheduleTask').and.callThrough();
|
||||
@ -41,10 +43,12 @@ describe('crypto test', () => {
|
||||
done();
|
||||
return;
|
||||
}
|
||||
const zoneASpec: ZoneSpec = {
|
||||
const zoneASpec = {
|
||||
name: 'A',
|
||||
onScheduleTask: (delegate: ZoneDelegate, currentZone: Zone, targetZone: Zone, task: Task):
|
||||
Task => { return delegate.scheduleTask(targetZone, task); }
|
||||
Task => {
|
||||
return delegate.scheduleTask(targetZone, task);
|
||||
}
|
||||
};
|
||||
const zoneA = Zone.current.fork(zoneASpec);
|
||||
spyOn(zoneASpec, 'onScheduleTask').and.callThrough();
|
||||
|
@ -43,7 +43,7 @@ console.log(`BUILD_SCM_LOCAL_CHANGES ${LOCAL_CHANGES}`);
|
||||
// This will ignore non-version tags which would break unit tests expecting a valid version
|
||||
// number in the package headers
|
||||
const BUILD_SCM_VERSION_RAW =
|
||||
_exec(`git describe --match [0-9].[0-9].[0-9]* --abbrev=7 --tags HEAD`);
|
||||
_exec(`git describe --match [0-9]*.[0-9]*.[0-9]* --abbrev=7 --tags HEAD`);
|
||||
|
||||
// Reformat `git describe` version string into a more semver-ish string
|
||||
// From: 5.2.0-rc.0-57-g757f886
|
||||
|
@ -10,7 +10,7 @@
|
||||
const shell = require('shelljs');
|
||||
const karmaBin = require.resolve('karma/bin/karma');
|
||||
const runfiles = require(process.env['BAZEL_NODE_RUNFILES_HELPER']);
|
||||
const sauceService = runfiles.resolve(process.argv[2]);
|
||||
const sauceService = runfiles.resolveWorkspaceRelative(process.argv[2]);
|
||||
process.argv = [
|
||||
process.argv[0],
|
||||
karmaBin,
|
||||
@ -61,7 +61,7 @@ try {
|
||||
}
|
||||
|
||||
console.error(`Launching karma ${karmaBin}...`);
|
||||
module.constructor._load(karmaBin, this, /*isMain=*/true);
|
||||
module.constructor._load(karmaBin, this, /*isMain=*/ true);
|
||||
} catch (e) {
|
||||
console.error(e.stack || e);
|
||||
process.exit(1);
|
||||
|
13
yarn.lock
13
yarn.lock
@ -1300,17 +1300,12 @@
|
||||
"@types/rx" "*"
|
||||
"@types/through" "*"
|
||||
|
||||
"@types/jasmine@*":
|
||||
"@types/jasmine@*", "@types/jasmine@3.5.10":
|
||||
version "3.5.10"
|
||||
resolved "https://registry.yarnpkg.com/@types/jasmine/-/jasmine-3.5.10.tgz#a1a41012012b5da9d4b205ba9eba58f6cce2ab7b"
|
||||
integrity sha512-3F8qpwBAiVc5+HPJeXJpbrl+XjawGmciN5LgiO7Gv1pl1RHtjoMNqZpqEksaPJW05ViKe8snYInRs6xB25Xdew==
|
||||
|
||||
"@types/jasmine@^2.8.8":
|
||||
version "2.8.16"
|
||||
resolved "https://registry.yarnpkg.com/@types/jasmine/-/jasmine-2.8.16.tgz#a6cb24b1149d65293bd616923500014838e14e7d"
|
||||
integrity sha512-056oRlBBp7MDzr+HoU5su099s/s7wjZ3KcHxLfv+Byqb9MwdLUvsfLgw1VS97hsh3ddxSPyQu+olHMnoVTUY6g==
|
||||
|
||||
"@types/jasminewd2@^2.0.6":
|
||||
"@types/jasminewd2@^2.0.8":
|
||||
version "2.0.8"
|
||||
resolved "https://registry.yarnpkg.com/@types/jasminewd2/-/jasminewd2-2.0.8.tgz#67afe5098d5ef2386073a7b7384b69a840dfe93b"
|
||||
integrity sha512-d9p31r7Nxk0ZH0U39PTH0hiDlJ+qNVGjlt1ucOoTUptxb2v+Y5VMnsxfwN+i3hK4yQnqBi3FMmoMFcd1JHDxdg==
|
||||
@ -8002,7 +7997,7 @@ istanbul-reports@^1.3.0:
|
||||
dependencies:
|
||||
handlebars "^4.0.3"
|
||||
|
||||
jasmine-core@^3.1.0, jasmine-core@^3.3, jasmine-core@~3.5.0:
|
||||
jasmine-core@^3.3, jasmine-core@^3.5.0, jasmine-core@~3.5.0:
|
||||
version "3.5.0"
|
||||
resolved "https://registry.yarnpkg.com/jasmine-core/-/jasmine-core-3.5.0.tgz#132c23e645af96d85c8bca13c8758b18429fc1e4"
|
||||
integrity sha512-nCeAiw37MIMA9w9IXso7bRaLl+c/ef3wnxsoSAlYrzS+Ot0zTG6nU8G/cIfGkqpkjX2wNaIW9RFG0TwIFnG6bA==
|
||||
@ -8029,7 +8024,7 @@ jasmine@2.8.0:
|
||||
glob "^7.0.6"
|
||||
jasmine-core "~2.8.0"
|
||||
|
||||
jasmine@^3.1.0, jasmine@~3.5.0:
|
||||
jasmine@^3.5.0, jasmine@~3.5.0:
|
||||
version "3.5.0"
|
||||
resolved "https://registry.yarnpkg.com/jasmine/-/jasmine-3.5.0.tgz#7101eabfd043a1fc82ac24e0ab6ec56081357f9e"
|
||||
integrity sha512-DYypSryORqzsGoMazemIHUfMkXM7I7easFaxAvNM3Mr6Xz3Fy36TupTrAOxZWN8MVKEU5xECv22J4tUQf3uBzQ==
|
||||
|
Reference in New Issue
Block a user